




已閱讀5頁,還剩9頁未讀, 繼續(xù)免費(fèi)閱讀
版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
優(yōu)就業(yè)JS教程-理解JavaScript中的作用域和上下文Java對于作用域(Scope)和上下文(Context)的實(shí)現(xiàn)是這門語言的一個(gè)非常獨(dú)到的地方,部分歸功于其獨(dú)特的靈活性。 函數(shù)可以接收不同的的上下文和作用域。這些概念為Java中的很多強(qiáng)大的設(shè)計(jì)模式提供了堅(jiān)實(shí)的基礎(chǔ)。 然而這也概念也非常容易給開發(fā)人員帶來困惑。為此,本文將全面的剖析這些概念,并闡述不同的設(shè)計(jì)模式是如何利用它們的。Statement作者: 景莊,Web開發(fā)者,主要關(guān)注Java、Node.js、React、Docker等。上下文(Context)和作用域(Scope)首先需要知道的是,上下文和作用域是兩個(gè)完全不同的概念。多年來,我發(fā)現(xiàn)很多開發(fā)者會混淆這兩個(gè)概念(包括我自己), 錯誤的將兩個(gè)概念混淆了。平心而論,這些年來很多術(shù)語都被混亂的使用了。函數(shù)的每次調(diào)用都有與之緊密相關(guān)的作用域和上下文。從根本上來說,作用域是基于函數(shù)的,而上下文是基于對象的。 換句話說,作用域涉及到所被調(diào)用函數(shù)中的變量訪問,并且不同的調(diào)用場景是不一樣的。上下文始終是this關(guān)鍵字的值, 它是擁有(控制)當(dāng)前所執(zhí)行代碼的對象的引用。變量作用域一個(gè)變量可以被定義在局部或者全局作用域中,這建立了在運(yùn)行時(shí)(runtime)期間變量的訪問性的不同作用域范圍。 任何被定義的全局變量,意味著它需要在函數(shù)體的外部被聲明,并且存活于整個(gè)運(yùn)行時(shí)(runtime),并且在任何作用域中都可以被訪問到。 在ES6之前,局部變量只能存在于函數(shù)體中,并且函數(shù)的每次調(diào)用它們都擁有不同的作用域范圍。 局部變量只能在其被調(diào)用期的作用域范圍內(nèi)被賦值、檢索、操縱。需要注意,在ES6之前,Java不支持塊級作用域,這意味著在if語句、switch語句、for循環(huán)、while循環(huán)中無法支持塊級作用域。 也就是說,ES6之前的Java并不能構(gòu)建類似于Java中的那樣的塊級作用域(變量不能在語句塊外被訪問到)。但是, 從ES6開始,你可以通過let關(guān)鍵字來定義變量,它修正了var關(guān)鍵字的缺點(diǎn),能夠讓你像Java語言那樣定義變量,并且支持塊級作用域??磧蓚€(gè)例子:ES6之前,我們使用var關(guān)鍵字定義變量:function func() if (true) var tmp = 123;console.log(tmp); / 123之所以能夠訪問,是因?yàn)関ar關(guān)鍵字聲明的變量有一個(gè)變量提升的過程。而在ES6場景,推薦使用let關(guān)鍵字定義變量:function func() if (true) let tmp = 123;console.log(tmp); / ReferenceError: tmp is not defined這種方式,能夠避免很多錯誤。什么是this上下文上下文通常取決于函數(shù)是如何被調(diào)用的。當(dāng)一個(gè)函數(shù)被作為對象中的一個(gè)方法被調(diào)用的時(shí)候,this被設(shè)置為調(diào)用該方法的對象上:var obj = foo: function()alert(this = obj);obj.foo(); / true這個(gè)準(zhǔn)則也適用于當(dāng)調(diào)用函數(shù)時(shí)使用new操作符來創(chuàng)建對象的實(shí)例的情況下。在這種情況下,在函數(shù)的作用域內(nèi)部this的值被設(shè)置為新創(chuàng)建的實(shí)例:function foo()alert(this);new foo() / foofoo() / window當(dāng)調(diào)用一個(gè)為綁定函數(shù)時(shí),this默認(rèn)情況下是全局上下文,在瀏覽器中它指向window對象。需要注意的是,ES5引入了嚴(yán)格模式的概念, 如果啟用了嚴(yán)格模式,此時(shí)上下文默認(rèn)為undefined。執(zhí)行環(huán)境(execution context)Java是一個(gè)單線程語言,意味著同一時(shí)間只能執(zhí)行一個(gè)任務(wù)。當(dāng)Java解釋器初始化執(zhí)行代碼時(shí), 它首先默認(rèn)進(jìn)入全局執(zhí)行環(huán)境(execution context),從此刻開始,函數(shù)的每次調(diào)用都會創(chuàng)建一個(gè)新的執(zhí)行環(huán)境。這里會經(jīng)常引起新手的困惑,這里提到了一個(gè)新的術(shù)語執(zhí)行環(huán)境(execution context),它定義了變量或函數(shù)有權(quán)訪問的其他數(shù)據(jù),決定了它們各自的行為。 它更偏向于作用域的作用,而不是我們前面討論的上下文(Context)。請務(wù)必仔細(xì)的區(qū)分執(zhí)行環(huán)境和上下文這兩個(gè)概念(注:英文容易造成混淆)。 說實(shí)話,這是個(gè)非常糟糕的命名約定,但是它是ECMA規(guī)范制定的,你還是遵守吧。每個(gè)函數(shù)都有自己的執(zhí)行環(huán)境。當(dāng)執(zhí)行流進(jìn)入一個(gè)函數(shù)時(shí),函數(shù)的環(huán)境就會被推入一個(gè)環(huán)境棧中(execution stack)。在函數(shù)執(zhí)行完后,棧將其環(huán)境彈出, 把控制權(quán)返回給之前的執(zhí)行環(huán)境。ECMA程序中的執(zhí)行流正是由這個(gè)便利的機(jī)制控制著。執(zhí)行環(huán)境可以分為創(chuàng)建和執(zhí)行兩個(gè)階段。在創(chuàng)建階段,解析器首先會創(chuàng)建一個(gè)變量對象(variable object,也稱為活動對象 activation object), 它由定義在執(zhí)行環(huán)境中的變量、函數(shù)聲明、和參數(shù)組成。在這個(gè)階段,作用域鏈會被初始化,this的值也會被最終確定。 在執(zhí)行階段,代碼被解釋執(zhí)行。每個(gè)執(zhí)行環(huán)境都有一個(gè)與之關(guān)聯(lián)的變量對象(variable object),環(huán)境中定義的所有變量和函數(shù)都保存在這個(gè)對象中。 需要知道,我們無法手動訪問這個(gè)對象,只有解析器才能訪問它。作用域鏈(The Scope Chain)當(dāng)代碼在一個(gè)環(huán)境中執(zhí)行時(shí),會創(chuàng)建變量對象的一個(gè)作用域鏈(scope chain)。作用域鏈的用途是保證對執(zhí)行環(huán)境有權(quán)訪問的所有變量和函數(shù)的有序訪問。 作用域鏈包含了在環(huán)境棧中的每個(gè)執(zhí)行環(huán)境對應(yīng)的變量對象。通過作用域鏈,可以決定變量的訪問和標(biāo)識符的解析。 注意,全局執(zhí)行環(huán)境的變量對象始終都是作用域鏈的最后一個(gè)對象。我們來看一個(gè)例子:var color = blue;function changeColor()var anotherColor = red;function swapColors()var tempColor = anotherColor;anotherColor = color;color = tempColor;/ 這里可以訪問color, anotherColor, 和 tempColor/ 這里可以訪問color 和 anotherColor,但是不能訪問 tempColorswapColors();changeColor();/ 這里只能訪問colorconsole.log(Color is now + color);上述代碼一共包括三個(gè)執(zhí)行環(huán)境:全局環(huán)境、changeColor()的局部環(huán)境、swapColors()的局部環(huán)境。 上述程序的作用域鏈如下圖所示:從上圖發(fā)現(xiàn)。內(nèi)部環(huán)境可以通過作用域鏈訪問所有的外部環(huán)境,但是外部環(huán)境不能訪問內(nèi)部環(huán)境中的任何變量和函數(shù)。 這些環(huán)境之間的聯(lián)系是線性的、有次序的。對于標(biāo)識符解析(變量名或函數(shù)名搜索)是沿著作用域鏈一級一級地搜索標(biāo)識符的過程。搜索過程始終從作用域鏈的前端開始, 然后逐級地向后(全局執(zhí)行環(huán)境)回溯,直到找到標(biāo)識符為止。閉包閉包是指有權(quán)訪問另一函數(shù)作用域中的變量的函數(shù)。換句話說,在函數(shù)內(nèi)定義一個(gè)嵌套的函數(shù)時(shí),就構(gòu)成了一個(gè)閉包, 它允許嵌套函數(shù)訪問外層函數(shù)的變量。通過返回嵌套函數(shù),允許你維護(hù)對外部函數(shù)中局部變量、參數(shù)、和內(nèi)函數(shù)聲明的訪問。 這種封裝允許你在外部作用域中隱藏和保護(hù)執(zhí)行環(huán)境,并且暴露公共接口,進(jìn)而通過公共接口執(zhí)行進(jìn)一步的操作??梢钥磦€(gè)簡單的例子:function foo()var localVariable = private variable;return function bar()return localVariable;var getLocalVariable = foo();getLocalVariable() / private variable模塊模式最流行的閉包類型之一,它允許你模擬公共的、私有的、和特權(quán)成員:var Module = (function()var privateProperty = foo;function privateMethod(args)/ do somethingreturn publicProperty: ,publicMethod: function(args)/ do something,privilegedMethod: function(args)return privateMethod(args);)();模塊類似于一個(gè)單例對象。由于在上面的代碼中我們利用了(function() . )();的匿名函數(shù)形式,因此當(dāng)編譯器解析它的時(shí)候會立即執(zhí)行。 在閉包的執(zhí)行上下文的外部唯一可以訪問的對象是位于返回對象中的公共方法和屬性。然而,因?yàn)閳?zhí)行上下文被保存的緣故, 所有的私有屬性和方法將一直存在于應(yīng)用的整個(gè)生命周期,這意味著我們只有通過公共方法才可以與它們交互。另一種類型的閉包被稱為立即執(zhí)行的函數(shù)表達(dá)式(IIFE)。其實(shí)它很簡單,只不過是一個(gè)在全局環(huán)境中自執(zhí)行的匿名函數(shù)而已:(function(window)var foo, bar;function private()/ do somethingwindow.Module = public: function()/ do something;)(this);對于保護(hù)全局命名空間免受變量污染而言,這種表達(dá)式非常有用,它通過構(gòu)建函數(shù)作用域的形式將變量與全局命名空間隔離, 并通過閉包的形式讓它們存在于整個(gè)運(yùn)行時(shí)(runtime)。在很多的應(yīng)用和框架中,這種封裝源代碼的方式用處非常的流行, 通常都是通過暴露一個(gè)單一的全局接口的方式與外部進(jìn)行交互。Call和Apply這兩個(gè)方法內(nèi)建在所有的函數(shù)中(它們是Function對象的原型方法),允許你在自定義上下文中執(zhí)行函數(shù)。 不同點(diǎn)在于,call函數(shù)需要參數(shù)列表,而apply函數(shù)需要你提供一個(gè)參數(shù)數(shù)組。如下:var o = ;function f(a, b) return a + b;/ 將函數(shù)f作為o的方法,實(shí)際上就是重新設(shè)置函數(shù)f的上下文f.call(o, 1, 2); / 3f.apply(o, 1, 2); / 3兩個(gè)結(jié)果是相同的,函數(shù)f在對象o的上下文中被調(diào)用,并提供了兩個(gè)相同的參數(shù)1和2。在ES5中引入了Ftotype.bind方法,用于控制函數(shù)的執(zhí)行上下文,它會返回一個(gè)新的函數(shù), 并且這個(gè)新函數(shù)會被永久的綁定到bind方法的第一個(gè)參數(shù)所指定的對象上,無論該函數(shù)被如何使用。 它通過閉包將函數(shù)引導(dǎo)到正確的上下文中。對于低版本瀏覽器,我們可以簡單的對它進(jìn)行實(shí)現(xiàn)如下(polyfill):if(!(bind in Ftotype)Ftotype.bind = function()var fn = this,context = arguments0,args = Atotype.slice.call(arguments, 1);return function()return fn.apply(context, args.concat(arguments);bind()方法通常被用在上下文丟失的場景下,例如面向?qū)ο蠛褪录幚?。之所以要這么做, 是因?yàn)楣?jié)點(diǎn)的addEventListener方法總是為事件處理器所綁定的節(jié)點(diǎn)的上下文中執(zhí)行回調(diào)函數(shù), 這就是它應(yīng)該表現(xiàn)的那樣。但是,如果你想要使用高級的面向?qū)ο蠹夹g(shù),或需要你的回調(diào)函數(shù)成為某個(gè)方法的實(shí)例, 你將需要手動調(diào)整上下文。這就是bind方法所帶來的便利之處:function MyClass()this.element = document.(div);this.element.addEventListener(click, this.onClick.bind(this), false);MyCtotype.onClick = function(e)/ do something;回顧上面bind方法的源代碼,你可能會注意到有兩次調(diào)用涉及到了Array的slice方法:Atotype.slice.call(arguments, 1);.slice.call(arguments);我們知道,arguments對象并不是一個(gè)真正的數(shù)組,而是一個(gè)類數(shù)組對象,雖然具有l(wèi)ength屬性,并且值也能夠被索引, 但是它們不支持原生的數(shù)組方法,例如slice和push。但是,由于它們具有和數(shù)組類似的行為,數(shù)組的方法能夠被調(diào)用和劫持, 因此我們可以通過類似于上面代碼的方式達(dá)到這個(gè)目的,其核心是利用call方法。這種調(diào)用其他對象方法的技術(shù)也可以被應(yīng)用到面向?qū)ο笾?,我們可以在Java中模擬經(jīng)典的繼承方式:MyCtotype.init = function()/ call the superclass init method in the context of the MyClass instanceMySuperCtotype.init.apply(this, arguments);也就是利用call或apply在子類(MyClass)的實(shí)例中調(diào)用超類(MySuperClass)的方法。ES6中的箭頭函數(shù)ES6中的箭頭函數(shù)可以作為Ftotype.bind()的替代品。和普通函數(shù)不同,箭頭函數(shù)沒有它自己的this值, 它的this值繼承自外圍作用域。對于普通函數(shù)而言,它總會自動接收一個(gè)this值,this的指向取決于它調(diào)用的方式。我們來看一個(gè)例子:var obj = / .addAll: function (pieces) var self = this;_.each(pieces, function (piece) self.add(piece););,/ .在上面的例子中,最直接的想法是直接使用this.add(piece),但不幸的是,在Java中你不能這么做, 因?yàn)閑ach的回調(diào)函數(shù)并未從外層繼承this值。在該回調(diào)函數(shù)中,this的值為window或undefined, 因此,我們使用臨時(shí)變量self來將外部的this值導(dǎo)入內(nèi)部。我們還有兩種方法解決這個(gè)問題:使用ES5中的bind()方法var obj = / .addAll: function (pieces) _.each(pieces, function (piece
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 幼兒園托班語言發(fā)展計(jì)劃
- 2025年公共事業(yè)單位企業(yè)文化建設(shè)計(jì)劃
- 施工現(xiàn)場安全培訓(xùn)與治安保衛(wèi)管理計(jì)劃
- 2025教科版一年級科學(xué)上冊教案計(jì)劃
- 二年級數(shù)學(xué)解題技巧訓(xùn)練活動計(jì)劃
- 湘教版三年級美術(shù)教學(xué)方案落地計(jì)劃
- 以規(guī)范化疼痛教育賦能胃癌手術(shù)患者術(shù)后鎮(zhèn)痛自我管理
- 以行為文化塑造國家示范性綜合實(shí)踐基地靈魂-M基地深度剖析與啟示
- 以自我解釋賦能高中物理問題解決教學(xué)的深度探索
- 以耗散結(jié)構(gòu)理論賦能中學(xué)語文閱讀教學(xué):突破困境與創(chuàng)新發(fā)展
- 臨近建構(gòu)筑物的低凈空硬法咬合樁施工工法
- 老年人消防安全知識普及
- 國開《工程經(jīng)濟(jì)與管理》形考任務(wù)1-12試題及答案
- 幼兒園玩教具明細(xì)表
- 旅游接待業(yè) 習(xí)題及答案匯總 重大 第1-10章 題庫
- 隋唐人的日常生活
- 2022年江蘇省公安廳招聘警務(wù)輔助人員和雇員筆試試題及答案
- 畢業(yè)50周年同學(xué)聚會邀請函匯編4篇
- 寧夏西吉縣公開招考10名城市社區(qū)工作者高頻考點(diǎn)題庫模擬預(yù)測試卷(共1000練習(xí)題含答案解析)
- 亞科科技(安慶)有限公司高端生物緩沖劑及配套項(xiàng)目(一期)環(huán)境影響報(bào)告書
- 土地評估報(bào)告書范文(通用6篇)
評論
0/150
提交評論