摘要:講作用域鏈?zhǔn)紫纫獜淖饔糜蛑v起,下面是百度百科里對(duì)作用域的定義作用域在許多程序設(shè)計(jì)語(yǔ)言中非常重要。原文出處談?wù)務(wù)Z法里一些難點(diǎn)問(wèn)題二
3) 作用域鏈相關(guān)的問(wèn)題
作用域鏈?zhǔn)莏avascript語(yǔ)言里非常紅的概念,很多學(xué)習(xí)和使用javascript語(yǔ)言的程序員都知道作用域鏈?zhǔn)抢斫鈐avascript里很重要的一些概念的關(guān)鍵,這些概念包括this指針,閉包等等,它非常紅的另一個(gè)重要原因就是作用域鏈理解起來(lái)太難,就算有人真的感覺(jué)理解了它,但是碰到很多實(shí)際問(wèn)題時(shí)候任然會(huì)是丈二和尚摸不到頭腦,例如上篇引子里講到的例子,本篇要講的主題就是作用域鏈,再無(wú)別的內(nèi)容,希望看完本文的朋友能有所收獲。
講作用域鏈?zhǔn)紫纫獜淖饔糜蛑v起,下面是百度百科里對(duì)作用域的定義:
作用域在許多程序設(shè)計(jì)語(yǔ)言中非常重要。
通常來(lái)說(shuō),一段程序代碼中所用到的名字并不總是有效/可用的,而限定這個(gè)名字的可用性的代碼范圍就是這個(gè)名字的作用域。
作用域的使用提高了程序邏輯的局部性,增強(qiáng)程序的可靠性,減少名字沖突。
在我最擅長(zhǎng)的服務(wù)端語(yǔ)言java里也有作用域的概念,java里作用域是以{}作為邊界,不過(guò)在純種的面向?qū)ο笳Z(yǔ)言里我們沒(méi)必要把作用域研究的那么深,也沒(méi)必要思考復(fù)雜的作用域嵌套問(wèn)題,因?yàn)檫@些語(yǔ)言關(guān)于作用域的深度運(yùn)用并不會(huì)給我們編寫(xiě)的代碼帶來(lái)多大好處。但是在javascript里卻大不相同,如果我們不能很好的理解javascript的作用域我們就沒(méi)辦法使用javascript編寫(xiě)出復(fù)雜的或者規(guī)模宏大的程序。
由百度百科里的定義,我們知道作用域的作用是保證變量的名字不發(fā)生沖突,用現(xiàn)實(shí)的場(chǎng)景來(lái)理解有個(gè)人叫做張三,張三雖然只是一個(gè)名字,但是認(rèn)識(shí)張三的人根據(jù)名字就能唯一確認(rèn)這個(gè)人到底是誰(shuí),但是這個(gè)世界上叫做張三的人可不止一個(gè),特別是兩個(gè)叫張三的人有交集的時(shí)候我們就要有個(gè)辦法明確指定這個(gè)張三絕不是另外一個(gè)張三,這時(shí)我們可能會(huì)根據(jù)兩大張三年齡的差異來(lái)區(qū)分:例如一個(gè)張三叫大張三,相對(duì)的另外一個(gè)張三叫小張三了。編程語(yǔ)言里的作用域其實(shí)就是為了做類(lèi)似的標(biāo)記,作用域會(huì)設(shè)定一個(gè)范圍,在這個(gè)范圍里我們是不會(huì)弄錯(cuò)變量的真實(shí)含義。
前面我講到在java里通過(guò){}來(lái)設(shè)置作用域,在{}里面的變量會(huì)得到保護(hù),這種保護(hù)就是不讓{}里的變量被外部變量混淆和污染。那么{}的方式適合于javascript嗎?我們看看下面的例子:
var s1 = "sharpxiajun"; function ftn(){ var s2 = "xtq"; console.log(this);// 運(yùn)行結(jié)果: window console.log("s1:" + this.s1 + ";s2:" + this.s2);//運(yùn)行結(jié)果:s1:sharpxiajun;s2:undefined console.log("s1:" + this.s1 + ";s2:" + s2);// 運(yùn)行結(jié)果:s1:sharpxiajun;s2:xtq } ftn();
在javascript世界里有一個(gè)大的作用域環(huán)境,這個(gè)環(huán)境就是window,window環(huán)境不需要我們自己使用什么方式構(gòu)建,頁(yè)面加載時(shí)候頁(yè)面會(huì)自動(dòng)構(gòu)造的,上面代碼里有一個(gè)大括號(hào),這個(gè)大括號(hào)是對(duì)函數(shù)的定義,運(yùn)行之,我們發(fā)現(xiàn)函數(shù)作用域內(nèi)部定義的s2變量是不能被window對(duì)象訪問(wèn)的,因此s2變量是被{}保護(hù)起來(lái)了,它的生命周期和這個(gè)函數(shù)的生命周期有關(guān)。
由這個(gè)例子是不是說(shuō)明在javascript里,變量也是被{}保護(hù)起來(lái)了,在javascript語(yǔ)言里還有非函數(shù)的{},我們?cè)倏纯聪旅娴睦樱?/p>
if (true){ var a = "aaaa"; } console.log(a);// 運(yùn)行結(jié)果:aaaa
我們發(fā)現(xiàn)javascript里{}有時(shí)是起不到定義作用域的功能。這也說(shuō)明javascript里的作用域定義是和其他語(yǔ)言例如java不同的。
在javascript里作用域有一個(gè)專(zhuān)門(mén)的定義execution context,有的書(shū)里把這個(gè)名字翻譯成執(zhí)行上下文,有的書(shū)籍里把它翻譯成執(zhí)行環(huán)境,我更傾向于后者執(zhí)行環(huán)境,下文我提到的執(zhí)行環(huán)境就是execution context。這個(gè)命名非常形象,這個(gè)形象體現(xiàn)在execution這個(gè)單詞,execution含義就是執(zhí)行,我們來(lái)想想javascript里那些情況是執(zhí)行:
情況一:當(dāng)頁(yè)面加載時(shí)候在script標(biāo)簽下的javascript代碼會(huì)按順序執(zhí)行,而這些能被執(zhí)行的代碼都是屬于window的變量或函數(shù);
情況二:當(dāng)函數(shù)的名字后面加上小括號(hào)(),例如ftn(),這也是在執(zhí)行,不過(guò)它執(zhí)行的是函數(shù)。
如此說(shuō)來(lái),javascript里的執(zhí)行環(huán)境有兩類(lèi)一類(lèi)是全局執(zhí)行環(huán)境,即window代表的全局環(huán)境,一類(lèi)是函數(shù)代表的函數(shù)執(zhí)行環(huán)境,這也就是我們常說(shuō)的局部作用域。
執(zhí)行環(huán)境在javascript語(yǔ)言里并非是一個(gè)抽象的概念,而是有具體的實(shí)現(xiàn),這個(gè)實(shí)現(xiàn)其實(shí)是個(gè)對(duì)象,這個(gè)對(duì)象也有個(gè)名字叫做variable object,這個(gè)變量有的書(shū)里翻譯為變量對(duì)象,這是直譯,有的書(shū)里把它稱(chēng)為上下文變量,這里我還是傾向于后者上下文變量,下文里提到的上下文變量就是指代variable object。上下文變量存儲(chǔ)的是上下文變量所處執(zhí)行環(huán)境里定義的所有的變量和函數(shù)。
全局執(zhí)行環(huán)境的上下文變量是可以訪問(wèn)到的,它就是window對(duì)象,所以我們說(shuō)window能代表全局作用域是有道理的,但是局部作用域即函數(shù)的執(zhí)行環(huán)境里的上下文變量是代碼不能訪問(wèn)到的,不過(guò)javascript引擎在處理數(shù)據(jù)時(shí)候會(huì)使用到它。
在javascript語(yǔ)言里還有一個(gè)概念,它的名字叫做execution context stack,翻譯成中文就是執(zhí)行環(huán)境棧,每個(gè)要被執(zhí)行的函數(shù)都會(huì)先把函數(shù)的執(zhí)行環(huán)境壓入到執(zhí)行環(huán)境棧里,函數(shù)執(zhí)行完畢后,這個(gè)函數(shù)的執(zhí)行環(huán)境就會(huì)被執(zhí)行環(huán)境棧彈出,例如上面的例子:函數(shù)執(zhí)行時(shí)候函數(shù)的執(zhí)行環(huán)境會(huì)被壓入到執(zhí)行環(huán)境棧里,函數(shù)執(zhí)行完畢,執(zhí)行環(huán)境棧會(huì)把這個(gè)環(huán)境彈出,執(zhí)行環(huán)境棧的控制權(quán)就會(huì)交由全局環(huán)境,如果函數(shù)后面還有代碼,那么代碼就是接著執(zhí)行。如果函數(shù)里嵌套了函數(shù),那么嵌套函數(shù)執(zhí)行完畢后,執(zhí)行環(huán)境棧的控制權(quán)就交由了外部函數(shù),然后依次類(lèi)推,最后就是全局執(zhí)行環(huán)境了。
講到這里我們大名鼎鼎的作用域鏈要登場(chǎng)了,函數(shù)的執(zhí)行環(huán)境被壓入到執(zhí)行環(huán)境棧里后,函數(shù)就要執(zhí)行了,函數(shù)執(zhí)行的第一步不是執(zhí)行函數(shù)里的第一行代碼而是在上下文變量里構(gòu)造一個(gè)作用域鏈,作用域鏈的英文名字叫做scope chain,作用域鏈的作用是保證執(zhí)行環(huán)境里有權(quán)訪問(wèn)的變量和函數(shù)是有序的,這個(gè)概念里有兩個(gè)關(guān)鍵意思:有權(quán)訪問(wèn)和有序,我們看看下面的代碼:
var b1 = "b1"; function ftn1(){ var b2 = "b2"; var b1 = "bbb"; function ftn2(){ var b3 = "b3"; b2 = b1; b1 = b3; console.log("b1:" + b1 + ";b2:" + b2 + ";b3:" + b3);// 運(yùn)行結(jié)果:b1:b3;b2:bbb;b3:b3 } ftn2(); } ftn1(); console.log(b1);// 運(yùn)行結(jié)果:b1
有這個(gè)例子我們發(fā)現(xiàn),ftn2函數(shù)可以訪問(wèn)變量b1,b2,這個(gè)體現(xiàn)了有權(quán)訪問(wèn)的概念,當(dāng)ftn1作用域里改變了b1的值并且把b1變量重新定義為ftn1的局部變量,那么ftn2訪問(wèn)到的b1就是ftn1的,ftn2訪問(wèn)到b1后就不會(huì)在全局作用域里查找b1了,這個(gè)體現(xiàn)了有序性。
下面我要總結(jié)下上面講述的知識(shí):
本篇的小標(biāo)題是:作用域鏈的相關(guān)問(wèn)題,這個(gè)標(biāo)題定義的含義是指作用域鏈?zhǔn)谴竺Χα?,但是作用域鏈在廣大程序員的理解里其實(shí)包含的意義已經(jīng)超越了作用域鏈在javascript語(yǔ)言本身的定義。廣大程序員對(duì)作用域鏈的理解有兩塊一塊是作用域,而作用域在javascript語(yǔ)言里指的是執(zhí)行環(huán)境execution context,執(zhí)行環(huán)境在javascript引擎里是通過(guò)上下文變量體現(xiàn)的variable object,javascript引擎里還有一個(gè)概念就是執(zhí)行環(huán)境棧execution context stack,當(dāng)某一個(gè)函數(shù)的執(zhí)行環(huán)境壓入到了執(zhí)行環(huán)境棧里,這個(gè)時(shí)候就會(huì)在上下文變量里構(gòu)造一個(gè)對(duì)象,這個(gè)對(duì)象就是作用域鏈scope chain,而這個(gè)作用域鏈就是廣大程序員理解的第二塊知識(shí),作用域鏈的作用是保證執(zhí)行環(huán)境里有權(quán)訪問(wèn)的變量和函數(shù)是有序的,作用域鏈的變量只能向上訪問(wèn),變量訪問(wèn)到window對(duì)象即被終止,作用域鏈向下訪問(wèn)變量是不被允許的。
很多人常常認(rèn)為作用域鏈?zhǔn)抢斫?b>this指針的關(guān)鍵,這個(gè)理解是不正確的的,this指針構(gòu)造是和作用域鏈同時(shí)發(fā)生的,也就是說(shuō)在上文變量構(gòu)建作用域鏈的同時(shí)還會(huì)構(gòu)造一個(gè)this對(duì)象,this對(duì)象也是屬于上下文變量,而this變量的值就是當(dāng)前執(zhí)行環(huán)境外部的上下文變量的一份拷貝,這個(gè)拷貝里是沒(méi)有作用域鏈變量的,例如代碼:
var b1 = "b1"; function ftn1(){ console.log(this);// 運(yùn)行結(jié)果: window var b2 = "b2"; var b1 = "bbb"; function ftn2(){ console.log(this);// 運(yùn)行結(jié)果: window var b3 = "b3"; b2 = b1; b1 = b3; console.log("b1:" + b1 + ";b2:" + b2 + ";b3:" + b3);// 運(yùn)行結(jié)果:b1:b3;b2:bbb;b3:b3 } ftn2(); } ftn1();
我們看到函數(shù)ftn1和ftn2里的this指針都是指向window,這是為什么了?因?yàn)樵趈avascript我們定義函數(shù)方式是通過(guò)function xxx(){}形式,那么這個(gè)函數(shù)不管定義在哪里,它都屬于全局對(duì)象window,所以他們的執(zhí)行環(huán)境的外部的執(zhí)行上下文都是指向window。
但是我們都知道現(xiàn)實(shí)代碼很多this指針都不是指向window,例如下面的代碼:
var obj = {
name:"sharpxiajun", ftn:function(){ console.log(this);// 運(yùn)行結(jié)果: Object { name="sharpxiajun", ftn=function()} console.log(this.name);//運(yùn)行結(jié)果: sharpxiajun }
}
obj.ftn();// :
運(yùn)行之,我們發(fā)現(xiàn)這里this指針指向了Object,這就怪了我前文不是說(shuō)javascript里作用域只有兩種類(lèi)型:一個(gè)是全局的一個(gè)是函數(shù),為什么這里Object也是可以制造出作用域了,那么我的理論是不是有問(wèn)題???那我們看看下面的代碼:
var obj1 = new Object(); obj1.name = "xtq"; obj1.ftn = function(){ console.log(this);// 運(yùn)行結(jié)果: Object { name="xtq", ftn=function()} console.log(this.name);//運(yùn)行結(jié)果: xtq } obj1.ftn();
這兩種寫(xiě)法是等價(jià)的,第一種對(duì)象的定義方法叫做字面量定義,而第二種寫(xiě)法則是標(biāo)準(zhǔn)寫(xiě)法,Object對(duì)象的本質(zhì)也是個(gè)function,所以當(dāng)我們調(diào)用對(duì)象里的函數(shù)時(shí)候,函數(shù)的外部執(zhí)行環(huán)境就是obj1本身,即外部執(zhí)行環(huán)境上下文變量代表的就是obj1,那么this指針也是指向了obj1。
原文出處:談?wù)刯avascript語(yǔ)法里一些難點(diǎn)問(wèn)題(二)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/85557.html
摘要:引子前不久我建立的技術(shù)群里一位問(wèn)了一個(gè)這樣的問(wèn)題,她貼出的代碼如下所示執(zhí)行結(jié)果如下所示第一個(gè)第二個(gè)這是一個(gè)令人詫異的結(jié)果,為什么第一個(gè)彈出框顯示的是,而不是呢這種疑惑的原理我描述如下一個(gè)頁(yè)面里直接定義在標(biāo)簽下的變量是全局變量即屬于對(duì)象的變量 1) 引子 前不久我建立的技術(shù)群里一位MM問(wèn)了一個(gè)這樣的問(wèn)題,她貼出的代碼如下所示: var a = 1; function hehe...
摘要:自我學(xué)習(xí)目前有成千上萬(wàn)的年輕人在學(xué)習(xí)和開(kāi)發(fā),希望獲得一份工作。知道的綁定規(guī)則。知道和原型屬性是什么以及它們的作用。高階函數(shù)了解函數(shù)是中的一級(jí)對(duì)象,這意味著什么知道從另一個(gè)函數(shù)返回函數(shù)是完全合法的。了解閉包和高階函數(shù)允許我們使用的情況。 翻譯原文出處:10 JavaScript concepts you need to know for interviews 之前不是鬧得沸沸揚(yáng)揚(yáng)的大漠窮...
摘要:為此決定自研一個(gè)富文本編輯器。例如當(dāng)要轉(zhuǎn)化的對(duì)象有環(huán)存在時(shí)子節(jié)點(diǎn)屬性賦值了父節(jié)點(diǎn)的引用,為了關(guān)于函數(shù)式編程的思考作者李英杰,美團(tuán)金融前端團(tuán)隊(duì)成員。只有正確使用作用域,才能使用優(yōu)秀的設(shè)計(jì)模式,幫助你規(guī)避副作用。 JavaScript 專(zhuān)題之惰性函數(shù) JavaScript 專(zhuān)題系列第十五篇,講解惰性函數(shù) 需求 我們現(xiàn)在需要寫(xiě)一個(gè) foo 函數(shù),這個(gè)函數(shù)返回首次調(diào)用時(shí)的 Date 對(duì)象,注意...
摘要:從最開(kāi)始的到封裝后的都在試圖解決異步編程過(guò)程中的問(wèn)題。為了讓編程更美好,我們就需要引入來(lái)降低異步編程的復(fù)雜性。異步編程入門(mén)的全稱(chēng)是前端經(jīng)典面試題從輸入到頁(yè)面加載發(fā)生了什么這是一篇開(kāi)發(fā)的科普類(lèi)文章,涉及到優(yōu)化等多個(gè)方面。 TypeScript 入門(mén)教程 從 JavaScript 程序員的角度總結(jié)思考,循序漸進(jìn)的理解 TypeScript。 網(wǎng)絡(luò)基礎(chǔ)知識(shí)之 HTTP 協(xié)議 詳細(xì)介紹 HTT...
閱讀 2503·2021-09-22 15:27
閱讀 3270·2021-09-03 10:32
閱讀 3572·2021-09-01 11:38
閱讀 2551·2019-08-30 15:56
閱讀 2274·2019-08-30 13:01
閱讀 1588·2019-08-29 12:13
閱讀 1474·2019-08-26 13:33
閱讀 947·2019-08-26 13:30