前端經典的JavaScript面試知識總結

小圖子發表於2019-03-01

1、使用typeof bar ==="object" 來確定bar是否是一個物件時有什麼潛在的缺陷?這個陷阱如何避免?

儘管typeof bar ===“object”是檢查bar是否是物件的可靠方法,但JavaScript中令人驚訝的問題null也被認為是一個物件!

因此,對於大多數開發人員來說,下面的程式碼會將true(而不是false)列印到控制檯:

var bar = null;
console.log(typeof bar === "object"); // logs true!
複製程式碼

只要知道這一點,就可以通過檢查bar是否為空來輕鬆避免該問題:

console.log((bar !== null) && (typeof bar === "object")); // logs false
複製程式碼

為了讓我們的答案更加的完整,還有兩件事值得注意: 首先,如果bar是一個函式,上面的解決方案將返回false。在大多數情況下,這是所期望的行為,但是在您希望函式返回true的情況下,您可以將上述解決方案修改為:

console.log((bar !== null) && ((typeof bar === "object") || (typeof bar === "function")));
複製程式碼

其次,如果bar是陣列,則上述解決方案將返回true(例如,如果var bar = [];)。在大多數情況下,這是所希望的行為,因為陣列確實是物件,但是在您想要對陣列也是false的情況下,可以將上述解決方案修改為:

console.log((bar !== null) && (typeof bar === "object") && (toString.call(bar) !== "[object Array]"));
複製程式碼

但是,還有一個替代方法對空值,陣列和函式返回false,但對於物件則為true:

console.log((bar !== null) && (bar.constructor === Object));
複製程式碼

或者,如果您使用jQuery:

console.log((bar !== null) && (typeof bar === "object") && (! $.isArray(bar)));
複製程式碼

ES5使得陣列的情況非常簡單,包括它自己的空檢查:

console.log(Array.isArray(bar));
複製程式碼

2、下面的程式碼將輸出到控制檯的是什麼,為什麼?

(function(){
  var a = b = 3;
})();
console.log("a defined? " + (typeof a !== 'undefined'));
console.log("b defined? " + (typeof b !== 'undefined'));
複製程式碼

由於a和b都在函式的封閉範圍內定義,並且由於它們所在的行以var關鍵字開頭,因此大多數JavaScript開發人員會希望typeof a和typeof b在上面的示例中都未定義。

但是,情況並非如此。這裡的問題是大多數開發人員錯誤地理解語句var a = b = 3;以下簡寫為:

var b = 3;
var a = b;
//但實際上,var a = b = 3;其實是速記:

b = 3;
var a = b;
//因此(如果您不使用嚴格模式),程式碼片段的輸出將為:

//a defined? false
//b defined? true
複製程式碼

但是如何在封閉函式的範圍之外定義b?那麼,因為宣告var a = b = 3;是語句b = 3的簡寫;並且var a = b; b最終成為一個全域性變數(因為它不在var關鍵字後面),因此它仍然在作用域內,即使在封閉函式之外。

注意,在嚴格模式下(即,使用strict),語句var a = b = 3;會產生一個ReferenceError的執行時錯誤:b沒有定義,從而避免了可能導致的任何頭headfakes/bugs。 (這就是為什麼你應該在你的程式碼中使用strict,一個重要的例子!)

3、下面的程式碼將輸出到控制檯的是什麼?,為什麼?

var myObject = {
    foo: "bar",
    func: function() {
        var self = this;
        console.log("outer func: this.foo = " + this.foo);
        console.log("outer func: self.foo = " + self.foo);
        (function() {
            console.log("inner func: this.foo = " + this.foo);
            console.log("inner func: self.foo = " + self.foo);
        }());
    }
};
myObject.func();
以上程式碼將輸出到控制檯:

outer func: this.foo = bar
outer func: self.foo = bar
inner func: this.foo = undefined
inner func: self.foo = bar
在外部函式中,this和self都引用myObject,因此都可以正確地引用和訪問foo。

但在內部函式中,這不再指向myObject。因此,this.foo在內部函式中是未定義的,而對區域性變數self的引用仍然在範圍內並且可以在那裡訪問。
複製程式碼

4、在JavaScript原始檔的開頭包含'use strict'的意義和有什麼好處?

