摘要:正文執(zhí)行環(huán)境也稱為環(huán)境是中最為重要的一個(gè)概念。執(zhí)行環(huán)境定義了變量或函數(shù)有權(quán)訪問的其他數(shù)據(jù),決定了它們各自的行為。簡(jiǎn)而言之,執(zhí)行環(huán)境是基于對(duì)象的,而作用域是基于函數(shù)的。
前述
在我們學(xué)習(xí)Javascript過程中,常常會(huì)遇到作用域(Scope)和執(zhí)行上下文(Context)等概念。其中,執(zhí)行上下文與this關(guān)鍵字的關(guān)系密切。
有面向?qū)ο缶幊探?jīng)驗(yàn)的各位,對(duì)于this關(guān)鍵字再熟悉不過了,因此我們很容易地把它和面向?qū)ο蟮木幊谭绞铰?lián)系在一起,它指向利用構(gòu)造器新創(chuàng)建出來的對(duì)象;在ECMAScript中,也支持this,然而, 正如大家所熟知的,this不僅僅只用來表示創(chuàng)建出來的對(duì)象。
在接下來的博文我們講介紹Javascript的作用域和執(zhí)行上下文,以及它們的異同之處。
正文
執(zhí)行環(huán)境(Execution context)也稱為“環(huán)境”是Javascript中最為重要的一個(gè)概念。執(zhí)行環(huán)境定義了變量或函數(shù)有權(quán)訪問的其他數(shù)據(jù),決定了它們各自的行為。每個(gè)執(zhí)行環(huán)境都有一個(gè)與之關(guān)聯(lián)的變量對(duì)象,環(huán)境中定義的所有變量和函數(shù)都保存在這個(gè)對(duì)象中。
看到了執(zhí)行環(huán)境的定義有點(diǎn)頭昏了,簡(jiǎn)而言之“每個(gè)執(zhí)行環(huán)境都有一個(gè)與之關(guān)聯(lián)的變量對(duì)象”;這里我們有一個(gè)疑問就是這個(gè)變量對(duì)象是怎樣定義的呢?
接下來,讓我們看一下變量對(duì)象的定義,具體實(shí)現(xiàn)如下:
/** * Execution context skeleton. */ activeExecutionContext = { // variable object. VO: {...}, this: thisValue };
通過上面的偽代碼我們知道對(duì)象字面量activeExecutionContext,它包含一個(gè)變量對(duì)象VO和this屬性。
這說明了this與上下文的可執(zhí)行代碼類型有關(guān),其值在進(jìn)入上下文階段就確定了,并且在執(zhí)行代碼階段是不能改變的(關(guān)于this使用可以閱讀《Javascript this 的一些學(xué)習(xí)總結(jié)》)。
作用域(Scope)控制著變量和參數(shù)的可見性及生命周期。
簡(jiǎn)而言之,執(zhí)行環(huán)境是基于對(duì)象的,而作用域是基于函數(shù)的。
作用域
我們將通過一個(gè)例子介紹作用域的使用,首先,我們定義了一個(gè)函數(shù)FooA()和FooB,示例代碼如下:
/** * Defines a function. */ var FooA = function(){ var a = 1; var FooB = function(){ var b = 2; console.log(a, b); // outputs: 1, 2 } console.log(a, b); // Error! b is not defined } FooA();
在示例中,第二個(gè)log輸出變量為未定義,這是由于在Javascript中定義在函數(shù)里面的參數(shù)和變量在函數(shù)外部是不可見的,而在一個(gè)函數(shù)內(nèi)部任何位置定義的參數(shù)和變量,在該函數(shù)內(nèi)部任何地方都是可見的。
執(zhí)行環(huán)境
首先,我們定義了對(duì)象字面量o,它包含一個(gè)屬性x和方法m(),示例代碼如下:
/** * Defines a literal object. * @type {Object} */ var o = { x:23, m: function(){ var x = 1; console.log(x, this.x); // outputs 1, 23 } } o.m();
示例中的兩個(gè)變量和屬性x都能被訪問,但它們被訪問的方式是截然不同,在log中訪問第一個(gè)x是通過作用域方式訪問了本地變量x,而this.x是通過執(zhí)行上下文方式訪問對(duì)象o的屬性x,因此輸出值也不盡相同。
上下文問題
接下來,我們修改一下前面的例子,在方法m()中添加一個(gè)函數(shù)f(),示例代碼如下:
/** * Defines a literal object. * @type {Object} */ var o = { x:23, m: function(){ var x = 1; var f = function(){ console.log(x, this.x); // outputs 1, undefined } f(); } } o.m();
上面,我們通過調(diào)用方法m()來輸出x的值,由于方法m()的具體實(shí)現(xiàn)是通過調(diào)用函數(shù)f()來實(shí)現(xiàn)。
當(dāng)我們調(diào)用對(duì)象o的方法m()時(shí),發(fā)現(xiàn)this.x是未定義的。
這究竟是什么原因呢?回憶前面的例子,由于方法m()獲取了對(duì)象o的上下文,所以this是指向?qū)ο髈的,難道是函數(shù)f()沒有獲取對(duì)象o的上下文,因此它不清楚this指向哪個(gè)對(duì)象?
首先讓我們回顧一下函數(shù)和方法以及屬性和變量的區(qū)別:方法和對(duì)象關(guān)聯(lián),如:object.myMethod = function() {},而函數(shù)非對(duì)象關(guān)聯(lián):var myFunc = function {};同樣屬性也是對(duì)象關(guān)系的,如:object.myProperty = 23,而變量:var myProperty = 23。
因?yàn)槲覀兲岬缴舷挛氖腔趯?duì)象的,所以函數(shù)f()不能獲取對(duì)象o的執(zhí)行上下文。
我們是否可以讓函數(shù)f()獲取對(duì)象o的執(zhí)行上下文呢?我們仔細(xì)地想一下,既然函數(shù)f()不清楚this指向的對(duì)象,那么可以直接調(diào)用對(duì)象的屬性就OK了。
/** * Fixs broken context issue. * @type {Object} */ var o = { x:23, m: function(){ var x = 1; var f = function(){ console.log(x, o.x); // outputs 1, 23 } f(); } } o.m();
我們?cè)诤瘮?shù)f()中直接調(diào)用對(duì)象o的屬性x,這樣函數(shù)f()就無需獲取執(zhí)行上下文直接調(diào)用對(duì)象的屬性了。
現(xiàn)在,我們又遇到一個(gè)新的問題了,如果對(duì)象不是o而是p,那么我們就需要修改函數(shù)f()中的對(duì)象了,更嚴(yán)重的情況就是我們沒有辦法確定具體是哪個(gè)對(duì)象,示例代碼如下:
/** * Defines a literal object. * @constructor */ var C = function(){} C.prototype = { x:23, m: function(){ var x = 1; var f = function(){ console.log(x, this.x); // outputs 1, undefined } f(); } } var instance1 = new C(); instance1.m();
上下文實(shí)例問題
上面,我們定義了函數(shù)C和它的原型對(duì)象,而且我們可以通過new方式創(chuàng)建C對(duì)象實(shí)例instance1,按照前面的方法解決Broken Context問題,具體實(shí)現(xiàn)如下:
/** * Defines a literal object. * @constructor */ var C = function(){} C.prototype = { x:23, m: function(){ var x = 1; var f = function(){ console.log(x, instance1.x); // outputs 1, undefined } f(); } } var instance1 = new C(); instance1.m();
如果我們?cè)趧?chuàng)建一個(gè)C的對(duì)象實(shí)例instance2,那么我們就不能指定函數(shù)f()中的對(duì)象了。
其實(shí),this是對(duì)象實(shí)例的抽象,當(dāng)實(shí)例有多個(gè)甚至成千上百個(gè)的時(shí)候,我們需要通過this引用這些對(duì)象實(shí)例。
因此,指定對(duì)象方法不能有效解決Broken Context問題,我們還是需要使用this來引用對(duì)象,前面我們講到由于函數(shù)f()沒有獲取對(duì)象o的執(zhí)行上下文,因此它不清楚this指向哪個(gè)對(duì)象,所以輸出this.x未定義,那么我們是否可以讓函數(shù)f()獲取對(duì)象的執(zhí)行上下文。
跨作用域的上下文
我們想想既然方法是基于對(duì)象的,而且可以獲取對(duì)象的執(zhí)行上下文,那么我們直接把f()定義為方法好了。
現(xiàn)在,我們?cè)贑對(duì)象原型中定義方法f(),示例代碼如下:
/** * Defines a literal object. * @constructor */ var C = function(){} C.prototype = { x:10, m: function(){ var x = 1; this.f(); }, f: function(){ console.log(x, this.x); // Reference ERROR!! } } var instance1 = new C(); instance1.m();
好啦,我們?cè)贑對(duì)象原型中定義方法f(),那么方法f()就可以獲取對(duì)象的執(zhí)行上下文。
現(xiàn)在,我們?cè)贔irefox運(yùn)行以上代碼,結(jié)果輸出Reference ERROR,這究竟是什么原因呢?我們想了一下問題出于變量x中,由于方法f()不能獲取方法m()的作用域,所以變量x不在方法f()中。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/88099.html
摘要:總結(jié)本博文通過介紹執(zhí)行上下文和作用域的異同的使用以及變量對(duì)象,讓我們加深對(duì)語言特性的理解。首先,我們介紹了執(zhí)行上下文和的的關(guān)系,并且執(zhí)行上下文是具有對(duì)象的然后,介紹了作用域使變量在作用域范圍內(nèi)可見,并且作用域是基于函數(shù)的。 接上一篇Javascript Context和Scope的學(xué)習(xí)總結(jié)01【轉(zhuǎn)自cnblogs的JKhuang】(可能是segmentfault對(duì)單篇文章發(fā)布字?jǐn)?shù)有限制...
摘要:函數(shù)上下文中的值是函數(shù)調(diào)用者提供并且由當(dāng)前調(diào)用表達(dá)式的形式而定的。然而,由于對(duì)于來說沒有任何意義,因此會(huì)隱式轉(zhuǎn)換為全局對(duì)象。這里注意到四個(gè)表達(dá)式中,只有第一個(gè)表達(dá)式是指向?qū)ο蟮?,而其他三個(gè)表達(dá)式則執(zhí)行。 摘要 相信有C++、C#或Java等編程經(jīng)驗(yàn)的各位,對(duì)于this關(guān)鍵字再熟悉不過了。由于Javascript是一種面向?qū)ο蟮木幊陶Z言,它和C++、C#或Java一樣都包含this關(guān)鍵字...
摘要:發(fā)生這種情況的條件是當(dāng)引用類型值的對(duì)象恰好為活躍對(duì)象。總結(jié)本文介紹中的使用,更重要的是幫助我們能更好地理解值在全局函數(shù)構(gòu)造函數(shù)以及一些特例的情況中值的變化。然而,由于對(duì)于來說沒有任何意義,因此會(huì)隱式轉(zhuǎn)換為全局對(duì)象。 接上一篇Javascript this 的一些學(xué)習(xí)總結(jié)02【轉(zhuǎn)自cnblogs的JKhuang】 引用類型以及this的null值 對(duì)于前面提及的情形,還有例外的情況,當(dāng)調(diào)...
摘要:正式由于作用域鏈的這種關(guān)系,我們就不難理解,為什么和不能通過作用域鏈向上搜索,因?yàn)閷?duì)和的搜索在當(dāng)前執(zhí)行函數(shù)的活動(dòng)對(duì)象就停止了。 對(duì)于Javascript程序員來說,閉包總會(huì)讓你覺得既熟悉又陌生,然而它對(duì)于開發(fā)人員來說卻非常重要,javascript里的許多設(shè)計(jì)模式中都用到了閉包,此處以函數(shù)作用域?yàn)槔?//示例代碼 var a=1; function foo(){ ...
摘要:此時(shí)的作用域鏈包含了兩個(gè)對(duì)象的活動(dòng)對(duì)象和對(duì)象。閉包的應(yīng)用場(chǎng)景保護(hù)函數(shù)內(nèi)的變量安全。依然如前例,由于閉包,函數(shù)中的一直存在于內(nèi)存中,因此每次執(zhí)行,都會(huì)給自加。 引子 JS的閉包一直是很多人不理解,也是在使用過程中經(jīng)常出現(xiàn)問題的地方。每次看文章都會(huì)有所了解閉包,但是,用起來還是不對(duì),而且錯(cuò)誤百出,其關(guān)鍵問題還是出在對(duì)其不理解,不了解。此文章會(huì)不定期更新以及完善,希望在我學(xué)習(xí)的時(shí)候,讓大家也...
閱讀 2520·2023-04-25 17:37
閱讀 1203·2021-11-24 10:29
閱讀 3709·2021-09-09 11:57
閱讀 704·2021-08-10 09:41
閱讀 2256·2019-08-30 15:55
閱讀 2822·2019-08-30 15:54
閱讀 1951·2019-08-30 15:53
閱讀 906·2019-08-30 15:43