1. 用var操作符聲明的變量與省略var操作符聲明的變量的區(qū)別
在JavaScript中,是通過var關(guān)鍵字來聲明變量的(稱這種聲明方式為正規(guī)聲明),但是,直接給沒有用var 聲明過的標(biāo)識符賦值,也會創(chuàng)建變量(稱這種聲明方式為非正規(guī)聲明),但是喻旷,它們是有區(qū)別的,區(qū)別如下:
- 非正規(guī)變量只有在標(biāo)識符被賦值的時候才會創(chuàng)建牢屋,而正規(guī)聲明的變量且预,在聲明時創(chuàng)建牺陶;
- 正規(guī)聲明的變量在其作用域內(nèi)的任何地方都可用,哪怕是其聲明之前辣之,只不過在其聲明之前掰伸,它的值是undefined,這是因為聲明提前的緣故怀估。而非正規(guī)聲明的變量狮鸭,只能在其創(chuàng)建之后的有效作用域內(nèi)使用;
- 非正規(guī)聲明的變量總是全局變量多搀,即使它在函數(shù)內(nèi)創(chuàng)建也是如此歧蕉;
- 在函數(shù)內(nèi),全局變量中有同名的變量康铭,正規(guī)聲明的標(biāo)識符會新創(chuàng)建變量并且使用優(yōu)先級高于全局變量惯退,而對于非正規(guī)聲明的變量不會重新創(chuàng)建變量,只是使用同名的全局變量从藤,即并非正規(guī)聲明無效催跪;
- 正規(guī)變量無法通過delete刪除,而非正規(guī)變量可以夷野;
2. 省略var操作符聲明的變量是全局變量的原因
我認為ECMAScript這樣規(guī)定的原因是:
假設(shè)某個地方對全局變量variable進行賦值懊蒸;
當(dāng)因為某種失誤variable沒有被聲明或創(chuàng)建時,解析器有兩種方案:
- 作為錯誤處理悯搔;
- 創(chuàng)建variable變量骑丸;
由于JavaScritpt是作為瀏覽器的腳本語言,且瀏覽器的JavaScritpt代碼是可以分散的妒貌,且瀏覽器并不能保證所有的JavaScritpt代碼全部執(zhí)行通危,也不能保證所有JavaScript代碼按順序執(zhí)行,所以經(jīng)常存在某個聲明變量的語句未在合適的時機執(zhí)行的情況灌曙,所以把這種示聲明的變量的引用當(dāng)作錯誤處理是不合適的菊碟,所以第一種方案不選,只能選擇第2種方案:創(chuàng)建變量平匈;
創(chuàng)建變量也有兩種方案:
- 創(chuàng)建局部變量框沟;
- 創(chuàng)建全局變量藏古;
但考慮以下2個原因增炭,應(yīng)該選擇:創(chuàng)建全局變量;
- 如果創(chuàng)建局部變量拧晕,則當(dāng)其它JavaScritpt代碼片斷執(zhí)行時仍存在上述問題隙姿;但如果創(chuàng)建全局變量,則當(dāng)其它JavaScritpt代碼片斷執(zhí)行時便不存在該變量沒有聲明的情況了厂捞;
- 上述問題發(fā)生在引用其它JavaScript代碼片斷中聲明的全局變量的情況下输玷,所以队丝,如果創(chuàng)建局部變量的話,就違背的了代碼的意圖(把該變量作為全局變量用)欲鹏;
所以机久,綜合以上就有:省略var操作符聲明的變量是全局變量;
3. 作用域鏈的理解
- 可以把作用域鏈理解成是一個棧結(jié)構(gòu)赔嚎;
- 每個作用域都有一個作用域?qū)ο笥糜诒4嬖谠撟饔糜騼?nèi)創(chuàng)建的變量(包括函數(shù))膘盖,其保存的方式是:在作用域內(nèi)創(chuàng)建的變量會成為作用域?qū)ο蟮膶傩裕?/li>
- 作用鏈鏈保存的是各級作用域?qū)ο蟮囊茫渲凶罱淖饔糜虻淖饔糜驅(qū)ο笤谧钋岸擞任螅竭h的作用域的作用域?qū)ο笤娇亢螅?/li>
- 全局作用域的作用域?qū)ο笫侨謱ο蟊旧硐琅希凰裕總€作用域鏈的最后端都是全局對象的引用损晤;
- 在全局作用域內(nèi)創(chuàng)建的變量會成為全局對象的屬性的原因:由于2(在作用域內(nèi)創(chuàng)建的變量會成為作用域?qū)ο蟮膶傩?和4(全局作用域的作用域?qū)ο笫侨謱ο蟊旧?软棺,所以在全局作用域創(chuàng)建的變量會成為全局對象的屬性;
- 函數(shù)的作用域鏈?zhǔn)窃诤瘮?shù)對象被創(chuàng)建時(被定義時)創(chuàng)建的尤勋;
- 每當(dāng)函數(shù)被執(zhí)行時喘落,都會新創(chuàng)建一個函數(shù)的作用域?qū)ο螅言撟饔糜驅(qū)ο笸频阶饔糜蜴湹淖钋岸耍?/li>
- 每當(dāng)函數(shù)執(zhí)行結(jié)束時最冰,都會把函數(shù)的作用域?qū)ο髲脑摵瘮?shù)作用鏈中推出揖盘;
4. 標(biāo)識符查找
- 訪問屬性,查找的是原型鏈锌奴;
- 訪問變量兽狭,查找的是作用域鏈;
作用鏈?zhǔn)且粋€棧鹿蜀,棧里保存的是作用域?qū)ο蠡郏瑮5淖铐敹耸钱?dāng)前作用域?qū)ο螅瑮5淖畹锥耸侨肿饔糜驅(qū)ο螅?br> 作用域?qū)ο笾挥?種:
- 全局作用域?qū)ο螅?/li>
- 函數(shù)作用域?qū)ο螅?/li>
所以當(dāng)通過屬性調(diào)用的方式調(diào)用對象的方法時茴恰,該對象只會成this的值颠焦;不會成為它的方法(即:對象中屬性值是函數(shù)的屬性)的作用域鏈中的作用域?qū)ο螅贿^可以通過with操作符使對象成為它的方法的作用域鏈中的作用域?qū)ο螅?/p>
5. 數(shù)組的本質(zhì)
數(shù)組的本質(zhì)還是對象往枣,數(shù)組是通過對象實現(xiàn)的伐庭,它并沒有為索引加入新的機制,數(shù)組中的索引本質(zhì)是作為數(shù)組對象的屬性存在的分冈,因為打點調(diào)用的屬性不能是數(shù)字圾另,所以數(shù)組的索引不通過打點調(diào)用,只能通過方括號的方式調(diào)用雕沉;
6. 函數(shù)this的取值機制
經(jīng)過測試集乔,總結(jié)得出以下結(jié)論:
結(jié)論1:容易理解的this取值機制
函數(shù)的this的值取決于函數(shù)的調(diào)用方式;
函數(shù)有2種調(diào)用方式坡椒,如下:
- 直接調(diào)用扰路;如:fn()尤溜;
- 屬性調(diào)用;如:obj.fn()汗唱;
- 當(dāng)函數(shù)以直接調(diào)用的方式調(diào)用時宫莱,this的值為全局對象,如:winodw哩罪;
- 當(dāng)函數(shù)以屬性調(diào)用的方式調(diào)用時梢睛,this的值為調(diào)用函數(shù)的對象;
對象屬性調(diào)用有2種方式:
- 打點調(diào)用识椰,如:
obj.attr
绝葡; - 方括號調(diào)用,如:
obj["attr"]
腹鹉;
所有通過這2種屬性調(diào)用方式調(diào)用的函數(shù)的this的值都為調(diào)用函數(shù)的對象藏畅;
又因為數(shù)組中的索引本質(zhì)是作為數(shù)組對象的屬性存在的,所以調(diào)用數(shù)組中的函數(shù)(如:array[index]()
功咒;)時愉阎,被調(diào)用的函數(shù)的this的值為該數(shù)組對象;
結(jié)論2:this取值的本質(zhì)機制
我認為力奋,本質(zhì)上榜旦,函數(shù)的this的值取決于函數(shù)對象的查找方式:
- 如果函數(shù)對象在被調(diào)用時是在作用域鏈上查找到的,則該函數(shù)中this的值為全局對象景殷;
- 如果函數(shù)對象在被調(diào)用時是在原型鏈上查找到的溅呢,則該函數(shù)中this的值為調(diào)用函數(shù)的對象;
因為:
- 當(dāng)函數(shù)以
直接調(diào)用
的方式調(diào)用時猿挚,它是在作用域鏈上被查找到的咐旧,所以該函數(shù)中this的值為全局對象; - 當(dāng)函數(shù)以
屬性調(diào)用
的方式調(diào)用時绩蜻,它是在原型鏈上被查找到的铣墨,所以該函數(shù)中this的值為調(diào)用函數(shù)的對象;
所以:
結(jié)論1是正確的办绝,并且結(jié)論1只是結(jié)論2的一個特例伊约,即:結(jié)論2包含結(jié)論1;
注意
- 計算屬性的get和set函數(shù)中的this也符合上述規(guī)則孕蝉;
- ES6引用了模塊機制盗扇,對模塊中頂層的this做了處理笙蒙,使得在ES6的模塊中百宇,頂層的this的值是undefined洛史,所以上忍,不應(yīng)該在頂層代碼使用this骤肛; 所以纳本,如果在模塊中直接訪問全局對象(如 window 對象)的計算屬性(即:不是通過調(diào)用 window 屬性的方式來訪問,如
window.計算屬性名
腋颠,而是直接訪問繁成,如全局對象的計算屬性名
),則該計算屬性的 set 和 get 函數(shù)的 this 也是 undefined 淑玫;
7. 函數(shù)調(diào)用表達的理解
假設(shè)在全局環(huán)境中有以下代碼:
var inWhere = "The Window";
var aObject = {
inWhere:"The aObject",
getWhere:function(){
return this.inWhere
}
};
則在全局環(huán)境下巾腕,以下三句代碼及其執(zhí)行結(jié)果如下:
代碼1:
aObject.getWhere(); //結(jié)果:"The aObject"
代碼2:
(aObject.getWhere)(); //結(jié)果:"The aObject"
代碼3:
(aObject.getWhere=aObject.getWhere)(); //結(jié)果:"The Window"
我認為造成以上執(zhí)行結(jié)果的原因是:
- 因為對于代碼1和代碼2中的函數(shù)在被調(diào)用時,都是在原型鏈上查找到的絮蒿,所以它們的執(zhí)行結(jié)果均為
The aObject
尊搬; - 因為賦值語句
=
在內(nèi)部本質(zhì)上是作為函數(shù)來實現(xiàn)的,并且這個函數(shù)有個返回值土涝,會返回等號=
右邊表達式的值佛寿,在代碼3中該返回值就是getWhere函數(shù),所以(aObject.getWhere=aObject.getWhere)
結(jié)果是getWhere函數(shù)但壮,并且這個函數(shù)是從等號=
函數(shù)的返回值中取得的冀泻;又因為返回值是在作用域鏈中查找到的,所以在getWhere被執(zhí)行時蜡饵,this的值為全局對象弹渔,所以代碼3執(zhí)行結(jié)果是The Window
;
8. 未定義的變量與未定義的屬性的訪問
假設(shè)以下代碼在全局作用域下執(zhí)行溯祸,且gby和dyx都從未定義過肢专,則執(zhí)行結(jié)果如下:
gby; //結(jié)果:報錯:ReferenceError: Can't find variable: gby;
window.gby; //結(jié)果:undefined
window.dyx; //結(jié)果:undefined
所以:訪問未定義的變量會報錯焦辅,但訪問未定義的屬性不會報錯鸟召,只是結(jié)果為undefined仆抵;
如果gby是全局變量,則gby 和 window.gby 訪問的都是同一個變量,那為什么這兩種訪問方式的結(jié)果不一樣呢?我認為原因如下:
訪問變量時是通過搜索作用域鏈來查找變量的旱物,而訪問屬性是通過搜索原型鏈來查找屬性的宝穗,因為這兩種訪問方式的搜索方式不一樣橱鹏,所以導(dǎo)致了結(jié)果不一樣礁竞;
9. JavaStript的特點
- JavaStript解析器會忽略后面對已存的變量的聲明操作,但會執(zhí)行對已存的變量的賦值操作(包括初始化操作);
- 當(dāng)function關(guān)鍵字用作函數(shù)的聲明時,不能省略函數(shù)名字蔑穴,當(dāng)function關(guān)鍵字用在表達式中創(chuàng)建函數(shù)時,就可以省略函數(shù)名柿顶;可以通過圓括號將“()”將函數(shù)聲明轉(zhuǎn)成函數(shù)表達式;所以直接執(zhí)行代碼:function(){}(); 會報錯肘交,而代碼:(function(){})()复罐;可以正常執(zhí)行咽笼;
- 聲明的全局變量會成為全局對象(window對象)的屬性叛甫,但定義全局變量 和 直接在全局對象上定義屬性 是有差別的,差別如下:
3.1. 定義全局變量生成的屬性的configurable特性配置 false
3.2直接在全局對象上定義屬性的屬性的configurable特性配置 true
所以杨伙,通過定義變量生成的屬性不能用delete操作符刪除限匣,通過直接定義屬性的屬性可能用delete操作符刪除贮庞;
10. new操作符的特性
使用new操作符的格式如下:
new 構(gòu)造函數(shù);
經(jīng)過探究,new操作符有如下特性:
- 每次調(diào)用都會創(chuàng)建一個新的對象较屿;
- 會把構(gòu)造函數(shù)的原型屬性設(shè)置成新創(chuàng)建的對象的原型;
- 會把構(gòu)造函數(shù)的this指向新創(chuàng)建的對象襟企;
- 如果構(gòu)造函數(shù)沒有返回值嘱么,則會默認返回new新創(chuàng)建的對象;
- 如果構(gòu)造函數(shù)有返回值整吆,則會返回構(gòu)造函數(shù)返回的值拱撵;
11. new.target
new.target屬性是一個可以被所有函數(shù)訪問的元屬性
new.target屬性允許你檢測函數(shù)或構(gòu)造方法是否通過是通過new運算符被調(diào)用的。
new.target的具體取值如下:
- 在通過new運算符被初始化的函數(shù)或構(gòu)造方法中表蝙,new.target返回一個指向構(gòu)造方法或函數(shù)的引用拴测;
- 在普通的函數(shù)調(diào)用中,new.target 的值是undefined府蛇。
- 在箭頭函數(shù)中集索,new.target指向外圍函數(shù)的 new.target。
new.target語法由一個關(guān)鍵字"new"汇跨,一個點务荆,和一個屬性名"target"組成。通常標(biāo)識符.
的作用是提供屬性訪問的上下文穷遂,但這里new.
其實不是一個真正的對象函匕。不過在構(gòu)造方法調(diào)用中,new.target指向被new調(diào)用的構(gòu)造函數(shù)蚪黑,所以new.
成為了一個虛擬上下文盅惜。
12. undefined和null
在JavaScript的原始類型中包含了undefined和null中剩;其中undefined是全局對象的一個屬性,null是一個字面量抒寂;
13. 正則表達式
在JavaScript中創(chuàng)建正則表達式對象有兩種方式:
- 正則表達式字面量结啼;
- RegExp構(gòu)造函數(shù),格式如下:new RegExp(模式:string,標(biāo)志:string)屈芜;
通過這2種方式創(chuàng)建的正則表達式是有區(qū)別的郊愧,如下:
對于相同的正則表達式字面量,共享峽一個RegExp類型的實例井佑,而使用構(gòu)造函數(shù)創(chuàng)建的每一個RegExp類型的實例都是一個新實例属铁;
注意:
RegExp構(gòu)造函數(shù)接收2個參數(shù),第1個參數(shù)是要匹配的字符串模式毅糟,第2個參數(shù)是可選的標(biāo)志字符串红选;在字符串格式的模式中澜公,需要對元字符進行雙重轉(zhuǎn)義姆另;
14. 提升
JavaScript在解析JavaScript代碼之前,會先進行一個聲明提升的預(yù)處理坟乾,聲明提升的規(guī)則如下:
- 會把聲明提升到單個<script>標(biāo)簽內(nèi)的每個作用域內(nèi)的最頂端 或者 單個文件內(nèi)的每個作用域內(nèi)的最頂端迹辐;
- 提升后,變量聲明總是放在所有函數(shù)聲明的前面甚侣;
- 對于同一作用域內(nèi)重復(fù)聲明的變量或者函數(shù)明吩,有如下規(guī)則:
3.1 對于變量,解析器會忽略第1次聲明之后的同名變量的聲明殷费;
3.2 對于函數(shù)印荔,后面聲明的函數(shù)會覆蓋前面聲明的同名函數(shù); - 變量聲明的提升只是提升變量名字的聲明详羡,不會提升變量的賦值操作仍律;對于函數(shù)聲明的提升,不僅會提升函數(shù)名字的導(dǎo)明实柠,也會提升函數(shù)的代碼定義水泉;
- FireFox瀏覽器不能對代碼塊內(nèi)的聲明進行提升;
注意:
由于上述的規(guī)則2窒盐,所以在同一聲明提升的范圍內(nèi)草则,如果同一名字即有變量的聲明又有函數(shù)的聲明,則函數(shù)的聲明總是會覆蓋掉變量的聲明蟹漓;
15. 閉包
我對閉包的定義是:
閉包的標(biāo)準(zhǔn)定義:攜帶外部變量的函數(shù)稱為閉包炕横;
我之所以這樣對閉包下定義,是因為這個定義幾乎適用所有語言的閉包葡粒,如:Object-C份殿、Swift姿锭、JavaScript等等;所以我認為這是較標(biāo)準(zhǔn)的定義伯铣;
對于JavaScript中的閉包雖然符合標(biāo)準(zhǔn)定義呻此,但是由于JavaScript語言的一些特性,使得JavaScript中的閉包的實現(xiàn)與其它語言(如:Object-C腔寡、Swift)的實現(xiàn)并不一樣焚鲜;
很多人都認為JavaScript中的閉包只會攜帶它內(nèi)部引用的外部變量,并不會攜帶沒有引用的外部變量放前,其實這是錯誤的忿磅;可以通過下面的代碼證明:
function outFun() {
var outArg1 = "外部參數(shù)1";
var outArg2 = "外部參數(shù)2";
function outArg3() {
console.log("外部參數(shù)3");
}
/*定義閉包
* codeStr:字符串類型的參數(shù),該參數(shù)的值將被當(dāng)作代碼執(zhí)行
* return : 返回將codeStr作為代碼執(zhí)行的結(jié)果凭语;
* */
function closureFun(codeStr) {
console.log("閉包引用的變量的值:",outArg1);
return eval(codeStr); //返回將codeStr作為代碼執(zhí)行的結(jié)果葱她;
}
return closureFun;
}
var getValueOf = outFun(); //獲取閉包
var arg2Value = getValueOf("outArg2"); //嘗試獲取閉包內(nèi)沒有引用的變量outArg2的值;
console.log(arg2Value); //輸出結(jié)果為:外部參數(shù)2
var arg3Value = getValueOf("outArg3"); //嘗試獲取閉包內(nèi)沒有引用的函數(shù)outArg3似扔;
arg3Value(); //輸出結(jié)果為:外部參數(shù)3
從示例代碼中的運行結(jié)果中可以看出吨些,對于閉包引用到的外部變量outArg1 和 閉包沒有引用到的變量outArg2和函數(shù)outArg3,在閉包執(zhí)行時都能被正確地訪問到炒辉,所以閉包會攜帶所有的外部變量(函數(shù)也是變量)豪墅;
其實閉包攜帶外部變量的機制并非閉包的特有機制,它是函數(shù)的作用域鏈的一個效應(yīng)黔寇;在JavaScript中偶器,閉包和普通函數(shù)沒有任何本質(zhì)的區(qū)別,閉包只是函數(shù)在某種使用場景下的一個名字缝裤,就好比兇器只是刀在用于行兇時的名字屏轰;
JavaScript中的閉包能攜帶外部變量的原因是:
JavaScript的函數(shù)在被創(chuàng)建時(被定義時)會生成自己的作用域鏈;該作用域鏈會保存各級作用域?qū)ο蟮囊帽锓桑訨avaScript的函數(shù)能夠訪問其外部的所有變量霎苗;
說見上文的《作用域鏈的理解》
所以,本質(zhì)上搀崭,JavaScript中的閉包攜帶的不是外部變量叨粘,而是外部的作用域?qū)ο螅?/p>
使用閉包的建議:
由于JavaScript中的函數(shù)(包括閉包)會外部的作用域鏈;所以瘤睹,建議:
- 閉包的嵌套不要太深升敲;
閉包嵌套越深,占用的內(nèi)存空間就越大轰传;- 不要使用過多的閉包驴党;因為閉包較占內(nèi)存;
16. 數(shù)組的splice()方法的參數(shù)說明
splice()方法可以接收任意數(shù)量的參數(shù)获茬,參數(shù)的含意如下:
第1個參數(shù):指定操作的起始項的索引港庄;
第2個參數(shù):指定要刪除的項數(shù)倔既;
第3個及以后的參數(shù):指定要插入的項目;
17. 各種添加事件處理程序的特點
事件處理程序的名字以on
開頭鹏氧,緊接個事件的名字渤涌,如:click事件的事件處理程序名字為onclick;
為事件指定處理程序的方式有好幾種:
方式1. 對象的事件屬性
格式為:
對象.事件屬性 = 事件處理函數(shù);
示例:
var btn = document.getElementById("myBtn");
btn.onclick = function(){
//處理事件
}
這種添加方式有以下特點:
- 事件處理程序是作為對象的方法來運行的把还,所以事件處理程序中this的值是相應(yīng)的對象实蓬;
- 事件處理程序會在事件流的冒泡階段被處理;
- 通過給對象的事件屬性設(shè)置null來移除事件吊履;
- 只能同時指定一個事件處理程序安皱;
- 只能把事件處理程序添加到冒泡階段;
- 使用 對象的事件屬性(方式1) 和 HTML的事件標(biāo)簽屬性(方式3)這兩種方式對 同一個元素 添加的事件處理器艇炎,只會最后一次設(shè)置的有效酌伊;因為 HTML的事件標(biāo)簽屬性的方式(方式3) 最終也是通過 對象的事件屬性的方式(方式1) 來添加事件處理器的;
- 如果事件處理程序返回假值
return false;
缀踪,則會阻止此事件的相關(guān)默認操作居砖,即:相當(dāng)于調(diào)用事件對象event的preventDefault()方法;
方式2. 元素的addEventListener方法
這種方式是通過元素的addEventListener方法添加事件處理程序辜贵,通過元素的removeEventListener方法移除事件悯蝉;
所有DOM節(jié)點中都包含addEventListener和removeEventListener這2個實例方法归形,這2個方法的描述如下:
addEventListener方法:
語法:
element.addEventListener(event, function, useCapture)
參數(shù)值:
event:必須托慨。字符串,指定事件名暇榴。
注意: 不要使用 "on" 前綴厚棵。 例如,使用 "click" ,而不是使用 "onclick"蔼紧。
function:必須婆硬。指定要事件觸發(fā)時執(zhí)行的函數(shù)。
當(dāng)事件對象會作為第一個參數(shù)傳入函數(shù)奸例。 事件對象的類型取決于特定的事件彬犯。例如, "click" 事件屬于 MouseEvent(鼠標(biāo)事件) 對象查吊。
useCapture:可選谐区。布爾值,指定事件是否在捕獲或冒泡階段執(zhí)行逻卖。true - 事件句柄在捕獲階段執(zhí)行
false- false- 默認宋列。事件句柄在冒泡階段執(zhí)行;
示例:
var btn = document.getElementById(“myBtn”);
btn.addEventListener("click", function(){
document.getElementById("demo").innerHTML = "Hello World";
});
removeEventListener方法:
語法:
element.removeEventListener(event, function, useCapture)
參數(shù)值:
event:必須评也。要移除的事件名稱炼杖。
注意: 不要使用 "on" 前綴灭返。 例如,使用 "click" ,而不是使用 "onclick"坤邪。
function:必須熙含。指定要移除的函數(shù)。
useCapture:可選艇纺。布爾值婆芦,可選。布爾值喂饥,指定移除事件句柄的階段消约。
true - 在捕獲階段移除事件句柄;
false- 默認员帮。在冒泡階段移除事件句柄或粮;
注意: 如果添加兩次事件句柄,一次在捕獲階段捞高,一次在冒泡階段氯材,你必須單獨移除該事件。
示例:
// 向 <div> 元素添加事件句柄
document.getElementById("myDIV").addEventListener("mousemove", myFunction);
// 移除 <div> 元素的事件句柄
document.getElementById("myDIV").removeEventListener("mousemove", myFunction);
這種添加方式有以下特點:
- 可以添加多個處理事件硝岗;
- 通過addEventListener添加的事件處理程序氢哮,必須通過removeEventListener才能移除;
- 即可以把事件處理程序添加到捕獲階段型檀,又可以添加到冒泡階段冗尤;
- 事件處理程序中this的值是被添加的元素對象;
- 不能通過事件處理程序返回假值
return false;
來會阻止此事件的相關(guān)默認操作胀溺;
方式3. HTML的事件標(biāo)簽屬性
格式為:
<標(biāo)簽名 事件處理程序名="JavaScript代碼">
通過這種方式指定的事件處理代碼(相應(yīng)事件處理程序的標(biāo)簽屬性(簡稱:事件標(biāo)簽屬性)的值裂七,如:“JavaScript代碼”)會被放在一個函數(shù)環(huán)境中去執(zhí)行;具體實現(xiàn)機制如下:
通過事件的標(biāo)簽屬性指定事件的實現(xiàn)要機制:
- 瀏覽器會為相應(yīng)事件動態(tài)地創(chuàng)建一個函數(shù)作為相應(yīng)事件的事件處理函數(shù)仓坞,并把該函數(shù)通過對象的事件屬性的方式設(shè)置給了當(dāng)前元素dom對象上對應(yīng)的事件處理器屬性上背零,該函數(shù)如下:
dom對象.事件屬性 = function (event) { with (document){ with (this){ //元素的事件標(biāo)簽屬性的值會在這里被當(dāng)作JavaScript代碼來執(zhí)行; eval(事件標(biāo)簽屬性的值); } } }
- 這個動態(tài)創(chuàng)建的事件處理函數(shù)中有一個入?yún)⑽薨#?
event
徙瓶,用來接收事件對象;- 事件標(biāo)簽屬性的值會被當(dāng)作JavaScript代碼在這個動態(tài)創(chuàng)建的事件處理程序中運行:
- 這個動態(tài)創(chuàng)建的事件處理函數(shù)內(nèi)部使用
with
將document
和this
對象擴展了成當(dāng)前的作用域?qū)ο蠹党疲?document
和this
的所有屬性都可以在當(dāng)前作用域里直接通過屬性名字作為變量來直接訪問侦镇;
通過這個實現(xiàn)機制,可知:
通過事件的標(biāo)簽屬性指定事件的方式有以下特點:
- 事件標(biāo)簽屬性的值會被當(dāng)作JavaScript代碼在這個動態(tài)創(chuàng)建的事件處理程序中運行澎埠;
- 使用 對象的事件屬性(方式1) 和 HTML的事件標(biāo)簽屬性(方式3)這兩種方式對 同一個元素 添加的事件處理器虽缕,只會最后一次設(shè)置的有效;因為 HTML的事件標(biāo)簽屬性的方式(方式3) 最終也是通過 對象的事件屬性的方式(方式1) 來添加事件處理器的;
- 這個動態(tài)創(chuàng)建的事件處理函數(shù)中的tihs值是事件的目標(biāo)元素氮趋;事件標(biāo)簽屬性的JavaScript代碼中被調(diào)用的函數(shù)的this值是 window 對象伍派;
- 在事件標(biāo)簽屬性的值的JavaScript代碼中,可以直接訪問 保存事件對象的變量
event
剩胁、綁定當(dāng)前事件的dom元素this
诉植,也可以通過document
對象 和 綁定當(dāng)前事件的dom對象 的任意屬性名字作為變量名來直接訪問;如:<input onclick="clickHandler(event,this,cookie,value)">
昵观,其中cookie
是document
對象的屬性晾腔,value
是 input元素的Dom對象的屬性;- 只能把事件處理程序添加到冒泡階段啊犬;
- 在事件標(biāo)簽屬性的值中可以通過返回假值
return false;
來阻止此事件的相關(guān)默認操作灼擂,即:相當(dāng)于調(diào)用事件對象event的preventDefault()方法;
18. change觉至、propertychange 和 input 事件的區(qū)別
onchange 事件與 onpropertychange 事件的區(qū)別:
onchange 事件在內(nèi)容改變(兩次內(nèi)容有可能還是相等的)且失去焦點時觸發(fā)剔应;
onpropertychange 事件卻是實時觸發(fā),即每增加或刪除一個字符就會觸發(fā)语御,通過 js 改變也會觸發(fā)該事件峻贮,但是該事件 IE 專有;
oninput 事件與 onpropertychange 事件的區(qū)別:
oninput 事件是 IE 之外的大多數(shù)瀏覽器支持的事件应闯,在 value 改變時觸發(fā)纤控,實時的,即每增加或刪除一個字符就會觸發(fā)碉纺,然而通過 js 改變 value 時船万,卻不會觸發(fā)。
onpropertychange 事件是任何屬性改變都會觸發(fā)的惜辑,而 oninput 卻只在 value 改變時觸發(fā)唬涧,oninput 要通過 addEventListener() 來注冊,onpropertychange 注冊方式跟一般事件一樣盛撑。(此處都是指在js中動態(tài)綁定事件,以實現(xiàn)內(nèi)容與行為分離)
oninput 與 onpropertychange 失效的情況:
oninput 事件:
- 當(dāng)腳本中改變 value 時捧搞,不會觸發(fā)抵卫;
- 從瀏覽器的自動下拉提示中選取時,不會觸發(fā)胎撇;
onpropertychange 事件:
- 當(dāng) input 設(shè)置為 disable=tru e后介粘,onpropertychange 不會觸發(fā);
19. void運算符
void是一元運算符晚树,它用在操作數(shù)的前面姻采,操作數(shù)可以是任意類型;操作數(shù)會被正常計算爵憎,void會忽略操作數(shù)的計算結(jié)果慨亲,并返回undefined婚瓜;
20. textContent 與 innerText、innerHTML 的區(qū)別
Node.textContent 屬性表示一個節(jié)點及其后代的文本內(nèi)容刑棵。
語法
let text = element.textContent;
element.textContent = "this is some sample text";
描述
- 如果 element 是 Document巴刻,DocumentType 或者 Notation 類型節(jié)點,則 textContent 返回 null蛉签。如果你要獲取整個文檔的文本以及CDATA數(shù)據(jù)胡陪,可以使用 document.documentElement.textContent。
- 如果節(jié)點是個CDATA片段碍舍,注釋柠座,ProcessingInstruction節(jié)點或一個文本節(jié)點,textContent 返回節(jié)點內(nèi)部的文本內(nèi)容(即 nodeValue)片橡。
- 對于其他節(jié)點類型愚隧,textContent 將所有子節(jié)點的 textContent 合并后返回,除了注釋锻全、ProcessingInstruction節(jié)點狂塘。如果該節(jié)點沒有子節(jié)點的話,返回一個空字符串鳄厌。
- 在節(jié)點上設(shè)置 textContent 屬性的話荞胡,會刪除它的所有子節(jié)點,并替換為一個具有給定值的文本節(jié)點了嚎。
與innerText的區(qū)別
Internet Explorer 引入了 node.innerText泪漂。意圖類似,但有以下區(qū)別:
- textContent 會獲取所有元素的內(nèi)容歪泳,包括
<script>
和<style>
元素萝勤,然而 innerText 不會。 - innerText意識到樣式呐伞,并且不會返回隱藏元素的文本敌卓,而textContent會。
- 由于 innerText 受 CSS 樣式的影響伶氢,它會觸發(fā)重排(reflow)趟径,但textContent 不會。
- 與 textContent 不同的是, 在 Internet Explorer (對于小于等于 IE11 的版本) 中對 innerText 進行修改癣防, 不僅會移除當(dāng)前元素的子節(jié)點蜗巧,而且還會永久性地破壞所有后代文本節(jié)點(所以不可能再次將節(jié)點再次插入到任何其他元素或同一元素中)。
與innerHTML的區(qū)別
正如其名稱蕾盯,innerHTML 返回 HTML 文本幕屹。通常,為了在元素中檢索或?qū)懭胛谋荆藗兪褂胕nnerHTML望拖。但是渺尘,textContent通常具有更好的性能,因為文本不會被解析為HTML靠娱。此外沧烈,使用textContent可以防止 XSS 攻擊。
歸屬區(qū)別
- textContent 是 Node 對象的屬性像云;
- innerHTML 是 Element 對象的屬性锌雀;
- innerText 是 HTMLElement 對象的屬性;
21. 類型檢查與驗證
typeof
語法:
typeof運算符后跟操作數(shù):
typeof operand
或者
typeof (operand)
參數(shù):
- operand 是一個表達式迅诬,表示對象或原始值腋逆,其類型將被返回;
- 括號是可選的侈贷。
說明:
typeof 操作符返回一個字符串惩歉,表示操作數(shù)的基本類型。
下表總結(jié)了typeof可能的返回值:
類型 | 結(jié)果 |
---|---|
Undefined | "undefined" |
Null | "object" |
Boolean | "boolean" |
Number | "number" |
String | "string" |
Symbol | "symbol" |
宿主對象(由JS環(huán)境提供) | Implementation-dependent |
函數(shù)對象 | "function" |
任何其他對象 | "object" |
instanceof
語法:
object instanceof constructor
參數(shù):
- object : 要檢測的對象
- constructor : 某個構(gòu)造函數(shù)
說明:
instanceof 運算符用來檢測 constructor.prototype 是否存在于參數(shù) object 的原型鏈上俏蛮。
機制:
instanceof 的等效函數(shù)如下:
function instanceof(object,constructor){
let nextProto = object.__proto__;
while (constructor !== null) {
if (nextProto === constructor.prototype) {
return true;
}
nextProto = nextProto.__proto__;
}
return false;
}
或者
function instanceof(object,constructor){
let nextProto = Object.getPrototypeOf(object);
while (constructor !== null) {
if (nextProto === constructor.prototype) {
return true;
}
nextProto = Object.getPrototypeOf(nextProto);
}
return false;
}
typeof 與 instanceof 區(qū)別
- typeof 是用來檢查類型的撑蚌,而 instanceof 是用來驗證類型的;
- typeof 返回的是字符串搏屑,而 instanceof 返回的是布爾值争涌;
- typeof 檢查的是基本類型,而 instanceof 驗證的是對象類型辣恋;
獲取對象類型的具體類型信息
typeof 操作符只能返回操作數(shù)的基本類型亮垫,對于任何自定義的對象類型, typeof 操作符只會返回 "object" 伟骨,而 instanceof 是用來驗證對象類型的饮潦,不是用來獲取對象類型的;若想獲取對象的具體類型信息携狭,可通過訪問對象的 constructor 屬性继蜡,該屬性會返回該對象的構(gòu)造函數(shù);若只想獲取對象的具體類型的名字暑中,則可通過訪問對象的構(gòu)造函數(shù) constructor 的 name 屬性壹瘟;如下:
獲取對象的類型:
object.constructor
獲取對象的類型的名字:
object.constructor.name
22. Attribute和Property的區(qū)別
1. Attribute和Property的概念
- attribute : 特性,XML 元素中的概念鳄逾,用于描述 XML 標(biāo)簽的附加信息,即: XML 標(biāo)簽的特性灵莲; 如:
<input type="text" value="初始值" />
中的type
雕凹、value
均是input元素的特性; - property : 屬性,JavaScript 對象的概念,用于描述 JavaScript 對象的成員枚抵,即:JavaScript 對象的屬性线欲;如: JavaScript 對象
var person = {name:"郭斌勇",age:28}
中的name
、age
均是對象person
的屬性汽摹;
2. Attribute和Property的關(guān)系
為了能夠在 JavaScript 中操作 HTML 瀏覽器會為 HTML 中的元素創(chuàng)建相應(yīng)的 Dom 對象李丰,Dom 對象就是普通的 JavaScript 對象,它是 HTMLElement
類型的對象逼泣,所以 dom 對象的所有成員都稱為 property (屬性)趴泌;
JavaScript 中的 HTMLElement
類型的對象有個 attributes
屬性(property),它是 NamedNodeMap
類型的類數(shù)組對象 拉庶,它里面保存的都是 HTML 中元素的 特性(attribute) 相對應(yīng)的 JavaScript 對象嗜憔;在 JavaScript 中,HTML 中元素的 attribute (特性) 用 Attr 類型的對象表示氏仗;
在 XML 中吉捶,元素的特性(attribute)值是沒有類型之分的,所以皆尔,無論是數(shù)字呐舔、字符中、布爾值慷蠕,在 XML 眼中珊拼,它們都是同一類東西; JavaScript 中的 Attr 類型把 HTML 中的元素的特性值都映射為字符串類型砌们;為了更好地使用 Dom 杆麸, 瀏覽器也在 Dom 對象中為標(biāo)準(zhǔn)的 HTML 元素的特性(attribute)添加了相應(yīng)的屬性 property ,如:dom 對象的 id
浪感、type
昔头、checked
、value
影兽、defaultValue
等等揭斧;為了更好的使用,瀏覽器為這些屬性的值也做了相應(yīng)的處理峻堰,所以讹开,這些屬性(property)的值與 HTML 元素中相應(yīng)的特性值并非完全一致;如:對于 Dom 對象捐名,dom.checked
的值始終是布爾類型的旦万,dom.value
的值始終是相應(yīng) input 元素的當(dāng)前值,并非是 HTML 中 input 元素的 value 特性的值镶蹋,dom.defaultValue
則是 HTML 中 input 元素的 value 特性的值成艘;