記一道經典前端題

題目以下:node

function Foo() {
    getName = function() { alert(1); }
    return this
}
Foo.getName = function() { alert(2); }
Foo.prototype.getName = function() { alert(3); }
var getName = function () { alert(4); }
function getName() { alert(5); }

// 輸出值
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName()
new Foo().getName()
new new Foo().getName()

// 輸出結果爲
// 2
// 4
// 1
// 1
// 2
// 3
// 3
複製代碼

下面對輸出值進行分析:函數

  1. Foo.getName() 輸出爲 2, 訪問的是函數 Foo 上的靜態屬性,輸出爲 2。優化

    如今,嘗試在函數內定義 getName 函數和在 Foo 原型上綁定 getName 函數, 都沒法成功執行 Foo.getName(), 而以字面量建立對象的方式建立對象後,則能正常的執行 getName() 函數。ui

嘗試

經過建立對象運行 getName() 定義在函數內部沒法正常執行,即便它是全局變量。this

嘗試

結論: 因爲函數自己是對象,經過函數綁定屬性和方法屬於靜態方法 ,能夠直接調用。綁定在原型上的屬性和方法要建立對象後才能調用,在構造函數對象內部定義的方法沒法經過對象調用。spa

  1. getName(); 結果輸出爲 4,而不是輸出 5。這是由於JS 存在變量聲明提高(全部聲明的變量或聲明的函數都會被提高到當前函數的頂部)。prototype

    故代碼執行順序爲:code

    var getName;
    function getName() { alert(5); }
    // ... 省略代碼
    getName = function () { alert(4) }
    複製代碼

    最終執行 getName 輸出爲 4cdn

    延伸題目:對象

    console.log( Foo )
    function Foo() {
        console.log(1);
    }
    var Foo = 1
    複製代碼

    Foo 的輸出結果爲?

  2. Foo().getName(); 輸出值爲1, 先執行 Foo() 函數,定義全局變量 getName, 以後調用全局對象的 getName() 方法, 返回 1。

    注意, Foo() 函數返回的 this 指向的是全局對象 window,因此調用的是全局對象 getName()。 函數裏的 getName 綁定的是全局對象,經過 Foo 調用會報錯。

    Node 下執行這條語句會報錯,由於 node 沒有全局對象 window, 因此沒法調用 getName

  3. getName() 調用全局函數, 由於執行 Foo(), 更新了 getName 的值,因此返回 1。

  4. new Foo.getName() 考察了運算符的優先級。 . 的優先級高於 new, 至關於執行 new (Foo.getName)(), 至關於執行 getName 的構造函數,返回 2

  5. new Foo().getName() 執行方式爲 (new Foo()).getName() 先生成 Foo 對象, 再執行 getName() 函數。 在 new Foo() 返回的是新建立的空對象,因爲對象這時還沒綁定屬性 getName, 因此這時調用的是原型上的 getName, 結果返回3

    注意: 構造函數 return this,在執行 new 的時候,返回的是新建立的對象。

    延伸題目:

    function A() {
        this.a = 2;
        function B() {
            this.a = 1;
        }
        return B();
    }
    console.log(new A());
    複製代碼

    a的值是? 若是 return new B(); a的值是?

  6. new new Foo().getName() 能夠改寫爲 new ((new Foo()).getName)() 先初始化實例,而後將原型對象上的 getName() 做爲構造函數執行,結果返回 3

最終代碼能夠優化爲

var getName;
function getName() { alert(5); }
function Foo() {
    getName = function() { alert(1); }
    return this
}
Foo.getName = function() { alert(2); }
Foo.prototype.getName = function() { alert(3); }
getName = function () { alert(4); }

Foo.getName(); // 2
getName(); // 4
Foo();
getName(); // 1
getName(); // 1
Foo.getName(); // 2
(new Foo()).getName(); // 3
(new Foo()).getName(); // 3
複製代碼
相關文章
相關標籤/搜索