這裡最簡單也是最重要的答案是use strict是一種在執行時自動執行更嚴格的JavaScript程式碼解析和錯誤處理的方法。如果程式碼錯誤被忽略或失敗,將會產生錯誤或丟擲異常。總的來說,這是一個很好的做法。

嚴格模式的一些主要優點包括:

使除錯更容易。 如果程式碼錯誤本來會被忽略或失敗,那麼現在將會產生錯誤或丟擲異常,從而更快地發現程式碼中的問題,並更快地指引它們的原始碼。 防止意外全域性。 如果沒有嚴格模式,將值賦給未宣告的變數會自動建立一個具有該名稱的全域性變數。這是JavaScript中最常見的錯誤之一。在嚴格模式下,嘗試這樣做會引發錯誤。 消除隱藏威脅。在沒有嚴格模式的情況下,對null或undefined的這個值的引用會自動強制到全域性。這可能會導致許多headfakes和pull-out-your-hair型別的錯誤。在嚴格模式下,引用null或undefined的這個值會引發錯誤。 不允許重複的引數值。 嚴格模式在檢測到函式的重複命名引數(例如,函式foo(val1,val2,val1){})時會引發錯誤,從而捕獲程式碼中幾乎可以肯定存在的錯誤,否則您可能會浪費大量的時間追蹤。 注意:它曾經是(在ECMAScript 5中)strict模式將禁止重複的屬性名稱(例如var object = {foo:“bar”,foo:“baz”};)但是從ECMAScript 2015 開始,就不再有這種情況了。 使eval()更安全。 eval()在嚴格模式和非嚴格模式下的行為方式有些不同。最重要的是,在嚴格模式下,在eval()語句內部宣告的變數和函式不會在包含範圍中建立(它們是以非嚴格模式在包含範圍中建立的,這也可能是問題的常見來源)。 丟擲無效的使用錯誤的刪除符。 刪除操作符(用於從物件中刪除屬性)不能用於物件的不可配置屬性。當試圖刪除一個不可配置的屬性時,非嚴格程式碼將自動失敗,而在這種情況下,嚴格模式會引發錯誤。

5、考慮下面的兩個函式。他們都會返回同樣的值嗎?為什麼或者為什麼不?

function foo1(){
  return {
      bar: "hello"
  };
}

function foo2(){
  return
  {
      bar: "hello"
  };
}
複製程式碼

令人驚訝的是,這兩個函式不會返回相同的結果。而是:

console.log("foo1 returns:");
console.log(foo1());
console.log("foo2 returns:");
console.log(foo2());

//會產生:

foo1 returns:
Object {bar: "hello"}
foo2 returns:
undefined

複製程式碼

這不僅令人驚訝,而且特別令人煩惱的是,foo2()返回未定義而沒有引發任何錯誤。

原因與JavaScript中分號在技術上是可選的事實有關(儘管忽略它們通常是非常糟糕的形式)。因此,在foo2()中遇到包含return語句的行(沒有其他內容)時,會在return語句之後立即自動插入分號。

由於程式碼的其餘部分是完全有效的,即使它沒有被呼叫或做任何事情(它只是一個未使用的程式碼塊,它定義了一個屬性欄,它等於字串“hello”),所以不會丟擲任何錯誤。

這種行為也被認為是遵循了在JavaScript中將一行開頭大括號放在行尾的約定,而不是在新行的開頭。如此處所示,這不僅僅是JavaScript中的一種風格偏好。

6、什麼是NaN?它的型別是什麼?如何可靠地測試一個值是否等於NaN?

NaN屬性表示“不是數字”的值。這個特殊值是由於一個運算元是非數字的(例如“abc”/ 4)或者因為操作的結果是非數字而無法執行的。

雖然這看起來很簡單,但NaN有一些令人驚訝的特徵,如果人們沒有意識到這些特徵,就會導致bug。

一方面,雖然NaN的意思是“不是數字”,但它的型別是,數字:

console.log(typeof NaN === "number"); // logs "true" 此外,NaN相比任何事情 - 甚至本身! - 是false:

console.log(NaN === NaN); // logs "false" 測試數字是否等於NaN的半可靠方法是使用內建函式isNaN(),但即使使用 isNaN()也不是一個好的解決方案。.

一個更好的解決方案要麼是使用value!==值,如果該值等於NaN,那麼只會生成true。另外,ES6提供了一個新的Number.isNaN()函式 ,它與舊的全域性isNaN()函式不同,也更加可靠。

