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