著名前端架構師Baranovskiy的博客一個帖子《So, you think you know JavaScript?》javascript
題目一:前端
if (!("a" in window)) { var a = 1; } alert(a);
題目二:java
var a = 1, b = function a(x) { x && a(--x); }; alert(a);
題目三:瀏覽器
function a(x) { return x * 2; } var a; alert(a);
題目四:架構
function b(x, y, a) { arguments[2] = 10; alert(a); } b(1, 2, 3);
題目五:函數
function a() { alert(this); } a.call(null);
請不要藉助任何幫助工具,心算答案。答案在下面。工具
.post
.this
.spa
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
答案:
if (!("a" in window)) { var a = 1; } alert(a);
代碼含義:若是window不包含屬性a,就聲明一個變量a,而後賦值爲1。
你可能認爲alert出來的結果是1,而後實際結果是「undefined」。要了解爲何,須要知道JavaScript裏的3個概念。
首先,全部的全局變量都是window的屬性,語句 var a = 1;等價於window.a = 1; 你能夠用以下方式來檢測全局變量是否聲明:
"變量名稱" in window
第二,全部的變量聲明都在範圍做用域的頂部,看一下類似的例子:
alert("a" in window); var a;
此時,儘管聲明是在alert以後,alert彈出的依然是true,這是由於JavaScript引擎首先會掃墓全部的變量聲明,而後將這些變量聲明移動到頂部,最終的代碼效果是這樣的:
var a; alert("a" in window);
這樣看起來就很容易解釋爲何alert結果是true了。
第三,你須要理解該題目的意思是,變量聲明被提早了,但變量賦值沒有,由於這行代碼包括了變量聲明和變量賦值。
你能夠將語句拆分爲以下代碼:
var a; //聲明 a = 1; //初始化賦值
當變量聲明和賦值在一塊兒用的時候,JavaScript引擎會自動將它分爲兩部以便將變量聲明提早,不將賦值的步驟提早是由於他有可能影響代碼執行出不可預期的結果。
因此,知道了這些概念之後,從新回頭看一下題目的代碼,其實就等價於:
var a; if (!("a" in window)) { a = 1; } alert(a);
這樣,題目的意思就很是清楚了:首先聲明a,而後判斷a是否在存在,若是不存在就賦值爲1,很明顯a永遠在window裏存在,這個賦值語句永遠不會執行,因此結果是undefined。
提早這個詞語顯得有點迷惑了,你能夠理解爲:預編譯。
var a = 1, b = function a(x) { x && a(--x); }; alert(a);
這個題目看起來比實際複雜,alert的結果是1;這裏依然有3個重要的概念須要咱們知道。
首先,在題目1裏咱們知道了變量聲明在進入執行上下文就完成了;第二個概念就是函數聲明也是提早的,全部的函數聲明都在執行代碼以前都已經完成了聲明,和變
量聲明同樣。澄清一下,函數聲明是以下這樣的代碼:
function functionName(arg1, arg2){ //函數體 }
以下不是函數,而是函數表達式,至關於變量賦值:
var functionName = function(arg1, arg2){ //函數體 };
澄清一下,函數表達式沒有提早,就至關於平時的變量賦值。
第三須要知道的是,函數聲明會覆蓋變量聲明,但不會覆蓋變量賦值,爲了解釋這個,咱們來看一個例子:
function value(){ return 1; } var value; alert(typeof value); //"function"
儘快變量聲明在下面定義,可是變量value依然是function,也就是說這種狀況下,函數聲明的優先級高於變量聲明的優先級,但若是該變量value賦值了,那結果就徹底不同了:
function value(){ return 1; } var value = 1; alert(typeof value); //"number"
該value賦值之後,變量賦值初始化就覆蓋了函數聲明。
從新回到題目,這個函數實際上是一個有名函數表達式,函數表達式不像函數聲明同樣能夠覆蓋變量聲明,但你能夠注意到,變量b是包含了該函數表達式,而該函數表達式的名字是a;不一樣的瀏覽器對a這個名詞處理有點不同,在IE裏,會將a認爲函數聲明,因此它被變量初始化覆蓋了,就是說若是調用a(–x)的話就會出錯,而其它瀏覽器在容許在函數內部調用a(–x),由於這時候a在函數外面依然是數字。基本上,IE裏調用b(2)的時候會出錯,但其它瀏覽器則返回undefined。
理解上述內容以後,該題目換成一個更準確和更容易理解的代碼應該像這樣:
var a = 1, b = function(x) { x && b(--x); }; alert(a);
這樣的話,就很清晰地知道爲何alert的老是1了。
function a() { return 1 ; } var a; alert(a);
這個題目比較簡單:即函數聲明和變量聲明的關係和影響,遇到同名的函數聲明,不會從新定義
function b(x, y, a) { arguments[2] = 10; alert(a); } b(1, 2, 3);
關於這個題目,ECMAsCRIPT 262-3的規範有解釋的。
活動對象是在進入函數上下文時刻被建立的,它經過函數的arguments屬性初始化。arguments屬性的值是Arguments對象.
關於 Arguments對象的具體定義,看這裏:ECMAScript arguments 對象
function a() { alert(this); } a.call(null);
這個題目能夠說是最簡單的,也是最詭異的!關於這個題目,咱們先來了解2個概念。
這個問題主要考察 Javascript 的 this 關鍵字,具體看這裏:
關於 a.call(null); 根據ECMAScript262規範規定:若是第一個參數傳入的對象調用者是null或者undefined的話,call方法將把全局對象(也就是window)做爲this的值。因此,無論你何時傳入null,其this都是全局對象window,因此該題目能夠理解成以下代碼:
function a() { alert(this); } a.call(window);
因此彈出的結果是[object Window]就很容易理解了。
—————
總結:
這5個題目雖然貌似有點偏,但實際上考察的依然是基本概念,只有熟知了這些基本概念才能寫出高質量代碼。
大體就這些,拋磚引玉。