7、下面的程式碼輸出什麼?解釋你的答案。

console.log(0.1 + 0.2);
console.log(0.1 + 0.2 == 0.3);
複製程式碼

對這個問題的一個有教養的回答是:“你不能確定。它可能列印出0.3和true,或者可能不列印。 JavaScript中的數字全部用浮點精度處理,因此可能不會總是產生預期的結果

上面提供的示例是演示此問題的經典案例。令人驚訝的是,它會列印出來:

0.30000000000000004
false
複製程式碼

一個典型的解決方案是比較兩個數字與特殊常數Number.EPSILON之間的絕對差值:

function areTheNumbersAlmostEqual(num1, num2) {
    return Math.abs( num1 - num2 ) < Number.EPSILON;
}
console.log(areTheNumbersAlmostEqual(0.1 + 0.2, 0.3));
複製程式碼

討論寫函式的可能方法isInteger(x),它確定x是否是一個整數。

這聽起來很平凡,事實上,ECMAscript 6為此正好引入了一個新的Number.isInteger()函式,這是微不足道的。但是,在ECMAScript 6之前,這有點複雜,因為沒有提供與Number.isInteger()方法等價的方法。

問題在於,在ECMAScript規範中,整數只在概念上存在;即數值始終作為浮點值儲存。

考慮到這一點,最簡單,最清潔的ECMAScript-6之前的解決方案(即使將非數字值(例如字串或空值)傳遞給該函式,該解決方案也具有足夠的可靠性以返回false)將成為以下用法按位異或運算子:

function isInteger(x) { return (x ^ 0) === x; }
複製程式碼

下面的解決方案也可以工作,儘管不如上面那樣高雅

function isInteger(x) { return Math.round(x) === x; }
複製程式碼

請注意,在上面的實現中Math.ceil()Math.floor()可以同樣使用(而不是Math.round())。

或者:

function isInteger(x) { return (typeof x === 'number') && (x % 1 === 0); }
複製程式碼

一個相當常見的不正確的解決方案如下:

function isInteger(x) { return parseInt(x, 10) === x; }
複製程式碼

雖然這個基於parseInt的方法對許多x值很有效,但一旦x變得相當大,它將無法正常工作。問題是parseInt()在解析數字之前將其第一個引數強制轉換為字串。因此,一旦數字變得足夠大,其字串表示將以指數形式呈現(例如1e + 21)。因此,parseInt()將嘗試解析1e + 21,但是當它到達e字元時將停止解析,因此將返回值1.觀察:

> String(1000000000000000000000)
'1e+21'
> parseInt(1000000000000000000000, 10)
1
> parseInt(1000000000000000000000, 10) === 1000000000000000000000
false
複製程式碼

9、執行下面的程式碼時,按什麼順序將數字1-4記錄到控制檯?為什麼?(event loop)

(function() {
    console.log(1);
    setTimeout(function(){console.log(2)}, 1000);
    setTimeout(function(){console.log(3)}, 0);
    new Promise(function(resolve,reject){
        console.log(6)
        resolve(5)
    }).then(function(val){
        console.log(val);
    })
    console.log(4);
})();

//瀏覽器輸出:
1
6
4
5
3
2
複製程式碼

原因:

JavaScript語言的一大特點就是單執行緒,也就是說,同一個時間只能做一件事,單執行緒就意味著,所有任務需要排隊,前一個任務結束,才會執行後一個任務。如果前一個任務耗時很長,後一個任務就不得不一直等著。
如果排隊是因為計算量大,CPU忙不過來,倒也算了,但是很多時候CPU是閒著的,因為IO裝置(輸入輸出裝置)很慢(比如Ajax操作從網路讀取資料),不得不等著結果出來,再往下執行.
於是,所有任務可以分成兩種,一種是同步任務(synchronous),另一種是非同步任務(asynchronous)。同步任務指的是,在主執行緒上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務;非同步任務指的是,不進入主執行緒、而進入"任務佇列"(task queue)的任務,只有"任務佇列"通知主執行緒,某個非同步任務可以執行了,該任務才會進入主執行緒執行。

(1)所有同步任務都在主執行緒上執行,形成一個執行棧(execution context stack)。
(2)主執行緒之外,還存在一個"任務佇列"(task queue)。只要非同步任務有了執行結果,就在"任務佇列"之中放置一個事件。
(3)一旦"執行棧"中的所有同步任務執行完畢,系統就會讀取"任務佇列",看看裡面有哪些事件。那些對應的非同步任務,於是結束等待狀態,進入執行棧,開始執行.
(4)主執行緒不斷重複上面的第三步。
複製程式碼

