摘要:第二問直接調用函數,相當于調用,那么與函數無關。第三問先執行了函數,然后調用函數的返回值對象的屬性函數。第四問再次調用函數時,此時函數已經被第三問執行時修改,所以結果為。
JavaScript函數調用的經典例題
很多初學JavaScript的人對函數的調用理解有些模糊的,話不多說,直接上題:
function Foo() { getName = function () { console.log(1); }; return this; } Foo.getName = function () { console.log(2); }; Foo.prototype.getName = function () { console.log(3); }; var getName = function () { console.log(4); }; function getName() { console.log(5); }
請寫出以下每行代碼的輸出結果
Foo.getName(); getName(); Foo().getName(); getName(); new Foo.getName(); new Foo().getName();
此題涉及的知識點比較廣泛,包括變量的作用域、變量提升、this指針指向、運算符優先級、原型、繼承、等等。
解題之前,我們先分析一下代碼:
//① 函數聲明,聲明了一個Foo函數,里面有個全局變量getName指向一個匿名函數 function Foo() { getName = function () { console.log(1); }; return this; } //② Foo函數的創建了一個靜態屬性getName指向一個匿名函數 Foo.getName = function () { console.log(2); }; //③ Foo函數的原型上創建了一個getName方法 Foo.prototype.getName = function () { console.log(3); }; //④ 函數表達式,定義一個變量getName指向一個匿名函數 var getName = function () { console.log(4); }; //⑤ 函數聲明,聲明了一個叫getName的有名函數 function getName() { console.log(5); }第一問
Foo.getName(); //2
訪問Foo函數上存儲的靜態屬性getName,所以結果為2。
getName(); //4
直接調用getName函數,相當于調用window.getName,那么與函數①、②、③無關。在分析題目前我們首先知道什么變量提升以及JS引擎工作的執行順序。
變量提升
所有的變量聲明都會被提升到它所在作用域的最開始的部分
例如:
console.log(a); //undifined var a = 10;
js引擎執行代碼的順序為:
var a; console.log(a); a = 10;
例如:
var a = 10; function test(){ conlose.log(a); //undifined var a = 20; } test();
js引擎執行代碼的順序為:
var a; a = 10; function test(){ var a; conlose.log(a); //undifined a = 20; }
JS引擎工作的執行順序:
解析器會預先讀取函數聲明和變量聲明,并使其在執行任何代碼前可以訪問;
再由上而下逐行執行,函數表達式必須等到解析器執行到它所在的代碼行才會真正被解釋執行
我們再看原題,函數④的變量會被提升,再結合上述JS引擎工作的執行順序,因此代碼的執行順序是:
function Foo(){ getName = function(){ console.log(1); }; return this; } var getName; function getName(){ console.log(5); } Foo.getName = function(){ console.log(2); }; Foo.prototype.getName = functio() { console.log(3); }; getName = function(){ console.log(4); //最終的賦值覆蓋function getName聲明 };
所以結果為4。
第三問Foo().getName(); //1
Foo().getName(); 先執行了Foo函數,然后調用Foo函數的返回值對象的getName屬性函數。
先看前面的Foo()
顯然調用了函數①,Foo函數第一句getName = function(){alert(1);};是一句賦值語句,因為它沒有var聲明,所以先向當前Foo函數作用域內尋找getName變量,沒有。再向當前函數作用域上層,即外層作用域內尋找是否含有getName變量,找到函數④var getName=function(){alert(1)};,將此變量getName賦值為function(){alert(1)}。
我們將相關的代碼多帶帶拿出:
function Foo() { getName = function () { //全局變量 console.log(1); }; } var getName = function () { console.log(4); }; Foo(); console.log(getName); //function(){console.log(1); }
實質上就是將外層作用域內的getName函數修改了。
再看Foo().getName()
Foo函數的返回值結果是this那么Foo().getName()=>this.getName()。
this的指向在函數定義的時候是確定不了的,只有函數被調用執行時才能確定this到底指向誰,誰調用指向誰。而此處是直接調用,this指向window對象。
this.getName()=> window.getName(),而window中的getName已經被修改為console.log(1),所以最終會輸出1。
此處考察了兩個知識點,一個是變量作用域,一個是this指向。
關于變量作用域,如果將代碼變為:
function Foo() { var getName = function () { // 局部變量,只能在函數內部訪問 console.log(1); }; } var getName = function () { console.log(4); }; Foo(); console.log(getName);//function(){console.log(4);}
那么此題結果則為4了。
第四問getName(); //1
再次調用getName函數時,此時函數④已經被第三問執行Foo()時修改,所以結果為1。
new Foo.getName(); //2
--> new(Foo.getName)()
此處考察的是js的運算符優先級。
.的優先級大于new,因此應該先執行Foo.getName(),那么執行函數②,結果是2,然后new了Foo的實例對象即new(Foo.getName)()。
最終結果為2。
new Foo().getName();//3
--> (new Foo()).getName()
先執行new Foo(),那么執行函數①,返回this,this在構造函數中代表當前實例化對象,所以Foo函數產生一個新的實例對象。
之后調用實例對象的getName方法即(new Foo()).getName()=>實例對象.getName()。
接著我們就要說到構造函數和原型實例和實例對象。
實例對象能夠調用的方法和屬性只能是定義在自身函數內方法和繼承了構造函數原型中的方法和屬性。
實例對象.getName()本身并沒有定義屬性和方法,則繼承其構造函數Foo原型中的方法,執行函數③,結果為3。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/95109.html
摘要:如果有時需要得到函數內的局部變量。上面代碼中,函數就在函數內部,這時內部的所有局部變量,對都是可見的。所謂內存泄漏指任何對象在您不再擁有或需要它之后仍然存在。閉包不能濫用,否則會導致內存泄露,影響網頁的性能。 一、引子 閉包(closure)是 Javascript 語言的一個難點,面試時常被問及,也是它的特色,很多高級應用都要依靠閉包實現。本文盡可能用簡單易懂的話,講清楚閉包的概念、...
摘要:最后重點理解結論箭頭函數的,總是指向定義時所在的對象,而不是運行時所在的對象。輸出,箭頭函數不會綁定所以傳入指向無效。原因是,要徹底理解應該是建立在已經大致理解了中的執行上下文,作用域作用域鏈,閉包,變量對象,函數執行過程的基礎上。 本文共 2025 字,看完只需 8 分鐘 概述 前面的文章講解了 JavaScript 中的執行上下文,作用域,變量對象,this 的相關原理,但是我...
摘要:因此二維數組的實質是一維數組,但是其元素類型是一維數組對數組地址解引用后得到是數組名數組首元素地址例題經典再現二維數組練習字符數組希望本文對你有所幫助 文章目錄 ...
閱讀 1035·2023-04-26 02:26
閱讀 2148·2021-09-26 10:16
閱讀 1554·2019-08-30 12:57
閱讀 3468·2019-08-29 16:10
閱讀 3222·2019-08-29 13:47
閱讀 1189·2019-08-29 13:12
閱讀 2141·2019-08-29 11:11
閱讀 1337·2019-08-26 13:28