前端不完全面試指北--js

目前 JavaScript 仍是前端開發(fā)的靈魂抛计,各種層出不窮的框架其實都是與底層相關(guān)。

開始之前跨释,借前端三元同學(xué)的靈魂發(fā)問自測一下掌握了多少:

原生JS靈魂之問, 請問你能接得住幾個胸私?(上)

原生JS靈魂之問(中),檢驗自己是否真的熟悉JavaScript鳖谈?

原生JS靈魂之問(下), 沖刺進階最后一公里

數(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)換為同一類型再進行比較芽唇;

  1. 兩者類型為null / undefined:true
  2. 一者類型為null / undefined:false
  3. 兩者類型為string 和 number:將string轉(zhuǎn)為 number
  4. 一者類型為boolean:將 boolean 轉(zhuǎn)為 number
  5. 一者類型為object,其另一者類型為string, number 或 symbol取劫,將 object 轉(zhuǎn)為原始類型匆笤。
  6. 兩者都為引用類型(對象、數(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

  1. 右邊:運算符的優(yōu)先級更高璧函,![] = !true = false;boolean需要轉(zhuǎn)換為number基显,false = 0蘸吓。
  2. 左邊,此時一方為object且另一方為number撩幽,將object轉(zhuǎn)換為原始類型库继,[] = ''箩艺;此時兩者類型為string和number,將string轉(zhuǎn)換為number, ''=0宪萄。

對象轉(zhuǎn)原始類型

對象轉(zhuǎn)原始類型艺谆,會調(diào)用內(nèi)置的[ToPrimitive]函數(shù),對于該函數(shù)而言拜英,其邏輯如下:

  1. 如果Symbol.toPrimitive()方法静汤,優(yōu)先調(diào)用再返回
  2. 調(diào)用valueOf(),如果轉(zhuǎn)換為原始類型居凶,則返回
  3. 調(diào)用toString()虫给,如果轉(zhuǎn)換為原始類型,則返回
  4. 如果都沒有返回原始類型侠碧,會報錯

<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)部, 11.0 是是同一個數(shù)丙笋,存在2個 0 :一個是 +0 谢澈,一個是 -0 煌贴,區(qū)別就是64位浮點數(shù)表示法的符號位不同。唯一的區(qū)別在于锥忿, +0-0 當(dāng)作分母牛郑,返回的值是不相等的。

JS中浮點數(shù)精度問題

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ù)值球及。
  • 八進制:有前綴 0o0O 的數(shù)值氧骤,或者有前導(dǎo)0、且只用到0-7的八個阿拉伯?dāng)?shù)字的數(shù)值吃引。
  • 十六進制:有前綴 0x0X 的數(shù)值筹陵。
  • 二進制:有前綴 0b0B 的數(shù)值。

在安全整數(shù)范圍內(nèi)镊尺,可通過 parseInt() 方法進行進制轉(zhuǎn)換朦佩。

JavaScript 提供 Number 對象的 MAX_VALUEMIN_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é)流

image

函數(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

  1. 運行時加載(require)。
  2. 單值導(dǎo)出(加載一個模塊就是加載對應(yīng)的一個文件晴音,一個模塊被多次加載但只執(zhí)行一次柔纵,放在緩存中)。
  3. 模塊輸出的是值拷貝(基本數(shù)據(jù)類型:值復(fù)制段多,引用數(shù)據(jù)類型:淺拷貝)首量。
  4. 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()
  1. 參數(shù)為一個promise,直接返回一個promise對象赋朦。
  2. 參數(shù)為一個thenable對象篓冲,返回的promise會跟把這個對象的狀態(tài)作為自己的狀態(tài)李破。
  3. 參數(shù)為一個定值,返回以該值為valude的成功狀態(tài)promise壹将。
  • Promise.reject()

參數(shù)作為reason嗤攻,返回一個帶有失敗原因的Promise對象。

  • Promise.prototype.finally()
  • Promise.all()
  1. 參數(shù)為空的可迭代對象诽俯,直接進行resolve()妇菱。
  2. 參數(shù)中所有promise的狀態(tài)都變成resolved,將所有的返回值以數(shù)組形式傳給回調(diào)函數(shù)惊畏,執(zhí)行resolve()恶耽,返回的promise對象成功。
  3. 參數(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í)行流程:

  1. 調(diào)用生成器函數(shù)后溉苛,程序阻塞镜廉,不會執(zhí)行任何語句;
  2. 調(diào)用next()方法后愚战,程序繼續(xù)執(zhí)行娇唯,直到遇到y(tǒng)ield關(guān)鍵字暫停齐遵;
  3. 暫停后,返回一個包含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>

新建

  1. {}字面創(chuàng)建
  2. new Object()

回顧new命令的創(chuàng)建過程枯跑,會發(fā)現(xiàn)字面量創(chuàng)建更高效一些,少了 __proto__ 指向賦值和 this綁定的操作白热。

  1. 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ù)組的方法:

  1. Array.from(arrayLike)
  2. [...arrayLike]
  3. Array.prototype.slice.call(arrayLike)
  4. Array.prototype.splice.call(arrayLike, 0)

