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