1. 程式人生 > >37個 JavaScript 基本面試問題和解答

37個 JavaScript 基本面試問題和解答

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

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

因此,對於大多數開發人員來說,下面的程式碼會將真實(而不是錯誤)記錄到控制檯:

12 varbar=null;console.log(typeof bar==="object");// logs true!

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

1 console.log((bar!==null)&&
(typeof bar==="object"));// logs false

為了在我們的答案更加的完整,還有兩件事值得注意:

首先,如果bar是一個函式,上面的解決方案將返回false。在大多數情況下,這是所期望的行為,但是在您希望函式返回true的情況下,您可以將上述解決方案修改為:

1 console.log((bar!==null)&&((typeof bar==="object")||(typeof bar==="function")));

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

1 console.log((bar!==null)&&(typeof bar==="object")&&(toString.call(bar)!=="[object Array]"));

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

1 console.log((bar!==null)&&(bar.constructor===Object));

或者,如果您使用jQuery:

1 console.log((bar!==null)&&(typeof bar==="object")&&(!$.isArray(bar)));

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

1 console.log(Array.isArray(bar));

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

123456 (function(){vara=b=3;})();console.log("a defined? "+(typeofa!=='undefined'));console.log("b defined? "+(typeofb!=='undefined'));

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

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

12 varb=3;vara=b;

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

12 b=3;vara=b;

因此(如果您不使用嚴格模式),程式碼片段的輸出將為:

12 adefined?falsebdefined?true

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

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

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

12345678910111213 varmyObject={foo:"bar",func:function(){varself=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();

以上程式碼將輸出到控制檯:

1234 outer func:this.foo=barouter func:self.foo=barinner func:this.foo=undefinedinner func:self.foo=bar

在外部函式中,this和self都引用myObject,因此都可以正確地引用和訪問foo。

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

4、在功能塊中封裝JavaScript原始檔的全部內容的重要性和原因是什麼?

這是一種日益普遍的做法,被許多流行的JavaScript庫(jQuery,Node.js等)所採用。這種技術在檔案的全部內容周圍建立一個閉包,這可能最重要的是建立一個私有名稱空間,從而有助於避免不同JavaScript模組和庫之間的潛在名稱衝突。

這種技術的另一個特點是為全域性變數提供一個容易引用(可能更短)的別名。例如,這通常用於jQuery外掛。 jQuery允許您使用jQuery.noConflict()來禁用對jQuery名稱空間的$引用。如果這樣做了,你的程式碼仍然可以使用$使用閉包技術,如下所示:

1 (function($){/* jQuery plugin code referencing $ */})(jQuery);

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

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

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

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

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

1234567891011121314 functionfoo1(){return{bar:"hello"};}functionfoo2(){return{bar:"hello"};}

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

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

會產生:

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

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

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

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

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

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

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

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

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

1 console.log(typeof NaN==="number");// logs "true"

此外,NaN相比任何事情 – 甚至本身! – 是false:

1 console.log(NaN===NaN);// logs "false"

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

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

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

12 console.log(0.1+0.2);console.log(0.1+0.2==0.3);

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

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

12 0.30000000000000004false

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

1234 functionareTheNumbersAlmostEqual(num1,num2){returnMath.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)將成為以下用法按位異或運算子:

1 functionisInteger(x){return(x^0)