注意氯葬,String沒有Splice方法

  1. 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命令

  1. 創(chuàng)建一個空對象;
  2. 新對象的原型 __proto__ 指向構(gòu)造函數(shù)的 prototype 屬性柄慰;
  3. 綁定 this 和新對象鳍悠;
  4. 執(zhí)行構(gòu)造函數(shù)內(nèi)部的代碼;
  5. 返回新對象

<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)境。

  1. 全局上下文

默認this指向window, 嚴格模式下指向undefined。

  1. 函數(shù)調(diào)用

當(dāng)一個函數(shù)不是一個對象的屬性時业踏,直接作為函數(shù)調(diào)用禽炬,this指向全局對象window。

  1. 對象的方法調(diào)用

當(dāng)一個函數(shù)作為一個對象的方法來調(diào)用時勤家,this指向該對象腹尖。

  1. 構(gòu)造調(diào)函數(shù)用

當(dāng)一個函數(shù)用new命令調(diào)用時,函數(shù)執(zhí)行時會創(chuàng)建一個新對象伐脖,this指向所要生成的實例對象热幔。

  1. apply / call / bind 綁定
  2. 箭頭函數(shù)

定義之后,this指向不可改變晓殊。

  1. 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>

原型鏈

  1. 使用構(gòu)造函數(shù)創(chuàng)建對象,在對象內(nèi)部包含一個指針蒙揣,這個指針指向構(gòu)造函數(shù)的prototype屬性靶溜,稱為對象的原型;
  2. 每個構(gòu)造函數(shù)內(nèi)部都有一個prototype屬性懒震,prototype也是一個對象罩息,這個對象包含了可以由該構(gòu)造函數(shù)所有實例共享的屬性和方法;
  3. 當(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)容的變動等。

  1. 異步觸發(fā)楔绞,要等到所有DOM操作都結(jié)束才觸發(fā)结闸,為了應(yīng)對DOM頻繁變更。
  2. 把DOM變動記錄封裝成數(shù)組進行處理酒朵,而不是逐條個別處理桦锄。
  3. 既可以觀察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

二凯沪、https://jq.qq.com/?_wv=1027&k=MlDBtuEG

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市买优,隨后出現(xiàn)的幾起案子著洼,更是在濱河造成了極大的恐慌,老刑警劉巖而叼,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異豹悬,居然都是意外死亡葵陵,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門瞻佛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來脱篙,“玉大人,你說我怎么就攤上這事伤柄∈实叮” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么斤葱? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮芹血,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己蝎宇,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布熊榛。 她就那樣靜靜地躺著玄坦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪豺总。 梳的紋絲不亂的頭發(fā)上择懂,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天喻喳,我揣著相機與錄音困曙,去河邊找鬼纲熏。 笑死勺拣,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的鱼填。 我是一名探鬼主播宣脉,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼剔氏!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起竹祷,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤谈跛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后塑陵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體感憾,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年令花,在試婚紗的時候發(fā)現(xiàn)自己被綠了阻桅。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡兼都,死狀恐怖嫂沉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情扮碧,我是刑警寧澤趟章,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站慎王,受9級特大地震影響蚓土,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜赖淤,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一蜀漆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧咱旱,春花似錦确丢、人聲如沸绷耍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽锨天。三九已至,卻和暖如春剃毒,著一層夾襖步出監(jiān)牢的瞬間病袄,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工赘阀, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留益缠,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓基公,卻偏偏與公主長得像幅慌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子轰豆,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 1.關(guān)于閉包 什么是閉包胰伍? 閉包是有權(quán)限訪問其它函數(shù)作用域內(nèi)的變量的一個函數(shù)。 在js中酸休,變量分為全局變量和局部變...
    自律寶藏男孩閱讀 305評論 0 0
  • 值類型 (1)值類型賦值的時候不會相互影響 // 值類型let a = 100let b = aa = 200co...
    WEB前端含光閱讀 252評論 0 0
  • 一骂租、html和css部分1、如何理解CSS的盒子模型斑司?標(biāo)準(zhǔn)盒子模型:寬度=內(nèi)容的寬度(content)+ bord...
    這是這時閱讀 406評論 0 5
  • 前言 金九銀十渗饮,又是一波跑路。趁著有空把前端基礎(chǔ)和面試相關(guān)的知識點都系統(tǒng)的學(xué)習(xí)一遍宿刮,參考一些權(quán)威的書籍和優(yōu)秀的文章...
    WEB前端含光閱讀 607評論 0 2
  • 久違的晴天互站,家長會。 家長大會開好到教室時僵缺,離放學(xué)已經(jīng)沒多少時間了胡桃。班主任說已經(jīng)安排了三個家長分享經(jīng)驗。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,520評論 16 22