這是我在Javascript微信公衆號上看到的一篇文章,以爲挺有意思的,因此轉載過來跟你們分享一下,同時,對這些題目也加上了一些我我的的理解,若是有不對的地方,請你們指正。面試
題目一數組
(function(){ return typeof arguments; })();
答案:Object瀏覽器
typeof
所返回的可能值爲:number
、string
、boolean
、undefined
、object
、function
。而咱們知道arguments
是一個僞數組,而僞數組也是一個對象,因此答案應該是Object
。那麼,若是把typeof arguments
改爲arguments instanceof Array
呢?微信
能夠看到,返回的是false
。所以,僞數組不是一個數組,只有在使用了Array.prototype.slice.call()
進行了轉換後,才能轉化成一個真數組。函數
題目二this
var f = function g(){ return 23; }; typeof g();
答案:會報錯(g()未定義)spa
這段代碼中混合了函數聲明和函數表達式的形式,而函數其實是綁定到了f上而不是g。可是這麼寫是否符合Javascript的要求呢?在《Javascript高級程序設計》中:prototype
也能夠同時使用函數聲明和函數表達式,例如:
var sum = function sum(){}
。不過,這種語法在safari中會致使錯誤。設計
既然在除了safari之外的瀏覽器能夠同時使用函數聲明和函數表達式來聲明函數,那麼爲何題目中的代碼會報錯呢?個人理解是,這樣寫的結果是致使了函數名g被包裝進了f中,變成了f的一個局部變量,只能在f函數內部訪問到g,我把代碼修改了一下:code
var f = function g(){ alert(g); return 23; }; f();
我在f函數內部訪問g,彈框彈出的內容爲function g(){ alert(g);return 23; }
,沒有報錯而且訪問成功,g的函數體內容與f是同樣的,只是函數名不同。
————————我是一條分割線——————————
咱們再來回顧一下函數聲明與函數表達式吧(由於在以前的一次面試中被問到了它們的區別,我一下沒打上來……因此仍是加強一下記憶)
函數聲明的形式:
function funcName(){ /*some code*/}
函數表達式的形式:
var funcName = function(){/*some code*/}
在《Javascript高級程序設計》中有一段話:
解析器在向執行環境中加載數據時,對函數聲明和函數表達式並不是一視同仁的。解析器會率先讀取函數聲明,並使其在執行任何代碼以前可用(能夠訪問);至於函數表達式,則必須等到解析器執行到它所在的代碼行,纔會真正被解釋執行。
也就是說,在函數聲明的代碼段以前訪問函數,是可以訪問到的(函數聲明提高)。而在函數表達式的代碼段以前訪問函數,則會發生錯誤,由於在執行到函數所在語句以前,函數表達式聲明的變量名中不會保存對函數的的引用。
除此以外函數聲明與函數表達式的語法實際上是等價的。
題目三:
(function(x){ delete x; return x; })(1);
答案:1
注意:delete是用來刪除對象的屬性,沒法刪除普通變量。
題目四
var y = 1, x = y = typeof x; x;
答案:undefined
由於賦值運算是從右往左的,所以題目中的代碼展開來等價於:
var y = 1; y = typeof x; var x = y; x;
第二行代碼在訪問x時,x並無聲明,值爲undefined,因此typeof x
也就是undefined
了。
題目五
(function f(f){ return typeof f(); })(function(){ return 1; });
答案:number
把代碼分開來看會比較清楚:
var baz = function(){ return 1; }; (function f(f){ return typeof f(); })(baz);
先將function(){ return 1; }
賦值給baz
,則baz()
應該是1。再把baz
當成參數傳入到當即執行函數中,當即執行函數返回的是傳入的參數的類型,即baz()
的類型,baz()
返回1類型爲number
。
題目六
var foo = { bar: function() { return this.baz; }, baz: 1 }; (function(){ return typeof arguments[0](); })(foo.bar);
答案:undefined
這裏關鍵要理解this
的所指。arguments[0]
即foo.bar
,而函數中的this
指的就是調用arguments[0]()
這個函數的對象,即window
對象。可是window
對象並無baz這個屬性,所以,返回的是undefined
。
題目七
var foo = { bar: function(){ return this.baz; }, baz: 1 } typeof (f = foo.bar)();
答案:undefined
將代碼改寫一些變成:
var foo = { bar: function(){ return this.baz; }, baz: 1 } var f = foo.bar; typeof f();
foo.bar
是一個函數,將foo.bar
賦值給了f
,那麼f
變成了一個函數名。直接調用f()
時,調用它的對象爲window
,而window
中並無baz
屬性,因此輸出的是undefined
。
題目八
var f = (function f(){ return "1"; }, function g(){ return 2; })(); typeof f;
答案:number
在這裏,咱們須要瞭解一下逗號操做符的做用:
在用於賦值時,逗號操做符總會返回表達式中的最後一項。
因此,這個當即執行函數返回的應該是後面那個函數的返回值,返回值爲2,類型爲number
。
題目九
var x = 1; if (function f(){}) { x += typeof f; } x;
答案:1undefined
首先,第一個問題是:函數在判斷語句中判斷的結果是true仍是false?,咱們能夠運行一下如下代碼:
Boolean(function f(){});//true
將一個空函數轉換爲布爾型,結果爲true,因此題中的代碼是可以進入if語句的。那麼第二個問題是:爲何typeof f
會是undefined
?由於if
判斷條件中並不能真正的聲明一個函數,所以,f
是一個沒有被聲明過的函數名,它的類型爲undefined
。
題目十
(function f(){ function f(){ return 1; } return f(); function f(){ return 2; } })();
答案:2
咱們在題目四里說過:Javascript解析器會率先讀取函數聲明,使它在任何代碼之間可用。所以,在外層函數f
函數體內的兩個函數聲明,都會提高到return f()
以前執行。而又由於Javascript沒有重載,所以以最後執行的同名函數輸出結果爲準。
題目十一
function f(){ return f; } new f() instanceof f;
答案:false
一、首先咱們要了解instanceof
的做用:instanceof
是用來檢測變量是不是給定引用類型的實例。
二、而後再看f()
函數體中return f
,返回了本身。
三、經過new
來調用構造函數生成實例的過程爲:1)建立一個空對象;2)將類的prototype中的屬性和方法複製到實例中;3)將第一步建立的空對象作爲類的參數調用類的構造函數;4)返回一個新的實例對象。
然而,在以new
形式調用構造函數f()
時,f()
返回了自身,將new
返回的實例對象覆蓋掉了,所以,new
以後的結果不是f
的一個引用了。
題目十二
var x = [typeof x, typeof y][1]; typeof typeof x;
答案:string
很明顯的能夠看出typeof x = undefined
和typeof y = undefined
,那麼x
是undefined
。其實其餘的都不看,光看typeof typeof
就能肯定輸出的是string
了。
題目十三
(function(foo){ return typeof foo.bar; })({ foo: { bar: 1 } });
答案:undefined
這道題目有一個小陷阱,一不注意還真被忽悠進去了。咱們仔細看一個參數傳入的內容最外面有一對{}
,那麼咱們把題目修改一下就一目瞭然了:
var temp = { foo: { bar: 1 } }; (function(foo){ return typeof foo.bar; })(temp);
傳入的參數爲temp這個對象,所以函數中的foo.bar
實際上爲temp.bar
,可是temp
裏只有foo一個屬性,沒有bar,因此應該是undefined
。