10、下面的程式碼將輸出到控制檯,為什麼?

console.log(1 + "2" + "2");
console.log(1 + +"2" + "2");
console.log(1 + -"1" + "2");
console.log(+"1" + "1" + "2");
console.log( "A" - "B" + "2");
console.log( "A" - "B" + 2);
以上程式碼將輸出到控制檯:

"122"
"32"
"02"
"112"
"NaN2"
NaN
複製程式碼

原因:

這裡的基本問題是JavaScript(ECMAScript)是一種鬆散型別的語言,它對值執行自動型別轉換以適應正在執行的操作。讓我們來看看這是如何與上面的每個例子進行比較。

示例1:1 +“2”+“2”輸出:“122”說明:第一個操作在1 +“2”中執行。由於其中一個運算元(“2”)是一個字串,所以JavaScript假定需要執行字串連線,因此將1的型別轉換為“1”,1 +“2”轉換為“12”。然後,“12”+“2”產生“122”。

示例2:1 + +“2”+“2”輸出:“32”說明:根據操作順序,要執行的第一個操作是+“2”(第一個“2”之前的額外+被視為一個一元運算子)。因此,JavaScript將“2”的型別轉換為數字,然後將一元+符號應用於它(即將其視為正數)。結果,下一個操作現在是1 + 2,當然這會產生3.但是,我們有一個數字和一個字串之間的操作(即3和“2”),所以JavaScript再次轉換數值賦給一個字串並執行字串連線,產生“32”。

示例3:1 + - “1”+“2”輸出:“02”說明:這裡的解釋與前面的示例相同,只是一元運算子是 - 而不是+。因此,“1”變為1,然後在應用 - 時將其變為-1,然後將其加1到產生0,然後轉換為字串並與最終的“2”運算元連線,產生“02”。

示例4:+“1”+“1”+“2”輸出:“112”說明:儘管第一個“1”運算元是基於其前面的一元+運算子的數值型別轉換的,當它與第二個“1”運算元連線在一起時返回一個字串,然後與最終的“2”運算元連線,產生字串“112”。

示例5:“A” - “B”+“2”輸出:“NaN2”說明:由於 - 運算子不能應用於字串,並且既不能將“A”也不能將“B”轉換為數值, “ - ”B“產生NaN,然後與字串”2“串聯產生”NaN2“。

例6:“A” - “B”+2輸出:NaN說明:在前面的例子中,“A” - “B”產生NaN。但是任何運算子應用於NaN和其他數字運算元仍然會產生NaN。

11、如果陣列列表太大,以下遞迴程式碼將導致堆疊溢位。你如何解決這個問題,仍然保留遞迴模式?

var list = readHugeList();

var nextListItem = function() {
    var item = list.pop();

    if (item) {
        // process the list item...
        nextListItem();
    }
};

`通過修改nextListItem函式可以避免潛在的堆疊溢位,如下所示:`

var list = readHugeList();

var nextListItem = function() {
    var item = list.pop();

    if (item) {
        // process the list item...
        setTimeout( nextListItem, 0);
    }
};
複製程式碼

堆疊溢位被消除,因為事件迴圈處理遞迴,而不是呼叫堆疊。當nextListItem執行時,如果item不為null,則將超時函式(nextListItem)推送到事件佇列,並且函式退出,從而使呼叫堆疊清零。當事件佇列執行超時事件時,將處理下一個專案,並設定一個計時器以再次呼叫nextListItem。因此,該方法從頭到尾不經過直接遞迴呼叫即可處理,因此呼叫堆疊保持清晰,無論迭代次數如何。

12.什麼是JavaScript中的“閉包”?舉一個例子

閉包是一個內部函式,它可以訪問外部(封閉)函式的作用域鏈中的變數。閉包可以訪問三個範圍內的變數;具體來說: (1)變數在其自己的範圍內, (2)封閉函式範圍內的變數 (3)全域性變數。

這裡是一個例子:

var globalVar = "xyz";

