记一道经典前端题
原文鏈接記一道經典前端題
題目如下:
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 復制代碼復制代碼
總結
- 上一篇: 【JS面试向】深入原型链之class的继
- 下一篇: ceph13 快速编译 v13.2.4