摘要:檢查當前上下文中的參數(shù),建立該對象下的屬性與屬性值。檢查當前上下文的函數(shù)聲明,也就是使用關(guān)鍵字聲明的函數(shù)。如果該變量名的屬性已經(jīng)存在,為了防止同名的函數(shù)被修改為,則會直接跳過,原屬性值不會被修改。
上一篇:《javascript高級程序設(shè)計》筆記:內(nèi)存與執(zhí)行環(huán)境
上篇文章中說到:
(1)當執(zhí)行流進入函數(shù)時,對應(yīng)的執(zhí)行環(huán)境就會生成
(2)執(zhí)行環(huán)境創(chuàng)建時會生成變量對象,確定作用域鏈,確定this指向
(2)每個執(zhí)行環(huán)境都有一個與之關(guān)聯(lián)的變量對象,環(huán)境中定義的所有變量和函數(shù)都保存在這個對象中
變量對象是在進入執(zhí)行環(huán)境就確定下來的,顧名思義,變量對象是用于存儲在這個執(zhí)行環(huán)境中的所有變量和函數(shù)的一個對象。只是這個對象是用于解析器處理數(shù)據(jù)時使用,我們無法直接調(diào)用
下圖描述了執(zhí)行流在執(zhí)行環(huán)境中的執(zhí)行過程(執(zhí)行環(huán)境的生命周期)
(1)建立arguments對象。檢查當前上下文中的參數(shù),建立該對象下的屬性與屬性值。
(2)檢查當前上下文的函數(shù)聲明,也就是使用function關(guān)鍵字聲明的函數(shù)。在變量對象中以函數(shù)名建立一個屬性,屬性值為指向該函數(shù)所在內(nèi)存地址的引用。如果函數(shù)名的屬性已經(jīng)存在,那么該屬性將會被新的引用所覆蓋。
(3)檢查當前上下文中的變量聲明,每找到一個變量聲明,就在變量對象中以變量名建立一個屬性,屬性值為undefined。如果該變量名的屬性已經(jīng)存在,為了防止同名的函數(shù)被修改為undefined,則會直接跳過,原屬性值不會被修改。
總之:function聲明會比var聲明優(yōu)先級更高一點
下面通過具體的例子來看變量對象:
function test() { console.log(a); console.log(foo()); var a = 1; function foo() { return 2; } } test();
當執(zhí)行到test()時會生成執(zhí)行環(huán)境testEC,具體形式如下:
// 創(chuàng)建過程 testEC = { // 變量對象(variable object) VO: {}, // 作用域鏈 scopeChain: [], // this指向 this: {} }
僅針對變量對象來具體展開:
VO = { // 傳參對象 arguments: {}, // 在testEC中定義的function foo: "", // 在testEC中定義的var a: undefined }
未進入執(zhí)行階段之前,變量對象中的屬性都不能訪問!但是進入執(zhí)行階段之后,變量對象轉(zhuǎn)變?yōu)榱嘶顒訉ο?,里面的屬性都能被訪問了,然后開始進行執(zhí)行階段的操作
// 執(zhí)行階段 VO -> AO // Active Object AO = { arguments: {...}, foo: function(){return 2}, a: 1 }
最后我們將變量對象創(chuàng)建時的VO和執(zhí)行階段的AO整合到一起就可以得到整個執(zhí)行環(huán)境中代碼的執(zhí)行順序:
function test() { function foo() { return 2; } var a; console.log(a);// undefined console.log(foo());// 2 a = 1; } test();
這個就是分步變量對象的創(chuàng)建和執(zhí)行階段來解讀預解析
2. 預解析預解析分為變量提升和函數(shù)提升
變量提升:提升的是當前變量的聲明,賦值還保留在原來的位置
函數(shù)提升:函數(shù)聲明,可以認為是把整個函數(shù)體聲明了
函數(shù)表達式方式定義函數(shù)相當于變量聲明 var fn = function(){}
一個簡單的變量提升的例子
!function(){ console.log(a); var a = 1; }() // 實際執(zhí)行順序 !function(){ var a; console.log(a); a = 1; }()
技巧:
將執(zhí)行過程手動拆分成兩個步驟
1.創(chuàng)建階段:也稱作編譯階段,主要是變量的聲明和函數(shù)的定義(找var和function)
2.執(zhí)行階段:變量賦值和函數(shù)執(zhí)行
這樣看預解析還是比較容易的,但是涉及到命名沖突時,又是怎樣的情況呢?
(1)一個函數(shù)和一個變量出現(xiàn)同名
情況一:變量只聲明了,但沒有賦值
!function(){ var f; function f() {}; console.log(f); // f(){} }()
情況二:變量只聲明且賦值
!function(){ console.log(f); // f(){} var f = 123; function f() {}; console.log(f); // 123 }()
結(jié)論:
1.一個函數(shù)和一個變量出現(xiàn)同名,如果是變量只聲明了,但沒有賦值,變量名會被忽略
2.一個函數(shù)和一個變量出現(xiàn)同名,變量聲明且賦值,賦值前值為函數(shù),賦值后為變量的值
(2)兩個變量出現(xiàn)同名
!function(){ console.log(f); // undefined var f = 123; var f = 456; console.log(f); // 456 }()
結(jié)論:
兩個變量出現(xiàn)同名,重復的var聲明無效,會被忽略,只會起到賦值的作用,前面賦值會被后面的覆蓋
(3)兩個函數(shù)出現(xiàn)同名
!function(){ console.log(f); // function f(){return 456}; function f(){return 123}; function f(){return 456}; console.log(f); // function f(){return 456}; }()
結(jié)論:
兩個函數(shù)出現(xiàn)同名,由于javascript中函數(shù)沒有重載,前面的同名函數(shù)會被后面的覆蓋
(4)變量名與參數(shù)名相同
函數(shù)參數(shù)的本質(zhì)是什么?函數(shù)參數(shù)也是變量,相當于在該函數(shù)的執(zhí)行環(huán)境內(nèi)最頂部聲明了實參
情況一:參數(shù)為變量,與變量命名沖突
(function (a) { console.log(a); // 100 var a = 10; console.log(a); // 10 })(100); // 相當于 (function (a) { var a = 100; var a; // 重復聲明的var沒有意義,忽略 console.log(a); // 100 a = 10; console.log(a); // 10 })(100);
情況二:參數(shù)為函數(shù),與變量命名沖突
(function (a) { console.log(a); // function(){return 2} var a = 10; console.log(a); // 10 })(function(){return 2}); // 相當于 (function (a) { var a = function(){return 2}; var a; // 重復聲明的var沒有意義,忽略 console.log(a); // function(){return 2} a = 10; console.log(a); // 10 })(function(){return 2});
情況三:參數(shù)為空,與變量命名沖突
(function (a) { console.log(a); // undefined var a = 10; console.log(a); // 10 })(); // 相當于 (function (a) { var a; console.log(a); // undefined a = 10; console.log(a); // 10 })();
結(jié)論:
1.重名的是一個變量,傳入了參數(shù)(變量或函數(shù)):
如果是在變量賦值之前,此時獲取的是:參數(shù)的值!
如果是在變量賦值之后,此時獲取的是:變量的值!
2.重名的是一個變量,但是沒有傳入?yún)?shù):
如果是在變量賦值之前,此時獲取的是:undefined! 如果是在變量賦值之后,此時獲取的是:變量的值!
(5)函數(shù)名與參數(shù)名相同
情況一:參數(shù)為變量,與函數(shù)命名沖突
(function (a) { console.log(a); // a(){} function a(){}; console.log(a); // a(){} })(100); // 相當于 (function (a) { var a = 100; function a(){}; console.log(a); // a(){} console.log(a); // a(){} })(100);
情況二:參數(shù)為函數(shù),與函數(shù)命名沖突
(function (a) { console.log(a); // function a(){return 1} function a(){return 1} console.log(a); // function a(){return 1} })(function(){return 2}); // 相當于 (function (a) { var a = function(){return 2}; function a(){return 1}; console.log(a); // function a(){return 1} console.log(a); // function a(){return 1} })(function(){return 2});
情況三:參數(shù)為空,與函數(shù)命名沖突
(function (a) { console.log(a); // function a(){}; function a(){}; console.log(a); // function a(){}; })(); // 相當于 (function (a) { var a; function a(){}; console.log(a); // function a(){}; console.log(a); // function a(){}; })();
注意:函數(shù)表達式聲明方式var f = function(){},在預解析中與普通的變量聲明無異,因為預解析階段主要是通過var和function來區(qū)分函數(shù)與變量的
結(jié)論:
傳參為函數(shù)時,賦值前均為參數(shù)的值,賦值后為函數(shù)的值,傳空時均為函數(shù)的值
現(xiàn)在,我們回到變量對象中,在變量對象的創(chuàng)建階段(執(zhí)行流編譯階段),分別確定了arguments對象、函數(shù)聲明和變量聲明,其優(yōu)先級也是arguments>function>var,arguments就是參數(shù),反推上面變量提升的結(jié)論,同樣是成立的
經(jīng)典推薦:《javascript高級程序設(shè)計》筆記:原型圖解
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/89969.html
摘要:因此,所有在方法中定義的變量都是放在棧內(nèi)存中的當我們在程序中創(chuàng)建一個對象時,這個對象將被保存到運行時數(shù)據(jù)區(qū)中,以便反復利用因為對象的創(chuàng)建成本通常較大,這個運行時數(shù)據(jù)區(qū)就是堆內(nèi)存。 上一篇:《javascript高級程序設(shè)計》筆記:繼承近幾篇博客都會圍繞著圖中的知識點展開 showImg(https://segmentfault.com/img/bVY0C4?w=1330&h=618);...
摘要:建筑的頂層代表全局作用域。實際的塊級作用域遠不止如此塊級作用域函數(shù)作用域早期盛行的立即執(zhí)行函數(shù)就是為了形成塊級作用域,不污染全局。這便是閉包的特點吧經(jīng)典面試題下面的代碼輸出內(nèi)容答案個如何處理能夠輸出閉包方式方式下一篇你不知道的筆記 下一篇:《你不知道的javascript》筆記_this 寫在前面 這一系列的筆記是在《javascript高級程序設(shè)計》讀書筆記系列的升華版本,旨在將零碎...
摘要:數(shù)據(jù)類型中有種簡單數(shù)據(jù)類型也稱為基本數(shù)據(jù)類型和。在中非空字符串,非零數(shù)字,任意對象,都被認為。而空字符串,和,,認為是。用于表示整數(shù)和浮點數(shù)。標識符由數(shù)字字母下劃線美元符組成,但首字母不能是數(shù)字。變量方法對象命名推薦駝峰法。 JavaScript語法 一.語法簡介 因為JavaScript語法和Java等語法非常類似。所以只是簡單介紹一下。 大小寫 JavaScript是大小寫敏感的語...
摘要:一寫在前面最近重讀高級程序設(shè)計,總結(jié)下來,查漏補缺。但這種影響是單向的修改命名參數(shù)不會改變中對應(yīng)的值。這是因為對象的長度是由傳入的參數(shù)個數(shù)決定的,不是由定義函數(shù)時的命名參數(shù)的個數(shù)決定的。實際改變會同步,改變也會同步 一、寫在前面 最近重讀《JavaScript高級程序設(shè)計》,總結(jié)下來,查漏補缺。 二、JS簡介 2.1 JS組成 ECMAscript:以ECMA-262為基礎(chǔ)的語言,由...
摘要:用于把對象序列化字符串,在序列化對象時,所有函數(shù)及原型成員都會被有意忽略,不體現(xiàn)在結(jié)果中。對第步返回的每個值進行相應(yīng)的序列化。參考文檔高級程序設(shè)計作者以樂之名本文原創(chuàng),有不當?shù)牡胤綒g迎指出。 showImg(https://segmentfault.com/img/bVburW1?w=658&h=494); JSON與JavaScript對象 JSON是一種表示結(jié)構(gòu)化數(shù)據(jù)的存儲格式,語...
閱讀 5455·2021-09-22 15:50
閱讀 1942·2021-09-02 15:15
閱讀 1234·2019-08-29 12:49
閱讀 2607·2019-08-26 13:31
閱讀 3523·2019-08-26 12:09
閱讀 1277·2019-08-23 18:17
閱讀 2804·2019-08-23 17:56
閱讀 3005·2019-08-23 16:02