(function outerFunc(outerArg) {
    var outerVar = 'a';

    (function innerFunc(innerArg) {
    var innerVar = 'b';

    console.log(
        "outerArg = " + outerArg + "\n" +
        "innerArg = " + innerArg + "\n" +
        "outerVar = " + outerVar + "\n" +
        "innerVar = " + innerVar + "\n" +
        "globalVar = " + globalVar);

    })(456);
})(123);
outerArg = 123
innerArg = 456
outerVar = a
innerVar = b
globalVar = xyz
複製程式碼

在上面的例子中,innerFunc,outerFunc和全域性名稱空間的變數都在 innerFunc的範圍內。上面的程式碼將產生以下輸出:

13、以下幾行程式碼輸出到控制檯?

console.log("0 || 1 = "+(0 || 1));
console.log("1 || 2 = "+(1 || 2));
console.log("0 && 1 = "+(0 && 1));
console.log("1 && 2 = "+(1 && 2));
複製程式碼

解釋你的答案。

//該程式碼將輸出以下四行:
0 || 1 = 1
1 || 2 = 1
0 && 1 = 0
1 && 2 = 2
複製程式碼

在JavaScript中,都是||和&&是邏輯運算子,當從左向右計算時返回第一個完全確定的“邏輯值”。

或(||)運算子。在形式為X || Y的表示式中,首先計算X並將其解釋為布林值。如果此布林值為真,則返回true(1),並且不計算Y,因為“或”條件已經滿足。但是,如果此布林值為“假”,我們仍然不知道X || Y是真還是假,直到我們評估Y,並將其解釋為布林值。

因此,0 || 1評估為真(1),正如1 || 2。

和(&&)運算子。在X && Y形式的表示式中,首先評估X並將其解釋為布林值。如果此布林值為false,則返回false(0)並且不評估Y,因為“and”條件已失敗。但是,如果這個布林值為“真”,我們仍然不知道X && Y是真還是假,直到我們評估Y,並將其解釋為布林值。

然而,&&運算子的有趣之處在於,當表示式評估為“真”時,則返回表示式本身。這很好,因為它在邏輯表示式中被視為“真”,但也可以用於在您關心時返回該值。這解釋了為什麼,有點令人驚訝的是,1 && 2返回2(而你可能會期望它返回true或1)

14 、下面的程式碼執行時輸出是什麼?說明。

console.log(false == '0')
console.log(false === '0')

//該程式碼將輸出:

true
false
複製程式碼

在JavaScript中,有兩套相等運算子。三重相等運算子===的行為與任何傳統的相等運算子相同:如果兩側的兩個表示式具有相同的型別和相同的值,則計算結果為true。然而,雙等號運算子在比較它們之前試圖強制這些值。因此,通常使用===而不是==。對於!== vs!=也是如此。

15、以下程式碼的輸出是什麼?解釋你的答案。

var a={},
    b={key:'b'},
    c={key:'c'};

a[b]=123;
a[c]=456;

console.log(a[b]); //456
//此程式碼的輸出將是456(不是123)。
複製程式碼

原因如下:設定物件屬性時,JavaScript會隱式地將引數值串聯起來。在這種情況下,由於b和c都是物件,它們都將被轉換為“[object Object]”。因此,a [b]和a [c]都等價於[“[object Object]”],並且可以互換使用。因此,設定或引用[c]與設定或引用[b]完全相同。

16、考慮下面的程式碼片段。控制檯的輸出是什麼,為什麼?

(function(x) {
    return (function(y) {
        console.log(x);
    })(2)
})(1);
//log  1;
複製程式碼

輸出將為1,即使x的值從未在內部函式中設定。原因如下:

正如我們的JavaScript招聘指南中所解釋的,閉包是一個函式,以及建立閉包時在範圍內的所有變數或函式。在JavaScript中,閉包被實現為“內部函式”;即在另一功能的主體內定義的功能。閉包的一個重要特徵是內部函式仍然可以訪問外部函式的變數。

因此,在這個例子中,因為x沒有在內部函式中定義,所以在外部函式的作用域中搜尋一個定義的變數x,該變數的值為1。

17、以下程式碼的輸出是什麼?

var length = 10;
function fn() {
    console.log(this.length);
}

var obj = {
  length: 5,
  method: function(fn) {
    fn();
    arguments[0]();
  }
};

obj.method(fn, 1);
輸出:

10
2
複製程式碼

為什麼不是10和5?

