深入理解JavaScript?變量對(duì)象
前言
在上節(jié)《深入 JavaScript 執(zhí)行上下文棧——Web 前端進(jìn)階系列第三節(jié)》我們講到,JavaScript 引擎執(zhí)行一段可執(zhí)行代碼時(shí),會(huì)創(chuàng)建對(duì)應(yīng)的執(zhí)行上下文。
對(duì)于每個(gè)執(zhí)行上下文,都有三個(gè)重要屬性:
- 變量對(duì)象(Variable object,VO)
- 作用域鏈(Scope chain)
- this
今天我們來(lái)重點(diǎn)講解變量對(duì)象。
變量對(duì)象
變量對(duì)象是與執(zhí)行上下文相關(guān)的數(shù)據(jù)作用域,存儲(chǔ)了在上下文中定義的變量和函數(shù)聲明。
執(zhí)行上下文分為兩種:全局上下文和函數(shù)上下文,接下來(lái)我們來(lái)分別講解這兩種上下文的變量對(duì)象。
全局上下文中變量對(duì)象
全局上下文中的變量對(duì)象是全局對(duì)象。
下面我們來(lái)了解一下全局對(duì)象,在 W3school 中的介紹有:
全局對(duì)象是預(yù)定義的對(duì)象,作為 JavaScript 的全局函數(shù)和全局屬性的占位符。通過(guò)使用全局對(duì)象,可以訪問(wèn)所有其他預(yù)定義的對(duì)象、函數(shù)和屬性。
在頂層 JavaScript 代碼中,可以用關(guān)鍵字 this 引用全局對(duì)象。全局對(duì)象在作用域鏈最底端,這意味著所有非限定性的變量和函數(shù)名都會(huì)作為該對(duì)象的屬性來(lái)查詢。
由于全局對(duì)象在作用域鏈最底端,這也意味著在頂層 JavaScript 代碼中聲明的變量都將成為全局對(duì)象的屬性。
字面上大家理解起來(lái)可能比較抽象,接下來(lái)我們結(jié)合具體例子作進(jìn)一步講解。
- 在頂層 JavaScript 代碼中,可以用關(guān)鍵字 this 引用全局對(duì)象。在瀏覽器 JavaScript 中,全局對(duì)象是 window。在 node.js 中,全局對(duì)象是 global。
console.log(this); // window console.log(this === window); // true
- 全局對(duì)象是 JavaScript 的全局函數(shù)和全局屬性的占位符。在頂層 JavaScript 代碼中聲明的變量都將成為全局對(duì)象的屬性。
// 聲明的變量成為了全局對(duì)象的屬性 var a = 1; console.log(this.a); // 1 // 聲明的函數(shù)成為了全局對(duì)象的屬性 function b() {} console.log(this.b); // function b
- 通過(guò)使用全局對(duì)象,可以訪問(wèn)全局函數(shù)和全局屬性,也可以訪問(wèn)所有其他預(yù)定義的對(duì)象、函數(shù)和屬性。
// 使用全局對(duì)象訪問(wèn)全局屬性 Math,它是一個(gè)對(duì)象,它擁有 random 方法。 console.log(this.Math.random()); // 打印一個(gè)隨機(jī)數(shù)
- 所有非限定性的變量和函數(shù)名都會(huì)作為該對(duì)象的屬性來(lái)查詢。
// 這里的 Math 是非限定性的函數(shù)名 console.log(Math.random()); // 打印一個(gè)隨機(jī)數(shù)
- 全局對(duì)象是 Object 構(gòu)造函數(shù)的實(shí)例,這也意味著 Object.prototype(原型)上預(yù)定義的屬性和方法,是可以通過(guò)全局對(duì)象訪問(wèn)到的。
console.log(this instanceof Object); // true
- 在瀏覽器 JavaScript 中,全局對(duì)象有 window 屬性且指向自身。
console.log(this.window === this); // true
函數(shù)上下文中的變量對(duì)象
在函數(shù)上下文中,我們用活動(dòng)對(duì)象(activation object, AO)來(lái)表示變量對(duì)象。
活動(dòng)對(duì)象和變量對(duì)象其實(shí)是一個(gè)東西,只是變量對(duì)象是規(guī)范上的或者說(shuō)是引擎實(shí)現(xiàn)上的,不可在 JavaScript 環(huán)境中訪問(wèn),只有到當(dāng)進(jìn)入一個(gè)執(zhí)行上下文中,這個(gè)執(zhí)行上下文的變量對(duì)象才會(huì)被激活,所以才叫 activation object,而只有被激活的變量對(duì)象,也就是活動(dòng)對(duì)象,各種屬性和方法才能被訪問(wèn)。
活動(dòng)對(duì)象是在進(jìn)入函數(shù)上下文時(shí)被創(chuàng)建的,它有函數(shù)的 arguments 屬性作為初始化屬性。arguments 屬性的值就是 Arguments 對(duì)象。
執(zhí)行過(guò)程
函數(shù)上下文的代碼執(zhí)行過(guò)程共分成兩個(gè)階段,分別是:預(yù)編譯和執(zhí)行。
預(yù)編譯
創(chuàng)建 AO 對(duì)象,尋找形參和變量聲明
把形參和變量名作為 AO 對(duì)象的屬性名,值為 undefined
把實(shí)參賦給形參,實(shí)參形參相統(tǒng)一
尋找函數(shù)聲明,值為函數(shù)體
我們來(lái)看個(gè)例子:
function foo(a) { var b = 2; function c() {} var d = function() {}; b = 3; } foo(1);
這個(gè)函數(shù)在預(yù)編譯完成后,AO 會(huì)變?yōu)椋?/p>
AO = { arguments: { 0: 1, length: 1 }, a: 1, b: undefined, c: reference to function c(){}, d: undefined }
代碼執(zhí)行
在代碼執(zhí)行階段,會(huì)順序執(zhí)行代碼。根據(jù)代碼,修改變量對(duì)象的值。
上面的例子當(dāng)代碼執(zhí)行完,AO 會(huì)變?yōu)椋?/p>
AO = { arguments: { 0: 1, length: 1 }, a: 1, b: 3, c: reference to function c(){}, d: reference to FunctionExpression "d" }
總結(jié)
至此,變量對(duì)象的創(chuàng)建過(guò)程我們就介紹完了,我們來(lái)做個(gè)總結(jié):
- 全局上下文的變量對(duì)象初始化是全局對(duì)象
- 函數(shù)上下文的變量對(duì)象初始化只包括 Arguments 對(duì)象
- 在進(jìn)入執(zhí)行上下文時(shí)會(huì)給變量對(duì)象添加形參、變量聲明、函數(shù)聲明等初始的屬性值(預(yù)編譯)
- 在代碼執(zhí)行階段,會(huì)修改變量對(duì)象的屬性值
練習(xí)題
- 第一題
來(lái)看下面兩端代碼,分別會(huì)打印什么?
function foo() { console.log(a); a = 1; } foo();
function bar() { a = 1; console.log(a); } bar();
第一段會(huì)報(bào)錯(cuò):Uncaught ReferenceError: a is not defined。
第二段會(huì)打?。?。
因?yàn)榈谝欢未a a 沒(méi)有變量聲明,所以函數(shù)執(zhí)行上下文的 AO 中沒(méi)有 a 變量的定義,此時(shí) AO 的值是:
AO = { arguments: { length: 0 } }
執(zhí)行打印時(shí),在函數(shù)執(zhí)行上下文的 AO 中沒(méi)有找到 a 變量的定義,然后就會(huì)去全局上下文中找,發(fā)現(xiàn)全局也沒(méi)有,所以就會(huì)報(bào)未定義的錯(cuò)。
第二段代碼,沒(méi)有使用 var 關(guān)鍵字聲明的變量會(huì)成為全局對(duì)象的屬性,所以執(zhí)行打印時(shí),會(huì)從全局對(duì)象找到 a 的值,所以會(huì)打印 1。
- 第二題
console.log(foo); function foo() {} var foo = 1;
會(huì)打印 foo 函數(shù),而不是 undefined。
因?yàn)樵陬A(yù)編譯的第 4 步,會(huì)尋找函數(shù)聲明,值為函數(shù)體,也就是函數(shù)聲明會(huì)覆蓋變量聲明。
到此這篇關(guān)于深入理解JavaScript 變量對(duì)象的文章就介紹到這了,更多相關(guān)JavaScript 變量對(duì)象內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JS實(shí)現(xiàn)超精簡(jiǎn)的鏈接列表在固定區(qū)域內(nèi)滾動(dòng)效果代碼
這篇文章主要介紹了JS實(shí)現(xiàn)超精簡(jiǎn)的鏈接列表在固定區(qū)域內(nèi)滾動(dòng)效果代碼,非常常見(jiàn)的頁(yè)面元素屬性變換控制實(shí)現(xiàn)滾動(dòng)效果,簡(jiǎn)單實(shí)用,需要的朋友可以參考下2015-11-11wavesurfer.js繪制音頻波形圖的實(shí)現(xiàn)
這篇文章主要介紹了wavesurfer.js繪制音頻波形圖的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04JS對(duì)象屬性的檢測(cè)與獲取操作實(shí)例分析
這篇文章主要介紹了JS對(duì)象屬性的檢測(cè)與獲取操作,結(jié)合實(shí)例形式分析了JS針對(duì)ES5、ES6實(shí)現(xiàn)對(duì)象屬性的檢測(cè)與獲取常見(jiàn)操作技巧,需要的朋友可以參考下2020-03-03JavaScript字符串轉(zhuǎn)數(shù)字的5種方法及遇到的坑
JavaScript是一個(gè)神奇的語(yǔ)言,字符串轉(zhuǎn)數(shù)字有5種方法,各有各的坑法!接下來(lái)通過(guò)本文給大家介紹JavaScript字符串轉(zhuǎn)數(shù)字的5種方法及其陷阱,感興趣的朋友一起看看吧2018-07-07window.open關(guān)于瀏覽器攔截問(wèn)題分析及解決方法
window.open是javascript函數(shù),該函數(shù)的作用是打開一個(gè)新窗口或這改變?cè)瓉?lái)的窗口,如果你直接在js中調(diào)用window.open()函數(shù)去打開一個(gè)新窗口,瀏覽器會(huì)攔截你,那么如何避免呢,感興趣的朋友可以了解下本文或許對(duì)你學(xué)習(xí)有所幫助2013-02-02JavaScript實(shí)現(xiàn)網(wǎng)頁(yè)圖片等比例縮放實(shí)現(xiàn)代碼及調(diào)用方式
為了保證圖片統(tǒng)一大小,直接設(shè)置圖片大小又會(huì)導(dǎo)致圖片拉伸,造成圖片模糊,接下來(lái)將介紹的代碼可以在圖片加載完成后自動(dòng)按比例調(diào)整圖片大小,感興趣的你可以參考下2013-02-02javascript setTimeout和setInterval計(jì)時(shí)的區(qū)別詳解
window對(duì)象有兩個(gè)主要的定時(shí)方法,分別是setTimeout 和 setInteval 他們的語(yǔ)法基本上相同,但是完成的功能取有區(qū)別。2013-06-06由 element.appendChild(newNode) ,談開去
由 element.appendChild(newNode) ,談開去...2006-11-11JS與HTML結(jié)合使用marquee標(biāo)簽實(shí)現(xiàn)無(wú)縫滾動(dòng)效果代碼
這篇文章主要介紹了JS與HTML結(jié)合使用marquee標(biāo)簽實(shí)現(xiàn)無(wú)縫滾動(dòng)效果代碼的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-07-07