說說牛客上的一道 JavaScript 題目

原文:http://blog.e10t.net/talk-abo...javascript

牛客上有這麼一道 JavaScript 的題目java

//填寫內容讓下面代碼支持a.name = 「name1」; b.name = 「name2」;
function obj(name){
    【1】
}
obj.【2】 = "name2";
var a = obj("name1");
var b = new obj;

【1】和【2】是填寫的內容,【2】的答案是 prototype.name,沒爭議。面試

問題是【1】,參考答案竟然是 if(name){ this.name = name;}return this;,這麼隨便地玩弄 this 不就是明擺着污染全局變量嗎?暴力賦值不可取。安全

下面的一些高票討論還說了一大堆解釋的廢話,連他本身都說本身好羅嗦。對,你不但羅嗦,並且尚未改錯。註釋裏都說了給 window 的屬性賦值,還不自知出問題,真是誤人子弟。app

先來分析一下題目,a 和 b 都從 obj 來,爲何同名的屬性值不同?能夠看出,是對 obj 這個函數的調用方式不同,a 是 obj 函數的調用結果,而 b 則是 obj 做爲構造函數調用的結果。因此這題的重點應該是如何區分_函數調用_和_構造函數調用_。函數

一個關鍵字 new 決定了不一樣。new 的做用是什麼呢?MDN 上說了,面試也會考你的,簡單來講是三步,new foothis

  1. 生成一個繼承於 foo.prototype 的對象.net

  2. foo 會被調用,其中的 this 值會被綁定爲 1 中的對象prototype

  3. 若是 foo 沒有返回一個對象(注意是對象!),則返回 1 的對象code

從 2 就能夠看出 this 值會被 new 綁定爲一個肯定的對象,而不是像普通函數調用中那樣本身不可預料,要看上下文的進程。

因而就能夠在這裏作文章。先來判斷 this 的值。

if (this instanceof obj) {}

instanceof 會檢查 this 的原型鏈上是否存在 foo.prototype。也就是說能判斷是否知足第 1 條,確保了對象能從 prototype 中讀取到 name 屬性。(畢竟代碼中並無給 b 的賦值中傳入)

instanceof 並非完美的判斷方法,可是在這裏足夠了,後面會談到這個問題。

if (this instanceof obj) {
    // new 調用
} else {
    // 非 new 調用
    return {
        name: name
    }
}

非 new 調用的狀況下,直接返回一個新對象就 OK 了。

而在 new 調用的狀況下,能夠看到 function obj(name) 定義的時候是有參數的,調用的時候卻沒參數,這就要當心了,爲了安全起見,仍是判斷一下爲妙。

if (this instanceof obj) {
    // new 調用
    if (name !== undefined) {
        this.name = name
    }
} else {
    // 非 new 調用
    return {
        name: name
    }
}

通常來講,判斷會寫成 if (name),可是碰到 null0false 就 GG 了,因此仍是謹慎點吧。

問題到這裏就能夠比較完美地解答了。

bonus: instanceof 的問題

instanceof 會檢查 this 的原型鏈上是否存在 foo.prototype』,爲何說得這麼拗口,是由於須要表達出 instanceof 原本就不是真的用來檢測是否調用 new 的方法。

在題目裏面,要求的是 a 須要從原型鏈上讀取到特定的屬性值,因此 instanceof 的做用恰好在這裏能符合要求而已。

函數調用除了題目中的方法還有第三種方法,那就是 foo.callfoo.apply,並且也能爲函數指定 this 的值(因此還有 bind)。所以是存在方法調戲 instanceof 的。

foo.prototype.name = 'foo'
var midman = new foo('fake foo')
var a = foo.call(midman)
var b = foo.call(midman, 'b')
a  // undefined, WTF?!
b  // undefined, WTF?!

這裏的 foo 調用的方式是做爲函數來調用,可是爲 this 綁定的值是從 foonew 出來的,換句話說,其原型鏈上存在 foo.prototype,因而就騙過了 instanceof

因而 ES2015 來搭救你了,新增了一個 new.target。因而修改爲:

if (new.target !== undefined) {
    // new 調用
    if (name !== undefined) {
        this.name = name
    }
} else {
    // 非 new 調用
    return {
        name: name
    }
}
相關文章
相關標籤/搜索