以前看過一套 JavaScript 題目,借花獻佛拿出來分享一下。附帶了自己的理解和答案。有爭議的地方,歡迎大家指出和討論。
題目一
1 2 3 |
(function(){ return typeof arguments; })(); |
答案:“object”
arguments是物件,偽陣列有兩件事要注意這裡:
引數不是陣列,它是一個陣列一樣的物體,你可以使用方括號和整數索引的元素,但方法通常可在一個如推上不存在引數陣列
Array.prototype.slice.call(arguments); 轉成陣列
當然arguments即使是陣列,返回的依然是”object”,因為陣列也是物件,附加:typeof 對型別的判斷
https://developer.mozilla.org/zh-CN/docs/JavaScript/Reference/Operators/typeof
arguments是物件,偽陣列有兩件事要注意這裡:
引數不是陣列,它是一個陣列一樣的物體,你可以使用方括號和整數索引的元素,但方法通常可在一個如推上不存在引數陣列
Array.prototype.slice.call(arguments); 轉成陣列
當然arguments即使是陣列,返回的依然是”object”,因為陣列也是物件,附加:typeof 對型別的判斷
https://developer.mozilla.org/zh-CN/docs/JavaScript/Reference/Operators/typeof
題目二
1 2 |
var f = function g(){ return 23; }; typeof g(); |
答案:會發生錯誤
因為function g(){ return 23; }是函式表示式,事實上只有事一個名字,不是一個函式宣告
函式實際上是繫結到變數f,不是g.
指定的識別符號在函式表示式雖然有其用途:堆疊跟蹤是清晰而不是充斥著無名的函式,你可以有一個匿名函式遞迴呼叫本身不使用argument.callee
附非常詳細的帖子函式表示式
http://kangax.github.io/nfe/
因為function g(){ return 23; }是函式表示式,事實上只有事一個名字,不是一個函式宣告
函式實際上是繫結到變數f,不是g.
指定的識別符號在函式表示式雖然有其用途:堆疊跟蹤是清晰而不是充斥著無名的函式,你可以有一個匿名函式遞迴呼叫本身不使用argument.callee
附非常詳細的帖子函式表示式
http://kangax.github.io/nfe/
題目三
1 2 3 4 |
(function(x){ delete x; return x; })(1); |
答案:1
引數不可刪除
見我發過的帖子(js中的delete定義)
http://www.cnblogs.com/aaronjs/articles/3148934.html
國外的帖子(理解刪除)
http://perfectionkills.com/understanding-delete/
引數不可刪除
見我發過的帖子(js中的delete定義)
http://www.cnblogs.com/aaronjs/articles/3148934.html
國外的帖子(理解刪除)
http://perfectionkills.com/understanding-delete/
題目四
1 2 |
var y = 1, x = y = typeof x; x; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
答案:"undefined" 通過重寫程式碼如下結果: var a, b; 展開就是 var a; var b;. A = B = C;相當於 B = C = B; 知道了這一點,我們重寫並得到: var y = 1; y = typeof x; var x = y; x; 當執行 y = typeof x時,x 還沒有被定義,所以y成為字串"undefined",然後被分配到x |
題目五
1 2 3 |
(function f(f){ return typeof f(); })(function(){ return 1; }); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
答案:"number" 為了便於理解我們繼續分解: 第一部分 var baz = function(){ return 1; }; 第二部分 (function f(f){ return typeof f(); })(baz); 在這裡,函式f接受一個引數是另一個函式,f函式內部執行這個實參函式並且返回型別 無論是從呼叫該函式返回,即使引數名稱f與函式名衝突,函式接受本身作為自己的引數,然後呼叫,此時就看誰更具有更高的優先順序了,顯然,引數的優先順序更高,所以實際執行的是return typeof 1 |
題目六
1 2 3 4 5 6 7 8 |
var foo = { bar: function() { return this.baz; }, baz: 1 }; (function(){ return typeof arguments[0](); })(foo.bar); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
答案: "undefined" 為什麼是"undefined"?. 我們必須要知道this運算子是怎麼工作的. JS語言精粹總結的很精煉: 1 純粹的函式呼叫 2 作為物件方法的呼叫 3 作為建構函式呼叫 4 apply呼叫 我們看看題目是屬於那種環境? 在arguments[0]()中執行了一個方法,arguments[0]就是foo.bar方法 注意:這在foo.bar中的this是沒有繫結到foo 雖然 foo.bar 傳遞給了函式,但是真正執行的時候,函式 bar 的上下文環境是 arguments ,並不是 foo arguemnts[0] 可以理解為 arguments.0(不過寫程式碼就不要這樣了,語法會錯誤的),所以這樣看來,上下文環境是 arguemnts 就沒問題了,所以在執行baz的時候自然this就是window了,window 上沒有baz屬性,返回的就是undefined, typeof呼叫的話就轉換成"undefined"了 附上博文 http://www.cnblogs.com/aaronjs/archive/2011/09/02/2164009.html MDC https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Operators/Special_Operators/this_Operator#Description |
題目七
1 2 3 4 5 |
var foo = { bar: function(){ return this.baz; }, baz: 1 } typeof (f = foo.bar)(); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
答案 "undefined" 繼續改寫一下: var foo = { bar: function(){ return this.baz; }, baz: 1 } f = foo.bar; typeof f(); 把foo.bar儲存給f然後呼叫,所以this在foo.bar引用的是全域性物件,所以就沒有baz屬性了 換句話說 foo.bar執行的時候上下文是 foo,但是當 把 foo.bar 賦值給 f 的時候,f 的上下文環境是 window ,是沒有 baz 的,所以是 ”undefined" |
題目八
1 2 |
var f = (function f(){ return "1"; }, function g(){ return 2; })(); typeof f; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
答案 "number" 逗號操作符的使用可以很混淆,但這段說明它的行為: var x = (1, 2, 3); x; x的值是3,這表明,當你有一系列的組合在一起,並由逗號分隔的表示式,它們從左到右進行計算,但只有最後一個表示式的結果儲存。由於同樣的原因,這個問題可以改寫為減少混亂: var f = (function g(){ return 2; })(); typeof f; 關於逗號表示式: 原文: http://www.2ality.com/2012/09/expressions-vs-statements.html 譯文: http://www.cnblogs.com/ziyunfei/archive/2012/09/16/2687589.html |
題目九
1 2 3 4 5 |
var x = 1; if (function f(){}) { x += typeof f; } x; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
答案 "1undefined" 這裡有個難點 if 中的 function f(){} 要如何處理? 函式宣告的實際規則如下: 函式宣告只能出現在程式或函式體內。從句法上講,它們 不能出現在Block(塊)({ ... })中,例如不能出現在 if、while 或 for 語句中。因為 Block(塊) 中只能包含Statement語句, 而不能包含函式宣告這樣的源元素。另一方面,仔細看一看規則也會發現,唯一可能讓表示式出現在Block(塊)中情形,就是讓它作為表示式語句的一部分。但是,規範明確規定了表示式語句不能以關鍵字function開頭。而這實際上就是說,函式表示式同樣也不能出現在Statement語句或Block(塊)中(因為Block(塊)就是由Statement語句構成的)。 假設程式碼我們不妨變一下: var x = 1; if (function(){}) { x += typeof f; } x; var x = 1; x += typeof f; x; f在這了沒有被定義,所以typeof f 是字串"undefined" ,字元與數字相加結果也是一個字串, 所以最後的x就是"1undefined"了 |
題目十
1 2 3 4 5 |
(function f(){ function f(){ return 1; } return f(); function f(){ return 2; } })(); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
答案 2 如果是一直看下來的話,這個題目應該是比較簡單 簡單的來說在執行return之前,函式宣告會在任何表示式被解析和求值之前先被解析和求值, 即使你的宣告在程式碼的最後一行,它也會在同作用域內第一個表示式之前被解析/求值, 參考如下例子,函式fn是在alert之後宣告的,但是在alert執行的時候,fn已經有定義了 alert(fn()); function fn() { return 'Hello world!'; } 所以題目中函式提升了兩次,第二次把第一次覆蓋了, 所以 return 後面的 f 是 return 語句的下一條語句宣告的函式 f 。 注意自執行函式 (function f (){})(); 中的 f 並沒有函式提升效果,它是表示式 |
題目十一
1 2 |
function f(){ return f; } new f() instanceof f; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
答案 false 怎樣去理解? new f() 首先這個操作會建立一個新物件並呼叫建構函式函式這一新的物件作為它的當前上下文物件 簡單的說 new f(); 依稀記得高階程式設計裡面是這麼說的: 1 建立空物件。 2 將類的prototype中的屬性和方法複製到例項中。 3 將第一步建立的空物件做為類的引數呼叫類的建構函式 預設如果沒有覆蓋這個空物件的話,返回this var a = new Object; a instanceof Object 為 true 我們在看 f() 返回了 return f; 那麼也就是說這個新的物件是是自身,建構函式本身在 new 的過程中會返回一個表示該物件的例項。 但是函式的返回值覆蓋了這個例項,這個new 就形同虛設 果f的形式為 function f(){return this}或function f(){}就不一樣 var a = new f(); a instanceof f // false 值得注意的是 instanceof 檢測的是原型 又附上我部落格的 JS 物件機制深剖——new 運算子 http://www.cnblogs.com/aaronjs/archive/2012/07/04/2575570.html |
題目十二
1 2 |
var x = [typeof x, typeof y][1]; typeof typeof x; |
1 2 3 4 5 |
這題目比較簡單,注意下返回型別即可 x = [,][1]; 即 x = typeof y = 'undefind'. typeof 返回的是string型別就可以了 typeof typeof必然就是'string'了. |
題目十三
1 2 3 |
function(foo){ return typeof foo.bar; })({ foo: { bar: 1 } }); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
答案 "undefined" 又是一個噁心的題目,純文字遊戲,大家看仔細看 先分解一下 var baz = { foo: { bar: 1 } }; (function(foo){ return typeof foo.bar; })(baz); 去掉函式關聯 var baz = { foo: { bar: 1 } }; var foo = baz; typeof foo.bar; 最後,通過替代我們除去中間變數foo var baz = { foo: { bar: 1 } }; typeof baz.bar; 所以現在就很清晰了,屬性中沒有定義baz;它被定義為baz.foo上了,所以結果是:”undefined" |
題目十四
1 |
with (function(x, undefined){}) length; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
答案 2 with用得很少,with 語句就是用於暫修改作用域鏈的或者通常用來縮短特定情形下必須寫的程式碼量 使用with語句的JavaScript程式碼很難優化,因此它的運算速度比不使用with語句的等價程式碼要慢得多。 而且,在with語句中的函式定義和變數初始化可能會產生令人驚訝的、相牴觸的行為,因此我們避免使用with語句 with的用法是這樣的: with(object) {},在大括號裡面,可以引用object的屬性而不用使用object.attr這種形式。 這道題裡面,with接受了一個物件,只不過這個物件是函式,函式有length屬性, 代表形參的個數,所以上面返回的值是2 mozilla with http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Statements:with |