前端工程師有時候面試時會遇到一類面試官,他們問的問題對於語言自己很是較真兒,每每不是候選人可能期待的面向實際的問題(有些候選人強調能幹活就行,至於知不知道其中原因是無關痛癢的)。這類題目,雖然沒有邏輯,但某種程度說,確實考察了候選人對於javascript
這門語言的理解。javascript
忽然想到這個話題是無聊在翻本身的Github,看看之前都寫過什麼醜貨。而後翻到了這篇解釋Javascript quiz的文章quiz-legend,反正沒事兒,就想搬過來供你們學習、理解、背誦、批判。前端
(function(){ return typeof arguments;//"object" })();
arguments
是一個Array-like對象,對應的就是傳入函數的參數列表。你能夠在任何函數中直接使用該變量。java
typeof
操做符只會返回string
類型的結果。參照以下列表可知對應不一樣數據,typeof
返回的值都是什麼:git
類型 | 結果 |
---|---|
undefined |
'undefined' |
null |
'object' |
Boolean |
'boolean' |
Number |
'number' |
String |
'string' |
Symbol (new in ECMAScript 2015) | 'symbol' |
Host object (provided by the JS environment) | Implementation-dependent |
Function object (implements [[Call]] in ECMA-262 terms) | 'function' |
Any other object | 'object' |
由此咱們推斷出,
typeof arguments
是object
github
var f = function g(){ return 23; }; typeof g();//報錯
這是一個名字是g
的function expression,而後又被賦值給了變量f
。面試
這裏的函數名g
和被其賦值的變量f
有以下差別:express
函數名g
不能變更,而變量f
能夠被從新賦值數組
函數名g
只能在函數體內部被使用,試圖在函數外部使用g
會報錯的前端工程師
(function(x){ delete x; return x;//1 })(1);
delete
操做符能夠從對象中刪除屬性,正確用法以下:ide
delete object.property delete object['property']
delete
操做符只能做用在對象的屬性上,對變量和函數名無效。也就是說delete x
是沒有意義的。
你最好也知道,
delete
是不會直接釋放內存的,她只是間接的中斷對象引用
var y = 1, x = y = typeof x; x;//"undefined"
咱們試圖分解上述代碼成下面兩步:
var y = 1; //step 1 var x = y = typeof x; //step 2
第一步應該沒有異議,咱們直接看第二步
賦值表達式從右向左執行
y
被從新賦值爲typeof x
的結果,也就是undefined
x
被賦值爲右邊表達式(y = typeof x
)的結果,也就是undefined
(function f(f){ return typeof f();//"number" })(function(){ return 1; });
直接上註釋解釋:
(function f(f){ //這裏的f是傳入的參數function(){ return 1; } //執行的結果天然是1 return typeof f(); //因此根據問題一的表格咱們知道,typeof 1結果是"number" })(function(){ return 1; });
var foo = { bar: function() { return this.baz; }, baz: 1 }; (function(){ return typeof arguments[0]();//"undefined" })(foo.bar);
這裏你可能會誤覺得最終結果是number
。向函數中傳遞參數能夠看做是一種賦值,因此arguments[0]
獲得是是真正的bar
函數的值,而不是foo.bar
這個引用,那麼天然this
也就不會指向foo
,而是window
了。
var foo = { bar: function(){ return this.baz; }, baz: 1 } typeof (f = foo.bar)();//"undefined"
這和上一題是同樣的問題,(f = foo.bar)
返回的就是bar
的值,而不是其引用,那麼this
也就指的不是foo
了。
var f = (function f(){ return '1'; }, function g(){ return 2; })(); typeof f;//"number"
逗號操做符 對它的每一個操做對象求值(從左至右),而後返回最後一個操做對象的值
因此(function f(){ return '1'; }, function g(){ return 2; })
的返回值就是函數g
,而後執行她,那麼結果是2
;最後再typeof 2
,根據問題一的表格,結果天然是number
var x = 1; if (function f(){}) { x += typeof f; } x;//"1undefined"
這個問題的關鍵點,咱們在問題二中談到過,function expression
中的函數名f
是不能在函數體外部訪問的
var x = [typeof x, typeof y][1]; typeof typeof x;//"string"
由於沒有聲明過變量y
,因此typeof y
返回"undefined"
將typeof y
的結果賦值給x
,也就是說x
如今是"undefined"
而後typeof x
固然是"string"
最後typeof "string"
的結果天然仍是"string"
(function(foo){ return typeof foo.bar;//"undefined" })({ foo: { bar: 1 } });
這是個純粹的視覺詭計,上註釋
(function(foo){ //這裏的foo,是{ foo: { bar: 1 } },並無bar屬性哦。 //bar屬性是在foo.foo下面 //因此這裏結果是"undefined" return typeof foo.bar; })({ foo: { bar: 1 } });
(function f(){ function f(){ return 1; } return f();//2 function f(){ return 2; } })();
經過function declaration
聲明的函數甚至能夠在聲明以前使用,這種特性咱們稱之爲hoisting。因而上述代碼實際上是這樣被運行環境解釋的:
(function f(){ function f(){ return 1; } function f(){ return 2; } return f(); })();
function f(){ return f; } new f() instanceof f;//false
當代碼new f()
執行時,下面事情將會發生:
一個新對象被建立。它繼承自f.prototype
構造函數f
被執行。執行的時候,相應的傳參會被傳入,同時上下文(this
)會被指定爲這個新實例。new f
等同於new f()
,只能用在不傳遞任何參數的狀況。
若是構造函數返回了一個「對象」,那麼這個對象會取代整個new
出來的結果。若是構造函數沒有返回對象,那麼new
出來的結果爲步驟1建立的對象,
ps:通常狀況下構造函數不返回任何值,不過用戶若是想覆蓋這個返回值,能夠本身選擇返回一個普通對象來覆蓋。固然,返回數組也會覆蓋,由於數組也是對象。
因而,咱們這裏的new f()
返回的仍然是函數f
自己,而並不是他的實例
with (function(x, undefined){}) length;//2
with
語句將某個對象添加的做用域鏈的頂部,若是在statement
中有某個未使用命名空間的變量,跟做用域鏈中的某個屬性同名,則這個變量將指向這個屬性值。若是沒有同名的屬性,則將拋出ReferenceError
異常。
OK,如今咱們來看,因爲function(x, undefined){}
是一個匿名函數表達式,是函數,就會有length
屬性,指的就是函數的參數個數。因此最終結果就是2
了
有人以爲這些題坑爹,也有人以爲開闊了眼界,見仁見智吧。但有一件事是真的,不管你是否堅決的實踐派,缺了理論基礎,也鐵定走不遠 - 你永遠不會見到哪一個熟練的技術工人忽然成了火箭專家。
看文檔、讀標準、結合實踐,纔是同志們的決勝之道