10 個最基本的JS麵試問題及答案
1.使用 typeof bar === "object"
來確定 bar
是否是對象的潛在陷阱是什麼?如何避免這個陷阱?
盡管 typeof bar === "object"
是檢查 bar
是否對象的可靠方法,令人驚訝的是在JavaScript中 null
也被認為是對象!
因此,令大多數開發人員驚訝的是,下麵的代碼將輸出 true
(而不是false
) 到控製台:
var bar = null;console.log(typeof bar === "object"); // logs true!
隻要清楚這一點,同時檢查 bar
是否為 null
,就可以很容易地避免問題:
console.log((bar !== null) && (typeof bar === "object")); // logs false
要答全問題,還有其他兩件事情值得注意:
首先,上述解決方案將返回 false
,當 bar
是一個函數的時候。在大多數情況下,這是期望行為,但當你也想對函數返回 true
的話,你可以修改上麵的解決方案為:
console.log((bar !== null) && ((typeof bar === "object") || (typeof bar === "function")));
第二,上述解決方案將返回 true
,當 bar
是一個數組(例如,當 var bar = [];
)的時候。在大多數情況下,這是期望行為,因為數組是真正的對象,但當你也想對數組返回 false
時,你可以修改上麵的解決方案為:
console.log((bar !== null) && (typeof bar === "object") && (toString.call(bar) !== "[object Array]"));
或者,如果你使用jQuery的話:
console.log((bar !== null) && (typeof bar === "object") && (! $.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
在上麵的例子中都是undefined。
然而,事實並非如此。這裏的問題是,大多數開發人員將語句 var a = b = 3;
錯誤地理解為是以下聲明的簡寫:
var b = 3;var a = b;
但事實上,var a = b = 3;
實際是以下聲明的簡寫:
b = 3;var a = b;
因此(如果你不使用嚴格模式的話),該代碼段的輸出是:
a defined? falseb defined? true
但是, b
如何才能被定義在封閉函數的範圍之外呢?是的,既然語句 var a = b = 3;
是語句 b = 3;
和 var a = b;
的簡寫, b
最終成為了一個全局變量(因為它沒有前綴 var
關鍵字),因此仍然在範圍內甚至封閉函數之外。
需要注意的是,在嚴格模式下(即使用 use strict
),語句var a = b = 3;
將生成ReferenceError: b is not defined
的運行時錯誤,從而避免任何否則可能會導致的headfakes /bug。 (還是你為什麼應該理所當然地在代碼中使用 use strict
的最好例子!)
如果你想學習JS可以來這個群,首先是291,中間是851,最後是189,裏麵可以學習和交流,也有資料可以下載。
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
。
在內部函數中, this
不再指向 myObject
。其結果是,this.foo
沒有在內部函數中被定義,相反,指向到本地的變量self
保持在範圍內,並且可以訪問。 (在ECMA 5之前,在內部函數中的this
將指向全局的 window
對象;反之,因為作為ECMA 5,內部函數中的功能this
是未定義的。)
4.封裝JavaScript源文件的全部內容到一個函數塊有什麼意義及理由?
這是一個越來越普遍的做法,被許多流行的JavaScript庫(jQuery,Node.js等)采用。這種技術創建了一個圍繞文件全部內容的閉包,也許是最重要的是,創建了一個私有的命名空間,從而有助於避免不同JavaScript模塊和庫之間潛在的名稱衝突。
這種技術的另一個特點是,允許一個易於引用的(假設更短的)別名用於全局變量。這通常用於,例如,jQuery插件中。jQuery允許你使用jQuery.noConflict()
,來禁用 $
引用到jQuery命名空間。在完成這項工作之後,你的代碼仍然可以使用$
利用這種閉包技術,如下所示:
(function($) { /* jQuery plugin code referencing $ */ } )(jQuery);
5.在JavaScript源文件的開頭包含 use strict
有什麼意義和好處?
對於這個問題,既簡要又最重要的答案是,use strict
是一種在JavaScript代碼運行時自動實行更嚴格解析和錯誤處理的方法。那些被忽略或默默失敗了的代碼錯誤,會產生錯誤或拋出異常。通常而言,這是一個很好的做法。
嚴格模式的一些主要優點包括:
-
使調試更加容易。那些被忽略或默默失敗了的代碼錯誤,會產生錯誤或拋出異常,因此盡早提醒你代碼中的問題,你才能更快地指引到它們的源代碼。
-
防止意外的全局變量。如果沒有嚴格模式,將值分配給一個未聲明的變量會自動創建該名稱的全局變量。這是JavaScript中最常見的錯誤之一。在嚴格模式下,這樣做的話會拋出錯誤。
-
消除
this
強製。如果沒有嚴格模式,引用null或未定義的值到this
值會自動強製到全局變量。這可能會導致許多令人頭痛的問題和讓人恨不得拔自己頭發的bug。在嚴格模式下,引用 null或未定義的this
值會拋出錯誤。 -
不允許重複的屬性名稱或參數值。當檢測到對象(例如,
var object = {foo: "bar", foo: "baz"};
)中重複命名的屬性,或檢測到函數中(例如,function foo(val1, val2, val1){}
)重複命名的參數時,嚴格模式會拋出錯誤,因此捕捉幾乎可以肯定是代碼中的bug可以避免浪費大量的跟蹤時間。 -
使
eval()
更安全。在嚴格模式和非嚴格模式下,eval()
的行為方式有所不同。最顯而易見的是,在嚴格模式下,變量和聲明在eval()
語句內部的函數不會在包含範圍內創建(它們會在非嚴格模式下的包含範圍中被創建,這也是一個常見的問題源)。 -
在
delete
使用無效時拋出錯誤。delete
操作符(用於從對象中刪除屬性)不能用在對象不可配置的屬性上。當試圖刪除一個不可配置的屬性時,非嚴格代碼將默默地失敗,而嚴格模式將在這樣的情況下拋出異常。
6.考慮以下兩個函數。它們會返回相同的東西嗎? 為什麼相同或為什麼不相同?
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()
返回undefined卻沒有任何錯誤拋出。
原因與這樣一個事實有關,即分號在JavaScript中是一個可選項(盡管省略它們通常是非常糟糕的形式)。其結果就是,當碰到 foo2()
中包含 return
語句的代碼行(代碼行上沒有其他任何代碼),分號會立即自動插入到返回語句之後。
也不會拋出錯誤,因為代碼的其餘部分是完全有效的,即使它沒有得到調用或做任何事情(相當於它就是是一個未使用的代碼塊,定義了等同於字符串 "hello"
的屬性 bar
)。
這種行為也支持放置左括號於JavaScript代碼行的末尾,而不是新代碼行開頭的約定。正如這裏所示,這不僅僅隻是JavaScript中的一個風格偏好。
7. NaN
是什麼?它的類型是什麼?你如何可靠地測試一個值是否等於 NaN
?
NaN
屬性代表一個“不是數字”的值。這個特殊的值是因為運算不能執行而導致的,不能執行的原因要麼是因為其中的運算對象之一非數字(例如, "abc" / 4
),要麼是因為運算的結果非數字(例如,除數為零)。
雖然這看上去很簡單,但 NaN
有一些令人驚訝的特點,如果你不知道它們的話,可能會導致令人頭痛的bug。
首先,雖然 NaN
意味著“不是數字”,但是它的類型,不管你信不信,是 Number
:
console.log(typeof NaN === "number"); // logs "true"
此外, NaN
和任何東西比較——甚至是它自己本身!——結果是false:
console.log(NaN === NaN); // logs "false"
一種半可靠的方法來測試一個數字是否等於 NaN,是使用內置函數 isNaN()
,但即使使用 isNaN()
依然並非是一個完美的解決方案。
一個更好的解決辦法是使用 value !== value
,如果值等於NaN,隻會產生true。另外,ES6提供了一個新的 Number.isNaN()
函數,這是一個不同的函數,並且比老的全局 isNaN()
函數更可靠。
8.下列代碼將輸出什麼?並解釋原因。
console.log(0.1 + 0.2);console.log(0.1 + 0.2 == 0.3);
一個稍微有點編程基礎的回答是:“你不能確定。可能會輸出“0.3”和“true”,也可能不會。JavaScript中的數字和浮點精度的處理相同,因此,可能不會總是產生預期的結果。“
以上所提供的例子就是一個演示了這個問題的典型例子。但出人意料的是,它會輸出:
0.30000000000000004false
9.討論寫函數 isInteger(x)
的可能方法,用於確定x是否是整數。
這可能聽起來是小菜一碟,但事實上,這很瑣碎,因為ECMAScript 6引入了一個新的正以此為目的 Number.isInteger()
函數。然而,之前的ECMAScript 6,會更複雜一點,因為沒有提供類似的 Number.isInteger()
方法。
問題是,在ECMAScript規格說明中,整數隻概念上存在:即,數字值總是存儲為浮點值。
考慮到這一點,最簡單又最幹淨的ECMAScript6之前的解決方法(同時也非常穩健地返回 false
,即使一個非數字的值,如字符串或 null
,被傳遞給函數)如下:
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) === 1000000000000000000000false
10.下列代碼行1-4如何排序,使之能夠在執行代碼時輸出到控製台? 為什麼?
(function() { console.log(1);
setTimeout(function(){console.log(2)}, 1000);
setTimeout(function(){console.log(3)}, 0);
console.log(4);
})();
序號如下:
1
4
3
2
讓我們先來解釋比較明顯而易見的那部分:
-
1
和4
之所以放在前麵,是因為它們是通過簡單調用console.log()
而沒有任何延遲輸出的 -
2
之所以放在3
的後麵,是因為2
是延遲了1000毫秒(即,1秒)之後輸出的,而3
是延遲了0毫秒之後輸出的。
好的。但是,既然 3
是0毫秒延遲之後輸出的,那麼是否意味著它是立即輸出的呢?如果是的話,那麼它是不是應該在 4
之前輸出,既然 4
是在第二行輸出的?
要回答這個問題,你需要正確理解JavaScript的事件和時間設置。
瀏覽器有一個事件循環,會檢查事件隊列和處理未完成的事件。例如,如果時間發生在後台(例如,腳本的 onload
事件)時,瀏覽器正忙(例如,處理一個 onclick
),那麼事件會添加到隊列中。當onclick處理程序完成後,檢查隊列,然後處理該事件(例如,執行 onload
腳本)。
同樣的, setTimeout()
也會把其引用的函數的執行放到事件隊列中,如果瀏覽器正忙的話。
當setTimeout()
的第二個參數為0的時候,它的意思是“盡快”執行指定的函數。具體而言,函數的執行會放置在事件隊列的下一個計時器開始。但是請注意,這不是立即執行:函數不會被執行除非下一個計時器開始。這就是為什麼在上述的例子中,調用 console.log(4)
發生在調用 console.log(3)
之前(因為調用 console.log(3)
是通過setTimeout被調用的,因此會稍微延遲)。
最後更新:2017-09-14 13:32:51
上一篇:
中國製造2025到來,智能工廠如何升級轉型?
下一篇:
Java麵試常被問到的題目+解答
調試MVC源代碼時"[A]System.Web.WebPages.Razor.Configuration.HostSection 無法強製轉換為 ..."解決辦法
Oracle Dataguard在阿裏雲ecs上的測試
月球趣事——月球錯覺
Mac OS X也可以跑OpenGL啦
MySQL SQL 分析 - 參數化查詢 vs query cache 功能
攻略:如何快速賺取積分,Get雲棲大會資料
ASP.NET Core應用針對靜態文件請求的處理[3]: StaticFileMiddleware中間件如何處理針對文件請求
長安汽車發布“香格裏拉計劃”:2025年全麵停售燃油車,布局新能源L3-L4級自動駕駛
藍天白雲何在?國外的天空分外藍,外國的月亮更加圓?真實與謊言
poj 1575 Easier Done Than Said?【字符串處理】