目前 JavaScript 仍是前端開發(fā)的靈魂抛计,各種層出不窮的框架其實都是與底層相關(guān)。
開始之前跨释,借前端三元同學(xué)的靈魂發(fā)問自測一下掌握了多少:
原生JS靈魂之問(中),檢驗自己是否真的熟悉JavaScript鳖谈?
數(shù)據(jù)類型
- 簡單數(shù)據(jù)類型(棧內(nèi)存)
string
number
boolean
null
undefined
symbol(創(chuàng)建后獨一無二且不可變的數(shù)據(jù)類型,常用于解決全局變量命名沖突缆娃、創(chuàng)建私有變量)
bigint(操作超出JS安全范圍的大整數(shù))
ES6規(guī)范不建議用new來創(chuàng)建基本類型的包裝類捷绒,用new 新建 symbol bigint會報錯。
- 引用數(shù)據(jù)類型(堆內(nèi)存)
<pre class="hljs delphi" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">Object Array Function </pre>
類型判斷
typeof
不能區(qū)分Object, Array, null贯要,都會返回object疙驾,null在設(shè)計之初就是對象。
instanceof
原理:檢查右邊構(gòu)造函數(shù)的 prototype
屬性郭毕,是否在左邊對象的原型鏈上它碎。
JS中一切皆對象,每個對象(除了null和undefined)都有自己的原型 __proto__
显押,指向?qū)?yīng)的構(gòu)造函數(shù)的 prototype
屬性扳肛,只有函數(shù)有 prototype
屬性。
只能用于對象乘碑,適合用于判斷自定義的類實例對象挖息,能夠區(qū)分Array、Object和Function兽肤,但是Array和Function也可以是Object套腹。
有一種特殊情況,當(dāng)左邊對象的原型鏈上只有 null
對象资铡, instanceof
判斷會失真电禀。
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">/**
- @description instanceof
- 檢測左邊對象在其原型鏈中是否存在構(gòu)右邊函數(shù)的 prototype 屬性
- 若是簡單數(shù)據(jù)類型或null直接返回false,原型鏈的盡頭是null
- @param {*} left
- @param {*} right
*/
function myInstanceof(left, right) {
if (typeof left !== 'object' || left === null) return false;
let proto = Object.getPrototypeOf(left);
while (proto !== null) {
if (proto === right.prototype) return true
proto = Object.getPrototypeOf(proto);
}
return false;
}
console.log(myInstanceof("111", String)); //false
console.log(myInstanceof(new String("111"), String));//true
復(fù)制代碼</pre>
Object.prototype.toString.call()
精準(zhǔn)判斷數(shù)據(jù)類型笤休。
<pre class="prettyprint hljs scala" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">var type = function (o){
var s = Object.prototype.toString.call(o);
return s.match(/[object (.*?)]/)[1].toLowerCase();
};
type({}); // "object"
type([]); // "array"
type(5); // "number"
type(null); // "null"
type(); // "undefined"
type(/abcd/); // "regex"
type(new Date()); // "date"
復(fù)制代碼</pre>
類型轉(zhuǎn)換
其他類型轉(zhuǎn)字符串
<pre class="hljs livescript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">null / undefined: null -> 'null', undefined -> 'undefined'尖飞。
</pre>
<pre class="hljs elixir" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">boolean:true -> 'true', false -> 'false'。
</pre>
number:直接轉(zhuǎn)換店雅,極大或極小值可能用指數(shù)形式政基。
symbol:只允許顯示強制類型轉(zhuǎn)換。
Object:對普通對象來說闹啦,除非自定義toString()方法沮明,否則會調(diào)用Object.prototype.toString()方法返回內(nèi)部屬性[[class]]。Array有自己的toString()方法窍奋。
<pre class="prettyprint hljs swift" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">'1'.toString(); // 1 會先轉(zhuǎn)成對象荐健,然后對象轉(zhuǎn)字符串酱畅,并不是三元說的null啊
1.toString(); // 報錯 .被認為是小數(shù)點
(1).toString(); // "1"
復(fù)制代碼</pre>
其他類型轉(zhuǎn)數(shù)值
null:0
<pre class="hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">undefined: NaN
</pre>
<pre class="hljs elixir" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">boolean:true -> 1, false -> 0
</pre>
string:相當(dāng)于Number()方法,空字符串為0摧扇,包含非數(shù)字字符則為NaN圣贸。
symbol:不能轉(zhuǎn)換為數(shù)字。
Object:首先被轉(zhuǎn)換為相應(yīng)的基本類型值扛稽,如果返回的是非數(shù)字的基本類型值吁峻,則再遵循以上規(guī)則將其強制轉(zhuǎn)換為數(shù)字。
解析字符串在张,如 parseInt() 用含,允許含有非數(shù)字字符,按從左到右的順序解析帮匾,如果遇到非數(shù)字字符就停止啄骇。
轉(zhuǎn)換字符串,如 Number()瘟斜,不允許出現(xiàn)非數(shù)字字符缸夹,否則會失敗并返回 NaN。
其他類型轉(zhuǎn)布爾值
假值:undefined, null, false, +0, -0, NaN, ""
所有對象(包括空對象)的轉(zhuǎn)換結(jié)果都是 true
螺句,甚至連 false
的布爾對象 new Boolean(false)
也是 true
==
===嚴格相等虽惭,要求數(shù)據(jù)類型相同;==相等蛇尚,會轉(zhuǎn)換為同一類型再進行比較芽唇;
- 兩者類型為null / undefined:true
- 一者類型為null / undefined:false
- 兩者類型為string 和 number:將string轉(zhuǎn)為 number
- 一者類型為boolean:將 boolean 轉(zhuǎn)為 number
- 一者類型為object,其另一者類型為string, number 或 symbol取劫,將 object 轉(zhuǎn)為原始類型匆笤。
- 兩者都為引用類型(對象、數(shù)組谱邪、函數(shù)):比較是否指向同一個地址炮捧,兩個空對象、兩個空數(shù)組虾标、兩個空函數(shù)指向不同的內(nèi)存地址寓盗。
<pre class="prettyprint hljs cpp" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">console.log({a: 1} == true); //false
console.log({a: 1} == "[object object]"); //true
console.log([1] == 1); //true 相當(dāng)于調(diào)用valueOf()方法
console.log([1] == '1'); //true
復(fù)制代碼</pre>
[] != [] 是 true,那么[] == ![] 為什么是true
- 右邊:運算符的優(yōu)先級更高璧函,![] = !true = false;boolean需要轉(zhuǎn)換為number基显,false = 0蘸吓。
- 左邊,此時一方為object且另一方為number撩幽,將object轉(zhuǎn)換為原始類型库继,[] = ''箩艺;此時兩者類型為string和number,將string轉(zhuǎn)換為number, ''=0宪萄。
對象轉(zhuǎn)原始類型
對象轉(zhuǎn)原始類型艺谆,會調(diào)用內(nèi)置的[ToPrimitive]函數(shù),對于該函數(shù)而言拜英,其邏輯如下:
- 如果Symbol.toPrimitive()方法静汤,優(yōu)先調(diào)用再返回
- 調(diào)用valueOf(),如果轉(zhuǎn)換為原始類型居凶,則返回
- 調(diào)用toString()虫给,如果轉(zhuǎn)換為原始類型,則返回
- 如果都沒有返回原始類型侠碧,會報錯
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">var a = {
value: 0,
valueOf: function() {
this.value++;
return this.value;
}
};
console.log(a == 1 && a == 2);//true
復(fù)制代碼</pre>
null和undefined
undefined類型只有一個值抹估,即undefined,表示在作用域中聲明但還沒有賦值弄兜,在轉(zhuǎn)換為數(shù)值時是NaN药蜻,用法:
- 變量聲明了,但是還沒有賦值替饿,默認為undefined语泽,如new Array(n);
- 調(diào)用函數(shù)時盛垦,沒有提供必需的參數(shù)湿弦,該參數(shù)等于undefined;
- 對象沒有賦值的屬性腾夯,該屬性的值為undefined颊埃;
- 函數(shù)沒有返回值時,默認返回undefined蝶俱;
null類型也只有一個值班利,即null,用來表示尚未存在的對象榨呆,在轉(zhuǎn)換為數(shù)值時是0翁逞,用法:
- 作為函數(shù)的參數(shù),表示該函數(shù)的參數(shù)不是對象软瞎;
- 作為原型鏈的終點骇笔;
Number
浮點數(shù)精度
JavaScript 只有一種數(shù)字類型Number,所有數(shù)字都是以64位浮點數(shù)形式儲存竿拆,因此設(shè)計小數(shù)的比較與運算要很小心宙拉。
JavaScript 內(nèi)部, 1
與 1.0
是是同一個數(shù)丙笋,存在2個 0
:一個是 +0
谢澈,一個是 -0
煌贴,區(qū)別就是64位浮點數(shù)表示法的符號位不同。唯一的區(qū)別在于锥忿, +0
或 -0
當(dāng)作分母牛郑,返回的值是不相等的。
0.1+0.2 !=0.3 怎么處理 把需要計算的數(shù)字升級(乘以10的n次冪)成計算機能夠精確識別的整數(shù)敬鬓,等計算完成后再進行降級(除以10的n次冪)淹朋,即:
<pre class="prettyprint hljs coffeescript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">(0.110 + 0.210)/10 == 0.3 //true
復(fù)制代碼</pre>
toFixed在不同瀏覽器下的四舍五入情況不太一致,可以重寫toFixed()函數(shù)統(tǒng)一列林。
安全整數(shù)
在安全范圍內(nèi)的整數(shù)瑞你,在二進制轉(zhuǎn)換時不會出現(xiàn)精度丟失的情況。
JavaScript 浮點數(shù)的64個二進制位希痴,從最左邊開始者甲,是這樣組成的:
第1位:符號位, 0
表示正數(shù)砌创, 1
表示負數(shù) 第2位到第12位(共11位):指數(shù)部分 第13位到第64位(共52位):小數(shù)部分(即有效數(shù)字)
精度最多只能到53個二進制位虏缸,這意味著,絕對值小于2的53次方的整數(shù)嫩实,即-2- 1到2刽辙,都可以精確表示。超出會自動轉(zhuǎn)換成 Infinity
或 -Infinity
甲献。
使用字面量直接表示一個數(shù)值時宰缤,JavaScript 對整數(shù)提供四種進制的表示方法:十進制、十六進制晃洒、八進制慨灭、二進制。
- 十進制:沒有前導(dǎo)0的數(shù)值球及。
- 八進制:有前綴
0o
或0O
的數(shù)值氧骤,或者有前導(dǎo)0、且只用到0-7的八個阿拉伯?dāng)?shù)字的數(shù)值吃引。 - 十六進制:有前綴
0x
或0X
的數(shù)值筹陵。 - 二進制:有前綴
0b
或0B
的數(shù)值。
在安全整數(shù)范圍內(nèi)镊尺,可通過 parseInt()
方法進行進制轉(zhuǎn)換朦佩。
JavaScript 提供 Number
對象的 MAX_VALUE
和 MIN_VALUE
屬性,返回可以表示的具體的最大值和最小值庐氮。
NaN
NaN
是 JavaScript 的特殊值吕粗,表示“非數(shù)字”(Not a Number)。
NaN
是唯一一個非自反的值旭愧,不等于任何值颅筋,包括它本身,通常用Number.isNaN()函數(shù)判斷输枯。
- isNaN()
會嘗試將這個參數(shù)轉(zhuǎn)換為數(shù)值议泵,任何不能被轉(zhuǎn)換為數(shù)值的的值都會返回 true,因此非數(shù)字值傳入也會返回 true 桃熄。
- Number.isNaN()
會首先判斷傳入?yún)?shù)是否為數(shù)字先口,如果是數(shù)字再繼續(xù)判斷是否為 NaN ,對于 NaN 的判斷更為準(zhǔn)確瞳收。
函數(shù)
函數(shù)定義
函數(shù)和變量同名會如何碉京?
函數(shù)定義優(yōu)先于變量提升。
函數(shù)聲明
<pre class="prettyprint hljs php" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// es5
function getSum(){}
function (){} // 匿名函數(shù)
// es6
() => {}
復(fù)制代碼</pre>
函數(shù)表達式
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// es5
var getSum = function(){}
// es6
const getSum = () => {}
復(fù)制代碼</pre>
構(gòu)造函數(shù)
<pre class="prettyprint hljs php" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">const getSum = new Function('a', 'b', 'return a+b')
復(fù)制代碼</pre>
高階函數(shù)
參數(shù)值為函數(shù)或者返回值為函數(shù)螟深。例如map谐宙,reduce,filter界弧,sort方法就是高階函數(shù)凡蜻。 編寫高階函數(shù),就是讓函數(shù)的參數(shù)能夠接收別的函數(shù)垢箕。
map(callback([item, index, array])[, thisArg]) reduce(callback([prevSum, currVal, array])[, originalVal]) filter(callback(item)) sort(callback(a,b)) 不傳函數(shù)參數(shù)時划栓,默認將值轉(zhuǎn)換為字符串,根據(jù)字母unicode值進行升序排序条获。
閉包
變量的作用域:在ES5中忠荞,只有全局作用域和函數(shù)作用域。
變量的生命周期:函數(shù)作用域內(nèi)的局部變量帅掘,會隨著函數(shù)調(diào)用的結(jié)束而被銷毀委煤。
在ES5時代,作用域通信常用的解決方式就是閉包锄开,但是對性能有負面影響(多執(zhí)行了一個函數(shù)素标,多一個內(nèi)存指向)。
- 概念
簡單來說萍悴,閉包(Closure)可以理解成“從內(nèi)部函數(shù)訪問外部函數(shù)作用域”头遭。函數(shù)作用域內(nèi)的局部變量,在父級作用域內(nèi)聲明癣诱,函數(shù)調(diào)用結(jié)束后仍然保留在內(nèi)存里计维。
JavaScript 語言特有的"鏈?zhǔn)阶饔糜?結(jié)構(gòu)(chain scope),當(dāng)訪問一個變量時撕予,解釋器會首先在當(dāng)前作用域查找標(biāo)示符鲫惶,如果沒有找到,會一級一級地向上尋找所有父對象的變量实抡。所以欠母,父對象的所有變量欢策,對子對象都是可見的,反之則不成立赏淌。
- 應(yīng)用
封裝異步操作踩寇,如 setTimeout()
, onClick
; 封裝變量六水; 延長局部變量的生命周期俺孙;
- 內(nèi)存管理
如果閉包的作用域鏈里包含了DOM節(jié)點,容易造成內(nèi)存泄漏掷贾,但這本質(zhì)上是垃圾回收機制的循環(huán)引用問題睛榄。 解決方案是在不需要使用變量后,設(shè)為 null
想帅。
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">for(var i = 1; i <= 5; i ++){
setTimeout(function timer(){
console.log(i)
}, 0)
} // 宏任務(wù)场靴,閉包,全部輸出6
for(var i = 1;i <= 5;i++){
(function(j){
setTimeout(function timer(){
console.log(j)
}, 0)
})(i)
} // 立即執(zhí)行函數(shù)表達式博脑,在循環(huán)時把i作為變量傳入憎乙,依次輸出 1 ~ 5
for(let i = 1; i <= 5; i ++){
setTimeout(function timer(){
console.log(i)
}, 0)
} // 塊級作用域,依次輸出 1 ~ 5
/**
- @description 閉包實現(xiàn)計數(shù)
*/
var counter = (function() {
var i = 0;
return function myPrint() {
i += 1;
console.log(i);
return i;
}
})();
counter(); // 1
counter(); // 2
counter(); // 3
復(fù)制代碼</pre>
柯里化
函數(shù)柯里化指的是把接受多個參數(shù)的一個函數(shù)轉(zhuǎn)換成一系列接受單個參數(shù)的函數(shù)叉趣。
curry 的這種用途可以理解為:參數(shù)復(fù)用泞边、提前返回和延遲執(zhí)行。典型的應(yīng)用場景是求和疗杉。
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">/**
- @description curry 把接受多個參數(shù)的函數(shù)轉(zhuǎn)換為一系列接受單個參數(shù)的函數(shù)
- @param {Function} fn
- @param {...any} args
*/
function curry(fn, ...args) {
return fn.length <= args.length ? fn(...args) : curry.bind(null, fn, ...args);
}
function curry(fn, args) {
args = args || [];
return function () {
let newArgs = args.concat([...arguments]);
if (fn.length <= newArgs.length) {
return fn.apply(this, newArgs);
} else {
return curry.call(this, fn, newArgs);
}
}
}
function multiFn(a, b, c) {
console.log(a, b, c)
return a * b * c;
}
var multi = curry(multiFn);
multi(2)(3)(4);
復(fù)制代碼</pre>
純函數(shù)
概念:相同的輸入永遠會得到相同的輸出阵谚,而且沒有任何可觀察的副作用。
例子: slice
提取目標(biāo)數(shù)組的一部分不改變原數(shù)組烟具,是純函數(shù)梢什;而 splice
返回原數(shù)組被刪除的部分元素,并可以在刪除的位置添加新的數(shù)組成員朝聋,會改變原數(shù)組嗡午,不是純函數(shù)。
偏函數(shù)
概念:使用一個函數(shù)冀痕,應(yīng)用其中一個或多個參數(shù)但不是全部參數(shù)荔睹,在這個過程中創(chuàng)建一個新函數(shù),新函數(shù)用于接受剩余的參數(shù)去完成功能言蛇。
防抖和節(jié)流
函數(shù)被觸發(fā)的頻率太高僻他,出于性能考慮,不希望回調(diào)函數(shù)被頻繁調(diào)用腊尚。 如window.onresize事件吨拗,mousemove事件,上傳進度,頻繁提交表單劝篷,輸入搜索聯(lián)想等哨鸭。
防抖(debounce)
函數(shù)被觸發(fā)執(zhí)行后,如果單位時間內(nèi)又被觸發(fā)携龟,不會執(zhí)行兔跌,且重新計時。
- 非立即執(zhí)行版峡蟋,至少等待n秒后執(zhí)行
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">/**
- @description debounce 非立即執(zhí)行版 適用場景:resize, input search
- @param {Function} fn
- @param {Number} interval
*/
const debounce = (fn, interval) => {
let timer = null;
// 箭頭函數(shù)沒有arguments,需要手動調(diào)用...args
return (...args) => {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
fn(...args);
}, interval);
}
}
function debounce (fn, interval) {
let timer = null;
return function () {
let context = this;
let args = arguments;
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(context, args);
}, interval);
}
}
復(fù)制代碼</pre>
- 立即執(zhí)行版华望,觸發(fā)事件后函數(shù)會立即執(zhí)行蕊蝗,然后 n 秒內(nèi)不觸發(fā)事件才能繼續(xù)執(zhí)行函數(shù)的效果宾抓。
節(jié)流(throttle)
稀釋函數(shù)的執(zhí)行頻率招驴,單位時間內(nèi)只執(zhí)行一次批狐。
- 時間戳版
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">function throttle (fn, delay) {
let previous = 0;
return function() {
let now = Date.now();
let _this = this;
let args = arguments;
if (now - previous > delay) {
fn.apply(_this, args);
previous = now;
}
}
}
復(fù)制代碼</pre>
- 定時器版
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">/**
- @description throttle
- @param {Function} fn
- @param {Number} interval
*/
const throttle = (fn, interval) => {
let timer = null;
return (...args) => {
if (!timer) {
timer = setTimeout(() => {
timer = null;
fn(...args);
}, interval);
}
}
}
function throttle(fn, interval) {
let timer = null;
return funtion () {
let context = this;
let args = arguments;
if (!timer) {
timer = setTimeout(() => {
timer = null;
fn.apply(this, args);
}, interval);
}
}
}
復(fù)制代碼</pre>
ES6新語法
面試官會根據(jù)你的回答擴散發(fā)問既绕。
變量聲明與作用域
- var
全局作用域(ES5只有全局作用域和函數(shù)作用域)。
存在 變量提升 (即變量可以在聲明之前使用甚带,值為 undefined
)碉输,函數(shù)聲明優(yōu)先于變量提升甜害。
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// 內(nèi)層變量會覆蓋外層變量嚣州,內(nèi)層存在變量提升
var tmp = new Date();
function f() {
console.log(tmp);
if (false) {
var tmp = 'hello world';
}
}
f(); // undefined
復(fù)制代碼</pre>
- let
塊級作用域:該語句所在的代碼塊內(nèi)鲫售。
不存在變量提升,但是可能發(fā)生 臨時死區(qū) (ReferenceError)该肴。
在ES5中情竹,可通過立即執(zhí)行函數(shù)表達式來模擬塊級作用域。
- const
只讀常量匀哄,指的是變量指向的內(nèi)存地址不得改動秦效,對于簡單數(shù)據(jù)類型,等同于常量涎嚼;對于引用數(shù)據(jù)類型阱州,變量指向的內(nèi)存地址保存的只是一個指向?qū)嶋H值的指針, const
只能保證這個指針是固定的(即總是指向另一個固定的地址)法梯。
在ES5中苔货,可通過 Object.defineProperty
設(shè)置 writable 和 configurable 屬性為false來模擬const。
箭頭函數(shù)
- 箭頭函數(shù)的的this,就是定義時所在的對象蒲赂;
- 一旦綁定了上下文阱冶,就不可改變箭頭函數(shù)內(nèi)部 this 的指向(call、apply滥嘴、bind 都不能改變)木蹬;
- 由于this函數(shù)的指向問題,箭頭函數(shù)不能作為構(gòu)造函數(shù)若皱,不能使用new 命令镊叁;
- 箭頭函數(shù)沒有arguments,需要手動使用...args參數(shù)代替走触;
- 箭頭函數(shù)不能用作generator函數(shù)晦譬;
解構(gòu)賦值
數(shù)組、對象互广、字符串敛腌、數(shù)值、布爾值惫皱、函數(shù)參數(shù)
繼承
- ES5的繼承
new命令會先創(chuàng)建一個子類的實例對象像樊,再執(zhí)行構(gòu)造函數(shù)的代碼,把父類的屬性和方法綁定到this上旅敷。
- ES6的繼承
雖然本質(zhì)上還是基于原型鏈的繼承生棍,但是會先執(zhí)行super(),把父類實例的屬性和方法綁定到this上媳谁,再通過子類的構(gòu)造函數(shù)修改this涂滴。
模塊
Node和ES6模塊
Node, CommonJS
- 運行時加載(require)。
- 單值導(dǎo)出(加載一個模塊就是加載對應(yīng)的一個文件晴音,一個模塊被多次加載但只執(zhí)行一次柔纵,放在緩存中)。
- 模塊輸出的是值拷貝(基本數(shù)據(jù)類型:值復(fù)制段多,引用數(shù)據(jù)類型:淺拷貝)首量。
- this是當(dāng)前模塊。
ES6, Module
<pre class="hljs elm" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">import()
</pre>
異步編程
回調(diào)函數(shù)
回調(diào)地獄(異步操作成功或失敗的多層嵌套)进苍,代碼的可讀性和維護性差加缘,異常處理復(fù)雜。 回調(diào)函數(shù)觉啊,內(nèi)部使用了發(fā)布-訂閱模式拣宏。
Promise
為了解決回調(diào)地獄,Promise采用了
- 回調(diào)函數(shù)延遲綁定
回調(diào)函數(shù)不是直接聲明的杠人,而是在通過后面的 then 方法傳入的勋乾,即延遲傳入宋下。
- 返回值穿透
把多層嵌套的回調(diào),包裝成鏈?zhǔn)秸{(diào)用辑莫。
- 錯誤冒泡
Promise 對象的錯誤具有“冒泡”性質(zhì)学歧,會一直向后傳遞直到被捕獲為止。也就是說各吨,錯誤總是會被下一個 catch
語句捕獲枝笨。如果不處理錯誤,Promise 內(nèi)部的錯誤不會影響到 Promise 外部的代碼揭蜒,通俗來說就是“Promise 會吃掉錯誤”横浑。
Promise.prototype.then(onFulfilled, onRejected)
報錯用的第二個參數(shù); Promise.prototype.catch(onRejected)
報錯只有一個參數(shù)屉更,也會捕獲.then()中回調(diào)函數(shù)的錯誤徙融。
Promises/A+ 規(guī)范
Promises/A+ 規(guī)范是 JavaScript Promise 的標(biāo)準(zhǔn),規(guī)定了一個 Promise 所必須具有的特性:
- 狀態(tài)機
Promise 實例有三種狀態(tài)瑰谜,pending欺冀、fulfilled 和 rejected,分別代表進行中萨脑、已成功和已失敗脚猾。狀態(tài)只能由 pending 轉(zhuǎn)變 fulfilled 或者 rejected 狀態(tài),并且狀態(tài)變更是不可逆的砚哗。
- 構(gòu)造函數(shù)
接收一個函數(shù)作為參數(shù)卤档,函數(shù)接受兩個參數(shù)resolve和reject友雳,返回一個 Promise 實例域帐。 resolve 將狀態(tài)從 pending 變成 fulfilled凿试,并返回成功的結(jié)果 value癣籽。 reject 將狀態(tài)從 pending 變成 rejected绍些,并返回失敗的原因 reason晕拆。
- 原型方法 then()
then 方法绘梦,接受兩個參數(shù) onFulfilled 和 onRejected胸哥,分別表示promise成功或失敗后的回調(diào)函數(shù)涯竟。then() 方法會返回一個promise,支持鏈?zhǔn)秸{(diào)用空厌。 為了更好地處理異常庐船,ES6中還定義了 catch() 和finally() 方法。
Promise 的執(zhí)行順序是 then收集回調(diào) -》異步操作完成觸發(fā)resolve / reject => resolve / reject 執(zhí)行回調(diào)
嘲更,類似于 收集依賴 =》 觸發(fā)通知 =》 取出依賴執(zhí)行
的發(fā)布-訂閱模式筐钟。
<pre class="prettyprint hljs kotlin" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">const STATUS = {
PENDING: 'pending',
FULFILLED: 'fulfilled',
REJECTED: 'rejected',
}
class MyPromise {
constructor(executor) {
this.status = STATUS.PENDING;
this.value = undefined;
this.reason = undefined;
this.onResolvedCbs = [];
this.onRejectedCbs = [];
const resolve = (value) => {
if (this.status === STATUS.PENDING) {
this.status = STATUS.FULFILLED;
this.value = value;
this.onResolvedCbs.forEach(fn => fn());
}
}
const reject = (reason) => {
if (this.status === STATUS.PENDING) {
this.status = STATUS.REJECTED;
this.reason = reason;
this.onRejectedCbs.forEach(fn => fn());
}
}
try {
executor(resolve, reject);
} catch(e) {
reject(e);
}
}
then(onFulfilled, onRejected) {
return new MyPromise((fulfill, reject) => {
if (this.status === STATUS.FULFILLED) {
fulfill(onFulfilled(this.value));
}
if (this.status === STATUS.REJECTED) {
reject(onRejected(this.reason));
}
if (this.status === STATUS.PENDING) {
this.onResolvedCbs.push(() => {
fulfill(onFulfilled(this.value))
});
this.onResolvedCbs.push(() => {
reject(onRejected(this.reason))
});
}
})
}
}
復(fù)制代碼</pre>
Promise的靜態(tài)方法
- Promise.resolve()
- 參數(shù)為一個promise,直接返回一個promise對象赋朦。
- 參數(shù)為一個thenable對象篓冲,返回的promise會跟把這個對象的狀態(tài)作為自己的狀態(tài)李破。
- 參數(shù)為一個定值,返回以該值為valude的成功狀態(tài)promise壹将。
- Promise.reject()
參數(shù)作為reason嗤攻,返回一個帶有失敗原因的Promise對象。
- Promise.prototype.finally()
- Promise.all()
- 參數(shù)為空的可迭代對象诽俯,直接進行resolve()妇菱。
- 參數(shù)中所有promise的狀態(tài)都變成resolved,將所有的返回值以數(shù)組形式傳給回調(diào)函數(shù)惊畏,執(zhí)行resolve()恶耽,返回的promise對象成功。
- 參數(shù)中只要有一個promise的狀態(tài)變成rejected颜启,將該返回值以數(shù)組形式傳給回調(diào)函數(shù)偷俭,執(zhí)行reject(),返回的promise對象失敗缰盏。
- Promise.race()
只要其中一個promise的狀態(tài)發(fā)生改變涌萤,直接執(zhí)行resolve(),將返回值傳給回調(diào)函數(shù)口猜。
- Promise.allSettled()
只有當(dāng)所有promise的狀態(tài)都改變负溪,不論是成功或失敗,將所有的返回值以數(shù)組形式傳給回調(diào)函數(shù)济炎。
- Promise.any() 提案階段
和race()很像川抡,但不會因為一個promise的狀態(tài)變成rejected而結(jié)束。
- try()须尚,提案階段
模擬 try
代碼塊崖堤,就像 promise.catch
模擬的是 catch
代碼塊。
有時候不論是同步或異步操作都想用promise處理耐床,但是同步任務(wù)會變成微任務(wù)密幔,解決方法是定義立即執(zhí)行的匿名函數(shù):
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// async
const f = () => console.log('now');
(async () => f())(); // 立即執(zhí)行的匿名函數(shù),但是會吃掉f()拋出的錯誤
console.log('next');
// Promise
const f = () => console.log('now');
(
() => new Promise(
resolve => resolve(f())
)
)();
console.log('next');
復(fù)制代碼</pre>
Promise實現(xiàn)sleep(頭條)
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">const sleep = (t) => new Promise((resolve, reject) => {
setTimeout(() => resolve(), t * 1000)
});
sleep(5).then(() => console.log('awake'));
(async function () {
console.log(Date.now());
const res = await sleep(5);
console.log(res);
console.log(Date.now());
})();
復(fù)制代碼</pre>
Promise實現(xiàn)并行
Promise.allSettled()
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">let n = 3;
let arr = new Array(n);
for (let i = 0; i < n; i++) {
arr[i] = n - i;
}
function asyncPromise (id, delay) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(id), delay * 1000)
});
}
let myPromises = arr.map((item, index) => asyncPromise(index, item));
// all
Promise.all(myPromises).then(res => {
console.log('all success', res);
}).catch(err => {
console.log('one error', err);
});
// allSettled
Promise.allSettled(myPromises).then(res => {
console.log('all done', res);
}).catch(err => {
console.log('error', err);
});
復(fù)制代碼</pre>
Promise實現(xiàn)串行
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">let n = 5;
let arr = new Array(n);
for (let i = 0; i < n; i++) {
arr[i] = n - i;
}
function asyncPromise (id, delay) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(id), delay * 1000)
});
}
let myPromises = arr.map((item, index) => asyncPromise(index, item));
// reduce
function serial (myPromises) {
let result = [];
return myPromises.reduce((prev, curr, index) => prev.then(res => {
return curr.then(res => {
console.log(res);
result.push(res);
return index == myPromises.length - 1 ? result : curr;
})
}), Promise.resolve());
}
async function serial(myPromises) {
let result = [];
for (let p of myPromises) {
let res = await p;
console.log(res);
result.push(res);
}
myPromises.forEach(async p => {
let res = await p;
console.log(res);
result.push(res);
}); // 經(jīng)測試撩轰,forEach不能保證異步代碼的順序執(zhí)行胯甩,因而不能用來實現(xiàn)串行
return result;
}
serial(myPromises).then(res => {
console.log('serial done', res);
});
復(fù)制代碼</pre>
Promise實現(xiàn)并發(fā)控制的串行
隊列里始終有K個promises正在執(zhí)行
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">let n = 10;
let arr = new Array(n);
for (let i = 0; i < n; i++) {
arr[i] = n - i;
}
function asyncPromise (id, delay) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(id), delay * 1000)
});
}
let myPromises = arr.map((item, index) => asyncPromise(index, item));
function parallelK (myPromises, limit) {
return new Promise((resolve, reject) => {
let result = [];
let i = 0;
let running = 0;
add();
function add () {
while (running < limit && i < myPromises.length) {
running += 1;
myPromises[i++].then(res => {
console.log(res);
result.push(res);
}).catch(err => {
console.log(err);
result.push(err);
}).finally(() => {
running -= 1;
if (i < myPromises.length) {
add();
} else if (running == 0) {
resolve(result); // 確保最后一個異步請求也完成了才能resolve()
}
});
}
}
});
}
parallelK(myPromises, 5).then(res => {
console.log('parallel k done', res);
});
復(fù)制代碼</pre>
生成器Generator
盡管Promise通過鏈?zhǔn)交卣{(diào)取代了回調(diào)嵌套,但過多的鏈?zhǔn)秸{(diào)用可讀性仍然不強堪嫂。 通常與co庫結(jié)合偎箫,處理異步操作。
<pre class="prettyprint hljs cs" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">co(function* () {
const r1 = yield readFilePromise('1.json');
const r2 = yield readFilePromise('2.json');
const r3 = yield readFilePromise('3.json');
const r4 = yield readFilePromise('4.json');
})
復(fù)制代碼</pre>
生成器的執(zhí)行流程:
- 調(diào)用生成器函數(shù)后溉苛,程序阻塞镜廉,不會執(zhí)行任何語句;
- 調(diào)用next()方法后愚战,程序繼續(xù)執(zhí)行娇唯,直到遇到y(tǒng)ield關(guān)鍵字暫停齐遵;
- 暫停后,返回一個包含value和done屬性的對象塔插,value表示當(dāng)前 yield后的結(jié)果梗摇,done 表示是否執(zhí)行完,return語句會使得done變?yōu)閠rue想许。
async/await
async / await 利用 協(xié)程
和 Promise
實現(xiàn)了同步方式編寫異步代碼的效果伶授,被稱為JS中的異步終極解決方案。
async 是一個通過異步執(zhí)行并隱式返回 Promise 作為結(jié)果的函數(shù)流纹。 async 函數(shù)內(nèi)部所有await 的promise對象執(zhí)行完糜烹,返回的promise對象才會發(fā)生狀態(tài)改變,除非遇到return語句或者拋出錯誤漱凝。
當(dāng) async
函數(shù)執(zhí)行的時候疮蹦,一旦遇到 await
相當(dāng)于執(zhí)行 Promise.resolve()
,不論 await
關(guān)鍵字后面返回的是不是promise茸炒, resolve()
任務(wù)進入微任務(wù)隊列愕乎,JS 引擎將暫停當(dāng)前協(xié)程的運行,把線程的執(zhí)行權(quán)交給 父協(xié)程
壁公,父協(xié)程對 await 返回的promise調(diào)用then 來監(jiān)聽異步操作的狀態(tài)改變感论,然后繼續(xù)往下執(zhí)行。
在 for...each...
中使用 async / await 并不能保證異步的有序執(zhí)行紊册, for...of...
可以比肄,因為采用的是迭代器遍歷。
對象
深拷貝和淺拷貝
- 淺拷貝
淺拷貝囊陡,拷貝的只是對象的引用薪前,即內(nèi)存地址。 如果屬性是對象关斜,淺拷貝都是引用。
解構(gòu)賦值铺浇、Object.assign()都是淺拷貝痢畜。
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">/**
@description shallow copy
@param {Object} obj
@returns {Object}
*/
const copy = (obj) => {
if (obj === null || typeof obj !== 'object') return obj;
let newObj = new obj.constructor; // 可能是數(shù)組
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = obj[key];
}
}
return newObj;
}
// 對象
let newObj = {...obj};
let newObj = Object.assign({}, obj);
// 數(shù)組
let newArr = [...arr];
復(fù)制代碼</pre>深拷貝
遍歷對象中的每一個屬性,拷貝值的副本鳍侣。 JSON.parse(JSON.stringify(obj)) 能覆蓋大部分的情況丁稀,但存在以下問題:
- 無法解決循環(huán)引用,會無限遞歸倚聚,深拷貝的解決方案是用Map標(biāo)記已經(jīng)拷貝過的對象线衫。
- 無法處理特殊對象,如RegExp, Date, Set, Map惑折,深拷貝的解決方案是用構(gòu)造函數(shù)授账。
- 無法拷貝函數(shù)
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">/**
- @description deep copy
- @param {Object} obj
- @returns {Object}
*/
const deepCopy = (obj, map = new Map()) => {
if (map.has(obj)) return obj;
if (obj === null || typeof obj !== 'object') return obj;
let newObj = new obj.constructor;
map.set(obj, true);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key], map) : obj[key];
}
}
return newObj;
}
let obj = {a: 1, b: 2};
obj.c = obj;
// let obj = new Date();
let newObj = deepCopy(obj);
console.log(newObj, Object.getPrototypeOf(newObj));
復(fù)制代碼</pre>
新建
- {}字面創(chuàng)建
- new Object()
回顧new命令的創(chuàng)建過程枯跑,會發(fā)現(xiàn)字面量創(chuàng)建更高效一些,少了 __proto__
指向賦值和 this
綁定的操作白热。
- Object.create(proto[, propertiesObject])
使用現(xiàn)有對象的原型 proto
對象及其屬性 propertiesObject
去創(chuàng)建一個新的對象敛助; 如果proto參數(shù)是 null
,那新對象就是個空對象屋确,沒有繼承 Object.prototype
上的任何屬性和方法纳击,如 hasOwnProperty()、toString()
等攻臀。
采用繼承(原型繼承焕数、構(gòu)造函數(shù)繼承、組合繼承刨啸、寄生繼承堡赔、寄生組合繼承)的方式創(chuàng)建,本質(zhì)上也是Object.create()
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">Object.myCreate = function(proto, properties) {
function F() {};
F.prototype = proto;
let newObj = new F();
if (properties) {
Object.defineProperties(newObj, properties);
}
return newObj;
}
let myNewObj = Object.myCreate({a: 1}, {b: {value: 2}}); // F {b: 2}
console.log(newObj.proto); // {a: 1}
let newObj = Object.myCreate({a: 1}, {b: {value: 2}}); // {b: 2}
復(fù)制代碼</pre>
凍結(jié)
- Object.preventExtensions()
禁止修改原型呜投,禁止添加屬性
- Object.feeeze()
相當(dāng)于執(zhí)行了Object.preventExtensions() 禁止添加屬性加匈,configurable: false禁止刪除屬性,writable: false 禁止修改屬性仑荐。
但是只凍結(jié)一層雕拼,如果屬性是對象,該對象屬性的屬性可以修改粘招,徹底凍結(jié)需要遞歸啥寇;
- Object.seal()
與Object.feeeze()不同的是,writable: true洒扎,可修改屬性值辑甜。
遍歷
- for...in...
遍歷對象的可枚舉屬性(symbol屬性不可枚舉),會獲取到原型鏈上的屬性袍冷,用hasOwnProperty過濾磷醋。
- Object.keys()
只返回對象可枚舉的屬性,不會獲取到原型鏈上的屬性胡诗,會獲取到原型方法邓线,在ES6類內(nèi)部定義的方法是不可枚舉的。 Object.getOwnPropertyNames() 方法可以返回不可枚舉的屬性名煌恢。
- for...of...
可迭代數(shù)據(jù)類型:原生具有[Symbol.iterator]屬性數(shù)據(jù)類型為可迭代數(shù)據(jù)類型骇陈。 可遍歷所有具備 Iterator 接口的數(shù)據(jù)結(jié)構(gòu),原生具備 Iterator 接口的數(shù)據(jù)結(jié)構(gòu)如下:
Array
Map
Set
String
TypedArray
類數(shù)組對象
forEach
遍歷Array, Map, Set瑰抵,但不能中斷你雌,不能return。
對于異步代碼二汛,即使用 async / await婿崭,也不能保證異步的順序執(zhí)行拨拓。
JSON
JSON 是一種基于文本的輕量級的數(shù)據(jù)交換格式。它可以被任何的編程語言讀取和作為數(shù)據(jù)格式來傳遞逛球。 JSON語法基于JS千元,但不同于JS里的對象,更為嚴格颤绕。
- JSON.stringify()
遇到undefined和函數(shù)的時候都會跳過
- JSON.parse()
數(shù)組
類數(shù)組對象
具有l(wèi)ength屬性幸海、可以通過下標(biāo)訪問的對象,即可以被迭代奥务,但不具有數(shù)組的方法物独。如arguments和DOM collections。 轉(zhuǎn)換為數(shù)組的方法:
- Array.from(arrayLike)
- [...arrayLike]
- Array.prototype.slice.call(arrayLike)
- Array.prototype.splice.call(arrayLike, 0)
注意氯葬,String沒有Splice方法
- Array.prototype.concat.apply([], arrayLike)
查找元素
- arr.indexOf(val)
- arr.includes(val)
- arr.find(val)
- arr.findIndex(val)
類型判斷
- instaceof
- 構(gòu)造函數(shù)檢查
- 原型檢查Object.getPrototypeOf()
- Object.prototype.toString.call()
- Array.isArray()
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">let arr = [1, 2, [3,4]];
let obj = {};
console.log(arr instanceof Array, obj instanceof Array);
console.log(arr.constructor === Array, obj.constructor === Array);
console.log(Object.getPrototypeOf(arr) === Array.prototype, Object.getPrototypeOf(obj) === Array.prototype);
console.log(Object.prototype.toString.call(arr) === '[object Array]', Object.prototype.toString.call(obj) === '[object Array]');
console.log(Array.isArray(arr), Array.isArray(obj));
復(fù)制代碼</pre>
元素去重
- Set去重
- 考慮元素是對象的情況挡篓,保持原先的先后順序
利用Map鍵值唯一的性質(zhì)
- 保留較大的value
先按value排序
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">let arr = [{key: 'fe', value: 19}, {key: 'ml', value: 20}, {key: 'fe', value: 17}];
const unique = (arr, key, value) => {
arr.sort((a, b) => a[value] - b[value]);
return [...new Map(arr.map(item => [item[key], item])).values()];
}
console.log(unique(arr, 'key', 'value'));
復(fù)制代碼</pre>
數(shù)組拍平
- flat()方法
- 字符串轉(zhuǎn)換
<pre class="hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">toString() join()
</pre>
- reduce() 遞歸
- Array.prototype.some()
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">let arr = [1,2,[2,3,4],[2,3,[4,5]],0,6,4];
let res = myFlat(arr);
console.log(res, res.length);
function myFlat(arr) {
let res = [];
// res = arr.flat(Infinity);
// res = arr.join().split(",").map(Number);
// res = arr.toString().split(",").map(Number);
// res = arr.reduce((prev, curr) => {
// return prev.concat(Array.isArray(curr) ? myFlat(curr) : curr)
// }, [])
// return res;
while (arr.some(Array.isArray)) {
arr = [].concat(...arr);
}
return arr;
}
復(fù)制代碼</pre>
數(shù)組排序
V8引擎里的sort()函數(shù),假設(shè)數(shù)組長度為n 當(dāng) n <= 10 時帚称,采用 插入排序 當(dāng) n > 10 時官研,采用 三路快速排序
10 < n <= 1000, 采用中位數(shù)作為哨兵元素
n > 1000, 每隔 200~215 個元素挑出一個元素,放到一個新數(shù)組闯睹,然后對它排序戏羽,找到中間位置的數(shù),以此作為中位數(shù)
冒泡排序
兩兩比較相鄰元素楼吃,第i趟遍歷使得當(dāng)前最大的元素冒泡到第n-i個位置始花。
- 插入排序
每趟遍歷把當(dāng)前元素插入到已經(jīng)有序的子序列里。子序列中比當(dāng)前元素大的元素孩锡,依次往后移動騰位置酷宵。
- 選擇排序(不穩(wěn)定排序)
第i躺遍歷,在未排序序列中找出最小的元素躬窜,與第i個元素交換位置浇垦。
- 堆排序(不穩(wěn)定排序)
選擇排序的升級,將未排序序列的末尾元素荣挨,與堆頂元素交換位置溜族,再調(diào)整堆。
堆是一種特殊的二叉樹垦沉,用數(shù)組存儲,a[i]的子元素是a[2 i]和a[2 i+1]仍劈,父元素是a[i/2]厕倍。
堆化,n/2到n的元素是葉子節(jié)點贩疙,不需要堆化讹弯。
插入堆况既,未排序序列從后往前插入堆,從上往下堆化组民。
堆排序數(shù)據(jù)訪問的方式?jīng)]有快速排序友好棒仍。
在排序過程中,堆排序算法的數(shù)據(jù)交換次數(shù)要多于快速排序臭胜。
- 歸并排序
把長度為N的序列看作N個長度為1的子序列莫其,對相鄰子序列兩兩合并,直到得到長度為N的序列耸三。
需要額外的存儲空間乱陡。
- 快速排序(不穩(wěn)定排序)
冒泡排序的升級,每躺排序選擇一個分割點仪壮,把待排序數(shù)組分割為兩部分憨颠,其中一部分的值小于另一部分,再分別對這兩部分遞歸排序积锅。
分割點選的不合理爽彤,最壞情況下時間復(fù)雜度是 O(n^2)。
理想的分割點缚陷,應(yīng)該使兩部分的元素數(shù)量差不多适篙。因此再實際應(yīng)用中常選取中點, 衍生出三數(shù)蹬跃、五數(shù)取中等匙瘪。
詳見數(shù)據(jù)結(jié)構(gòu)。
組合數(shù)組
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">let list = [['熱', '冷', '冰'], ['大', '中', '小'], ['重辣', '微辣'], ['重麻', '微麻']];
let options = compose(list);
console.log(options, options.length);
// 輸出所有的維度組合
function compose(arr) {
let res = arr.reduce((result, items) => {
return items.reduce((prev, curr) => prev.concat(
result.map(group => [].concat(group, curr))
), []);
});
return res.map(item => item.join("+"));
}
復(fù)制代碼</pre>
面向?qū)ο?/h2>
構(gòu)造函數(shù)
- 函數(shù)內(nèi)部使用this關(guān)鍵字蝶缀,代表所要生成的對象實例丹喻;
- 生成對象必須使用new命令;
- 常見的構(gòu)造函數(shù)翁都,ES6規(guī)范不建議用new來創(chuàng)建基本數(shù)據(jù)類型的包裝類碍论。
<pre class="prettyprint hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">Boolean() Number() String() Array() Date() Function() RegExp() Error() Object()
</pre>
new命令
- 創(chuàng)建一個空對象;
- 新對象的原型
__proto__
指向構(gòu)造函數(shù)的prototype
屬性柄慰; - 綁定
this
和新對象鳍悠; - 執(zhí)行構(gòu)造函數(shù)內(nèi)部的代碼;
- 返回新對象
<pre class="prettyprint hljs kotlin" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">/**
- @description new命令
- 創(chuàng)建一個新的空對象
- 將新對象的原型指向構(gòu)造函數(shù)的prototypr屬性
- 綁定新對象到構(gòu)造函數(shù)的this坐搔,并傳遞參數(shù)
- @param {Function} constructor
- @param {...any} args
- @returns {Object}
*/
function createNew(constructor, ...args) {
if (typeof constructor !== 'function') {
throw 'not a function';
}
// let obj = {};
// Object.setPrototypeOf(obj, constructor.prototype);
let obj = Object.create(constructor.prototype);
let result = constructor.apply(obj, ...args);
return result instanceof Object ? result : obj;
}
復(fù)制代碼</pre>
this關(guān)鍵字
概念: this
就是函數(shù)運行時所在的對象(即環(huán)境)藏研。由于JS支持環(huán)境動態(tài)切換,this的指向是動態(tài)的概行。this的設(shè)計目的就是在函數(shù)體內(nèi)部蠢挡,指向函數(shù)當(dāng)前的運行環(huán)境。
- 全局上下文
默認this指向window, 嚴格模式下指向undefined。
- 函數(shù)調(diào)用
當(dāng)一個函數(shù)不是一個對象的屬性時业踏,直接作為函數(shù)調(diào)用禽炬,this指向全局對象window。
- 對象的方法調(diào)用
當(dāng)一個函數(shù)作為一個對象的方法來調(diào)用時勤家,this指向該對象腹尖。
- 構(gòu)造調(diào)函數(shù)用
當(dāng)一個函數(shù)用new命令調(diào)用時,函數(shù)執(zhí)行時會創(chuàng)建一個新對象伐脖,this指向所要生成的實例對象热幔。
- apply / call / bind 綁定
- 箭頭函數(shù)
定義之后,this指向不可改變晓殊。
- DOM事件綁定
onclik, addEventListener 默認指向綁定事件的元素断凶。
call / apply / bind
三個函數(shù)的作用都是將函數(shù)綁定到上下文中,用來切換/固定函數(shù)中this的指向(不能用于箭頭函數(shù))巫俺,常用于借用函數(shù)(類數(shù)組借用數(shù)組實例方法)认烁,構(gòu)造函數(shù)的繼承。
-
call
方法接受的是若干個參數(shù)介汹。 -
apply
接收的是一個包含多個參數(shù)的數(shù)組却嗡,apply
在運行前要對作為參數(shù)的數(shù)組進行一系列檢驗和深拷貝,所以會比call
慢嘹承,但非常適合返回數(shù)組的一類操作窗价。 -
bind
方法用于將函數(shù)體內(nèi)的this
綁定到某個對象,然后返回一個新函數(shù)叹卷。
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">/**
- @description fun.call(thisArg, arg1, arg2...)
- 判斷調(diào)用對象是否為函數(shù)
- 傳入的上下文對象如果不存在撼港,則默認為全局對象window
- 將函數(shù)設(shè)為上下文對象的方法
- 傳入給定參數(shù),并通過上下文對象調(diào)用執(zhí)行函數(shù)
- 刪除剛才新增的屬性
- 返回結(jié)果
- @param {Object} context
*/
Function.prototype.myCall = function () {
if (typeof this !== 'function') {
throw new TypeError('not a function');
}
let context = arguments[0] || window; // 在Node中沒有全局對象window
let fn = Symbol('fn');
context.fn = this;
let args = [...arguments].slice(1); // 獲取剩余參數(shù)
console.log('myCall', context, args);
let result = context.fn(...args);
delete context.fn;
return result;
}
/**
- @description fun.apply(thisArg, [arg1, arg2...])
- @param {Object} context
*/
Function.prototype.myApply = function() {
if (typeof this !== 'function') {
return new TypeError('not a function'); // 在Node中沒有全局對象window
}
let context = arguments[0] || window;
let fn = Symbol('fn');
context.fn = this;
let result;
if (arguments[1]) {
result = context.fn(...arguments[1]);
} else {
result = context.fn()
}
delete context.fn;
return result;
}
/**
- @description fn.bind(thisArg, arg1, arg2...)()
- 判斷調(diào)用對象是否為函數(shù)
- 保存當(dāng)前函數(shù)的引用骤竹,獲取其余傳入的參數(shù)值
- 創(chuàng)建一個函數(shù)返回
- 內(nèi)部使用apply來綁定函數(shù)調(diào)用
- 需要判斷函數(shù)作為構(gòu)造函數(shù)的情況(傳入當(dāng)前函數(shù)的this)帝牡,其余情況都傳入指定的上下文對象
- @param {Object} context
- @returns {Function}
*/
Function.prototype.myBind = function () {
if (typeof this !== 'function') {
return new TypeError('not a function');
}
let context = arguments[0] || window; // 在Node中沒有全局對象window
let args = [...arguments].slice(1);
let self = this;
console.log('myBind', context, args, self);
return function fn() {
return self.apply(
this instanceof fn ? this : context,
args.concat(...arguments)
);
}
}
let base = new Number(0);
let arr = [1, 2, 3];
// let res1 = Math.max.call(base, arr[0], arr[1], arr[2]);
// let res2 = Math.max.apply(base, arr);
// let res3 = Math.max.apply(base);
// let myMax = Math.max.bind(base, arr[0]);
// let res4 = myMax(arr[1], arr[2]);
let res1 = Math.max.myCall(base, arr[0], arr[1], arr[2]);
let res2 = Math.max.myApply(base, arr);
let res3 = Math.max.myApply(base);
let myMax = Math.max.myBind(base, arr[0]);
let res4 = myMax(arr[1], arr[2]);
console.log(res1, res2, res3, res4);
復(fù)制代碼</pre>
原型鏈
- 使用構(gòu)造函數(shù)創(chuàng)建對象,在對象內(nèi)部包含一個指針蒙揣,這個指針指向構(gòu)造函數(shù)的prototype屬性靶溜,稱為對象的原型;
- 每個構(gòu)造函數(shù)內(nèi)部都有一個prototype屬性懒震,prototype也是一個對象罩息,這個對象包含了可以由該構(gòu)造函數(shù)所有實例共享的屬性和方法;
- 當(dāng)我們訪問一個對象的屬性个扰,如果對象內(nèi)部不存在這個屬性瓷炮,就回去原型對象里尋找對應(yīng)的屬性,原型對象又有自己的原型递宅,也就是原型鏈的概念娘香,原型鏈的盡頭是null宛琅;
Object.prototype.hasOwnProperty()
函數(shù)压恒,在執(zhí)行對象查找時,永遠不會去查找原型弃酌。
獲取原型的方法
<pre class="hljs elm" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">p. proto
p.constructor.prototype
Object.getPrototypeOf(p)
</pre>
繼承
ES5繼承
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// 父類
function Animal(name, colors) {
this.name = name || 'Jack'
this.colors = colors || ['white']
this.makeSound = function(animal) {
animal.sound();
}
}
Animal.prototype.eat = function (food) {
console.log(this.name + ' is eating ' + food)
}
復(fù)制代碼</pre>
原型繼承
<pre class="hljs nginx" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">prototype
</pre>
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">//子類
function Cat(name) {
this.name = name || 'Tom'
}
Cat.prototype = new Animal();
let cat = new Cat()
cat.colors.push('yellow') // 繼承父類的colors屬性
cat.eat('fish') // 繼承父類的eat方法
console.log(cat instanceof Animal) // true
console.log(cat instanceof Cat) // true
let cat_1 = new Cat()
console.log(cat_1.colors) // ['white', 'yellow'] 子類的多個實例將共享父類的屬性
復(fù)制代碼</pre>
call繼承(構(gòu)造函數(shù))
<pre class="hljs kotlin" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">parent.call(this)
</pre>
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">function Dog(name) {
Animal.call(this) // 動態(tài)綁定this
this.name = name || 'Bob'
}
let dog = new Dog()
dog.colors.push('black') // 繼承父類的colors屬性
dog.eat('bone') // Uncaught TypeError: dog.eat is not a function
console.log(dog.colors)
console.log(dog instanceof Animal) // false
console.log(dog instanceof Dog) // true
let dog_1 = new Dog()
console.log(dog_1.colors) // ['white']
復(fù)制代碼</pre>
組合繼承
- 使用構(gòu)造函數(shù)繼承
parent.call(this)
土榴,可以繼承父類實例屬性和方法诀姚;使用原型繼承child.prototype = new parent()
可以繼承父類原型屬性和方法; - 缺點:調(diào)用了兩次父類構(gòu)造函數(shù)玷禽,生成了兩份實例赫段。
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">function Mouse(name) {
Animal.call(this)
this.name = name || 'Jerry'
}
Mouse.prototype = new Animal()
Mouse.prototype.constructor = Mouse
let mouse = new Mouse()
mouse.colors.push('gray') // 繼承父類的colors屬性
mouse.eat('rice') // 繼承父類的原型方法 eat
console.log(mouse instanceof Animal) // true
console.log(mouse instanceof Mouse) // true
let mouse_1 = new Mouse()
console.log(mouse_1.colors) // ['white']
復(fù)制代碼</pre>
寄生繼承
- 依托于 一個對象 而生的一種繼承方式,
Object.create()
矢赁。 - 實際生產(chǎn)中糯笙,繼承一個單例對象的場景很少。
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">let animal = {
name: 'Jack',
colors: ['white'],
sleep: function() {
console.log(this.name + ' is singing')
}
}
function Bird(obj, name) {
let o = Object.create(obj)
return o
}
let bird = new Bird(animal);
bird.colors.push('red') // 繼承父類的colors屬性
bird.sleep() // 繼承父類的實例方法 sleep
console.log(bird.colors)
復(fù)制代碼</pre>
寄生組合式繼承
- 使用構(gòu)造函數(shù)繼承撩银,可以繼承父類實例屬性和方法
parent.call(this)
给涕,使用寄生Object.create(parent.prototype)
和原型繼承child.prototype = parent.prototype
,可以繼承父類原型屬性和方法额获,同時子類的多個實例不會共享父類的屬性够庙。 - 子類可以傳遞動態(tài)參數(shù)給父類,父類的構(gòu)造函數(shù)只執(zhí)行了一次抄邀。
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">function inherit(child, parent) {
// 繼承父類的原型耘眨,合并覆蓋
// Object.setPrototypeOf(B.prototype, A.prototype);
child.prototype = Object.assign(Object.create(parent.prototype), child.prototype)
// 重寫被污染的子類的構(gòu)造函數(shù)
child.prototype.constructor = child
}
function Pokemon(name) {
Animal.call(this)
this.name = name || 'pika'
}
inherit(Pokemon, Animal)
Pokemon.prototype.sound = function() {
console.log('pika pika');
}
let pokemon = new Pokemon()
pokemon.colors.push('yellow')
console.log(pokemon.colors) // 繼承父類的colors屬性
pokemon.makeSound(pokemon) // 繼承并改寫父類的實例方法 makeSound
pokemon.eat('unknow') // 繼承父類的原型方法 eat
console.log(pokemon instanceof Animal) // true
console.log(pokemon instanceof Pokemon) // true
let pokemon_1 = new Pokemon()
console.log(pokemon_1.colors) // ['white']
復(fù)制代碼</pre>
ES6繼承
<pre class="hljs kotlin" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">parent.apply(this)
super()
</pre>
<pre class="prettyprint hljs scala" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // 調(diào)用父類的構(gòu)造函數(shù),否則得不到this對象境肾,相當(dāng)于Point.prototype.constructor.call(this);
this.color = color;
}
toString() {
return this.color + super.toString(); // 調(diào)用父類的toString()
}
}
Object.getPrototypeOf(ColorPoint) === Point; // true
ColorPoint.proto === Point; // true
ColorPoint.prototype.proto === Point.prototype; // true
// B 的實例繼承 A 的實例
Object.setPrototypeOf(B.prototype, A.prototype);
// B 繼承 A 的靜態(tài)屬性
Object.setPrototypeOf(B, A);
復(fù)制代碼</pre>
use strict
- 全局變量顯示聲明
- 禁止刪除變量
- 靜態(tài)綁定
- 增強的安全措施
禁止this關(guān)鍵字指向全局對象 禁止在函數(shù)內(nèi)部遍歷調(diào)用棧
- 顯示報錯
- 重名錯誤
- arguments對象的限制
- 函數(shù)必須聲明在頂層
DOM操作
DOM 是 JavaScript 操作網(wǎng)頁的接口剔难,全稱為“文檔對象模型”(Document Object Model)。它的作用是將網(wǎng)頁轉(zhuǎn)為一個 JavaScript 對象奥喻,從而可以用腳本進行各種操作(比如增刪內(nèi)容)偶宫。
操作方法
- 增
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">let newElem = document.createElement("span");
parentElem.appendChild(newElem);
parentElem.insertBefore(newElem, parentElem.lastChild());
復(fù)制代碼</pre>
- 刪
<pre class="hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">parentElem.removeChild(oldNode);
復(fù)制代碼</pre>
- 改
<pre class="prettyprint hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">parentElem.replaceChild(newNode, oldNode);
element.setAttribute(key, value);
復(fù)制代碼</pre>
- 查
<pre class="prettyprint hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">document.getElementsByName("myname"); // array-like
document.getElementsByTagName("span"); // array-like
document.getElementsByClassName("myclass"); // array-like
document.getElementById("myid"); // one
element.parentNode();
element.previousSibling();
element.nextSibling();
parentElem.attributes();
parentElem.childNodes(); // array-like
parentElem.firstChild();
parentElem.lastChild();
復(fù)制代碼</pre>
Mutation Observer API
Mutation Observer API用來監(jiān)視DOM變動,比如節(jié)點的刪減衫嵌、屬性的變動读宙、文本內(nèi)容的變動等。
- 異步觸發(fā)楔绞,要等到所有DOM操作都結(jié)束才觸發(fā)结闸,為了應(yīng)對DOM頻繁變更。
- 把DOM變動記錄封裝成數(shù)組進行處理酒朵,而不是逐條個別處理桦锄。
- 既可以觀察DOM的所有類型變動,也可以指定只觀察某一類變動蔫耽。
<pre class="prettyprint hljs dart" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// 構(gòu)造函數(shù)
const observer = new MutationObserver((mutations, observer) => {
callback();
});
// 實例方法
// 所要觀察的DOM節(jié)點结耀,和所要觀察的特定變動
const domNode = document.querySelector('article');
const options = {
attributes: true,
characterData: true,
childList: true,
subtree: true, // 是否將觀察者應(yīng)用于該節(jié)點的后代所有節(jié)點
attributeOldValue: true, //表示觀察attributes變動時留夜,是否需要記錄變動前的屬性值
characterDataOldValue: true,
attributeFilter: ['class','src'], // 數(shù)組,表示需要觀察的特定屬性
};
// 開始觀察
observer.observe(domNode, options);
// 停止觀察图甜。調(diào)用該方法后碍粥,DOM 再發(fā)生變動,也不會觸發(fā)觀察器
observer.disconnect();
// 清除變動記錄黑毅,即不再處理未處理的變動嚼摩。該方法返回變動記錄的數(shù)組。
observer.takeRecords();
復(fù)制代碼</pre>
事件
EventTarget
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">// addEventListener(type, listener[, useCapture]) useCapture默認為false, 只在冒泡階段被觸發(fā)
// target.addEventListener(type, listener[, options]); once只觸發(fā)一次; passive使preventDefault()失效
function hello() {
console.log('Hello world');
}
const button = document.getElementById('btn');
button.addEventListener('click', hello, false);
// removeEventListener() 移除事件監(jiān)聽, 參數(shù)必須與addEventListener完全一致
const event = new Event('click');
// dispatchEvent() 觸發(fā)事件
button.dispatchEvent(event);
復(fù)制代碼</pre>
事件模型
事件驅(qū)動編程矿瘦,通過監(jiān)聽函數(shù)對事件作出反應(yīng)枕面。
事件傳播
- DOM0事件模型
- IE事件模型
- DOM2事件模型
事件捕獲(capture phase):事件開始由不太具體的節(jié)點接收,從windows對象向下傳播到目標(biāo)節(jié)點缚去。 目標(biāo)階段(target phase): 在目標(biāo)節(jié)點上觸發(fā)潮秘。 事件冒泡(bubbling phase):從目標(biāo)節(jié)點逐級向上傳播到較為不具體的節(jié)點或文檔, div -> body -> html -> document -> window
事件代理(委托)
事件的代理(delegation):由于事件在冒泡階段會向上傳播到父節(jié)點易结,因此可以把子節(jié)點的監(jiān)聽函數(shù)定義在父節(jié)點上枕荞,由父節(jié)點的監(jiān)聽函數(shù)統(tǒng)一處理,可以:
- 提高事件的處理速度衬衬,減少內(nèi)存的占用买猖,比如列表的事件綁定。
- 不需要因為元素改動而修改事件綁定滋尉。
eventTarget玉控,事件的原始觸發(fā)節(jié)點。 currentEventTarget狮惜,事件當(dāng)前所在的節(jié)點高诺。
<pre class="prettyprint hljs php" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">ul.addEventListener('click', function(e){
if(e.target.tagName.toLowerCase() === 'li'){
fn() // 執(zhí)行某個函數(shù)
}
})
復(fù)制代碼</pre>
編譯原理
高級程序設(shè)計語言 =》匯編語言 =》機器語言
- 靜態(tài)編譯
簡稱AOT(Ahead of time,提前編譯)碾篡,通常為靜態(tài)類型語言虱而,在編譯時就能提前發(fā)現(xiàn)錯誤。 以Angular為例开泽,AOT不需要在客戶端導(dǎo)入體積龐大的編譯器牡拇。
- 動態(tài)解釋
簡稱JIT(Just in time,即時編譯)穆律,通常為動態(tài)類型語言惠呼,沒有類型判斷。
AOT | JIT | |
---|---|---|
編譯平臺 | Server | Browser |
編譯時機 | Build | Runtime |
包大小 | Small | Large |
執(zhí)行性能 | Better | |
啟動時間 | Quicker |
現(xiàn)代編譯器的主要工作流程:
源代碼 source code =》 預(yù)處理器 preprocessor =》編譯器 compiler =》 匯編程序 assembler =》目標(biāo)代碼 object code =》鏈接器 linker =》可執(zhí)行文件 executables
核心階段:
- 解析 Parsing
詞法分析Tokenizer 和語法分析峦耘,將原始代碼字符串解析成抽象語法樹(Abstract Syntax Tree, AST)剔蹋;
- 轉(zhuǎn)換 Transformation
利用遍歷器,對AST做轉(zhuǎn)換處理操作辅髓;
- 生成代碼 Code Generation
將轉(zhuǎn)換之后的AST對象生成目標(biāo)語言代碼字符串泣崩;
<pre class="prettyprint hljs verilog" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">/**
- @params {String} input
- @returns {String}
*/
function compiler(input) {
let tokens = tokenizer(input);
let ast = parser(tokens); // 括號匹配少梁,棧
let newAst = transformer(ast);
let output = codeGenerator(newAst);
return output;
}
復(fù)制代碼</pre>
有想了解更多的朋友可以加Q群
一、搜索QQ群矫付,前端學(xué)習(xí)交流群:1093606290