題目如下:
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
複製程式碼
下面對輸出值進行分析:
-
Foo.getName()
輸出為 2, 訪問的是函式 Foo 上的靜態屬性,輸出為 2。現在,嘗試在函式內定義 getName 函式和在 Foo 原型上繫結 getName 函式, 都無法成功執行
Foo.getName()
, 而以字面量建立物件的方式建立物件後,則能正常的執行getName()
函式。
通過建立物件執行 getName()
定義在函式內部無法正常執行,即使它是全域性變數。
結論: 由於函式本身是物件,通過函式繫結屬性和方法屬於靜態方法 ,可以直接呼叫。繫結在原型上的屬性和方法要建立物件後才能呼叫,在建構函式物件內部定義的方法無法通過物件呼叫。
-
getName();
結果輸出為 4,而不是輸出 5。這是因為JS 存在變數宣告提升(所有宣告的變數或宣告的函式都會被提升到當前函式的頂部)。故程式碼執行順序為:
var getName; function getName() { alert(5); } // ... 省略程式碼 getName = function () { alert(4) } 複製程式碼
最終執行
getName
輸出為 4延伸題目:
console.log( Foo ) function Foo() { console.log(1); } var Foo = 1 複製程式碼
Foo 的輸出結果為?
-
Foo().getName();
輸出值為1, 先執行 Foo() 函式,定義全域性變數 getName, 之後呼叫全域性物件的getName()
方法, 返回 1。注意,
Foo()
函式返回的this
指向的是全域性物件 window,所以呼叫的是全域性物件getName()
。 函式裡的 getName 繫結的是全域性物件,通過 Foo 呼叫會報錯。Node 下執行這條語句會報錯,因為 node 沒有全域性物件 window, 所以無法呼叫 getName
-
getName()
呼叫全域性函式, 因為執行Foo()
, 更新了 getName 的值,所以返回 1。 -
new Foo.getName()
考察了運算子的優先順序。.
的優先順序高於 new, 相當於執行new (Foo.getName)()
, 相當於執行 getName 的建構函式,返回 2 -
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的值是? -
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
複製程式碼