首先,由於fn作為函式方法的引數傳遞,函式fn的作用域(this)是視窗。 var length = 10;在視窗級別宣告。它也可以作為window.length或length或this.length來訪問(當這個===視窗時)。

方法繫結到Object obj,obj.method用引數fn和1呼叫。雖然方法只接受一個引數,但呼叫它時已經傳遞了兩個引數;第一個是函式回撥,其他只是一個數字。

當在內部方法中呼叫fn()時,該函式在全域性級別作為引數傳遞,this.length將有權訪問在Object obj中定義的var length = 10(全域性宣告)而不是length = 5。

現在,我們知道我們可以使用arguments []陣列訪問JavaScript函式中的任意數量的引數。

因此arguments0只不過是呼叫fn()。在fn裡面,這個函式的作用域成為引數陣列,並且記錄引數[]的長度將返回2。

18、考慮下面的程式碼。輸出是什麼,為什麼?

(function () {
    try {
        throw new Error();
    } catch (x) {
        var x = 1, y = 2;
        console.log(x);
    }
    console.log(x);
    console.log(y);
})();
1
undefined
2
複製程式碼

var語句被掛起(沒有它們的值初始化)到它所屬的全域性或函式作用域的頂部,即使它位於with或catch塊內。但是,錯誤的識別符號只在catch塊內部可見。它相當於:

(function () {
    var x, y; // outer and hoisted
    try {
        throw new Error();
    } catch (x /* inner */) {
        x = 1; // inner x, not the outer one
        y = 2; // there is only one y, which is in the outer scope
        console.log(x /* inner */);
    }
    console.log(x);
    console.log(y);
})();
複製程式碼

19、這段程式碼的輸出是什麼?

var x = 21;
var girl = function () {
    console.log(x);
    var x = 20;
};
girl ();
//21,也不是20,結果是‘undefined’的
複製程式碼

這是因為JavaScript初始化沒有被掛起。

(為什麼它不顯示21的全域性值?原因是當函式執行時,它檢查是否存在本地x變數但尚未宣告它,因此它不會查詢全域性變數。)

20、以下幾行輸出什麼,為什麼?

console.log(1 < 2 < 3);
console.log(3 > 2 > 1);
複製程式碼

第一條語句返回true,如預期的那樣。

第二個返回false是因為引擎如何針對<和>的操作符關聯性工作。它比較從左到右,所以3> 2> 1 JavaScript翻譯為true> 1. true具有值1,因此它比較1> 1,這是錯誤的。

21、怎麼獲取頁面所有的元素(遞迴方式實現)

function getAllNode() {
    var nodes = document.childNodes;
    var arr_all_ele = [];
    function returnNode(arr_nodes, arr_all_ele) {
        for(var i=0; i<arr_nodes.length; i++) {
            var parentNode = arr_nodes[i];
            arr_all_ele.push(parentNode);
            var childNodes = parentNode.childNodes;
            if(childNodes.length != 0) {
                returnNode(childNodes, arr_all_ele);
            }
        }
    }
    returnNode(nodes, arr_all_ele);
    return arr_all_ele;
}
複製程式碼

22、程式碼返回後會怎麼樣?

console.log(typeof typeof 1);
//列印結果:string
複製程式碼

typeof 1將返回“number”,typeof“number”將返回字串。

23、怎麼判斷一個物件是不是陣列型別?

1.typeof {}和typeof []的結果都是object

2、從原型入手,Array.prototype.isPrototypeOf(obj); 利用isPrototypeOf()方法,判定Array是不是在obj的原型鏈中,如果是,則返回true,否則false。

3.先說說 typeof 和 instanceof 的區別? 兩者都可以用來判斷變數,typeof會返回基本型別,如文章開頭,我們很簡單可以用 typeof a != 'undefined' 判斷a變數存在。而instanceof只會返回一個布林值,那麼我們試試,結果如下:

"fff" instanceof Array
false
[] instanceof Array
true
複製程式碼

4.根據物件的class屬性(類屬性),跨原型鏈呼叫toString()方法。

Object.prototype.toString.call([])
"[object Array]"
複製程式碼

5.ES6的Array.isArray()方法;

Array.isArray([])
true
複製程式碼

24、一個DOM元素繫結多個事件時,先執行冒泡還是捕獲;

所有事件的順序是:其他元素捕獲階段事件 -> 本元素程式碼順序事件 -> 其他元素冒泡階段事件

相關文章