深入理解JavaScript中的作用域和上下文

介紹

JavaScript中有一個(gè)被稱為作用域(Scope)的特性灸芳。雖然對(duì)于許多新手開發(fā)者來(lái)說(shuō)珠移,作用域的概念并不是很容易理解倔丈,我會(huì)盡我所能用最簡(jiǎn)單的方式來(lái)解釋作用域灰嫉。理解作用域?qū)⑹鼓愕拇a脫穎而出郊酒,減少錯(cuò)誤遇绞,并幫助您使用它強(qiáng)大的設(shè)計(jì)模式键袱。

什么是作用域(Scope)?

作用域是在運(yùn)行時(shí)代碼中的某些特定部分中變量摹闽,函數(shù)和對(duì)象的可訪問(wèn)性蹄咖。換句話說(shuō),作用域決定了代碼區(qū)塊中變量和其他資源的可見性付鹿。

為什么說(shuō)作用域是最小訪問(wèn)原則澜汤?

那么,為什么要限制變量的可見性呢倘屹,為什么你的變量不是在代碼的任何地方都可用呢银亲?一個(gè)優(yōu)點(diǎn)是作用域?yàn)槟拇a提供了一定程度的安全性。計(jì)算機(jī)安全的一個(gè)常見原則是用戶應(yīng)該一次只能訪問(wèn)他們需要的東西纽匙。

想象一下計(jì)算機(jī)管理員务蝠。由于他們對(duì)公司的系統(tǒng)有很多控制權(quán)限,因此向他們授予超級(jí)管理員權(quán)限就好了烛缔。他們都可以完全訪問(wèn)系統(tǒng)馏段,一切工作順利。但突然發(fā)生了一些壞事践瓷,你的系統(tǒng)感染了惡意病毒≡合玻現(xiàn)在你不知道誰(shuí)犯的錯(cuò)誤?你意識(shí)到應(yīng)該授予普通用戶權(quán)限晕翠,并且只在需要時(shí)授予超級(jí)訪問(wèn)權(quán)限喷舀。這將幫助您跟蹤更改,并記錄誰(shuí)擁有什么帳戶淋肾。這被稱為最小訪問(wèn)原則硫麻。看起來(lái)很直觀樊卓?這個(gè)原則也適用于編程語(yǔ)言設(shè)計(jì)拿愧,在大多數(shù)編程語(yǔ)言中被稱為作用域,包括我們接下來(lái)要研究的 JavaScript 碌尔。

當(dāng)你繼續(xù)在你的編程旅程浇辜,您將意識(shí)到,您的代碼的作用域有助于提高效率唾戚,幫助跟蹤錯(cuò)誤并修復(fù)它們柳洋。作用域還解決了命名問(wèn)題,在不同作用域中變量名稱可以相同叹坦。記住不要將作用域與上下文混淆膳灶。它們的特性不同。

JavaScript中的作用域

在JavaScript中有兩種類型的作用域:

全局作用域

局部作用域(也叫本地作用域)

定義在函數(shù)內(nèi)部的變量具有局部作用域,而定義在函數(shù)外部的變量具有全局范圍內(nèi)轧钓。每個(gè)函數(shù)在被調(diào)用時(shí)都會(huì)創(chuàng)建一個(gè)新的作用域序厉。

全局作用域

當(dāng)您開始在文檔中編寫JavaScript時(shí),您已經(jīng)在全局作用域中了毕箍。全局作用域貫穿整個(gè)javascript文檔弛房。如果變量在函數(shù)之外定義,則變量處于全局作用域內(nèi)而柑。

JavaScript代碼:

// 默認(rèn)全局作用域

varname='Hammad';

在全局作用域內(nèi)的變量可以在任何其他作用域內(nèi)訪問(wèn)和修改文捶。

JavaScript代碼:

varname='Hammad';

console.log(name);// logs 'Hammad'

functionlogName(){

console.log(name);// 'name' 可以在這里和其他任何地方被訪問(wèn)

}

logName();// logs 'Hammad'

局部作用域

函數(shù)內(nèi)定義的變量在局部(本地)作用域中。而且個(gè)函數(shù)被調(diào)用時(shí)都具有不同的作用域媒咳。這意味著具有相同名稱的變量可以在不同的函數(shù)中使用粹排。這是因?yàn)檫@些變量被綁定到它們各自具有不同作用域的相應(yīng)函數(shù),并且在其他函數(shù)中不可訪問(wèn)涩澡。

JavaScript代碼:

// Global Scope

functionsomeFunction(){

// Local Scope #1

functionsomeOtherFunction(){

// Local Scope #2

}

}

// Global Scope

functionanotherFunction(){

// Local Scope #3

}

// Global Scope

塊語(yǔ)句

塊語(yǔ)句顽耳,如if和switch條件語(yǔ)句或for和while循環(huán)語(yǔ)句,不像函數(shù)妙同,它們不會(huì)創(chuàng)建一個(gè)新的作用域射富。在塊語(yǔ)句中定義的變量將保留在它們已經(jīng)存在的作用域中。

JavaScript代碼:

if(true){

// 'if' 條件語(yǔ)句塊不會(huì)創(chuàng)建一個(gè)新的作用域

varname='Hammad';// name 依然在全局作用域中

}

console.log(name);// logs 'Hammad'

ECMAScript 6 引入了let和const關(guān)鍵字粥帚∫群模可以使用這些關(guān)鍵字來(lái)代替var關(guān)鍵字。

JavaScript代碼:

varname='Hammad';

let likes='Coding';

constskills='Javascript and PHP';

與var關(guān)鍵字相反芒涡,let和const關(guān)鍵字支持在局部(本地)作用域的塊語(yǔ)句中聲明柴灯。

JavaScript代碼:

if(true){

// 'if' 條件語(yǔ)句塊不會(huì)創(chuàng)建一個(gè)新的作用域

// name 在全局作用域中,因?yàn)橥ㄟ^(guò) 'var' 關(guān)鍵字定義

varname='Hammad';

// likes 在局部(本地)作用域中费尽,因?yàn)橥ㄟ^(guò) 'let' 關(guān)鍵字定義

let likes='Coding';

// skills 在局部(本地)作用域中赠群,因?yàn)橥ㄟ^(guò) 'const' 關(guān)鍵字定義

constskills='JavaScript and PHP';

}

console.log(name);// logs 'Hammad'

console.log(likes);// Uncaught ReferenceError: likes is not defined

console.log(skills);// Uncaught ReferenceError: skills is not defined

只要您的應(yīng)用程序生活,全球作用域就會(huì)生存依啰。 只要您的函數(shù)被調(diào)用并執(zhí)行,局部(本地)作用域就會(huì)存在店枣。

上下文

許多開發(fā)人員經(jīng)乘倬混淆 作用域(scope) 和 上下文(context),很多誤解為它們是相同的概念鸯两。但事實(shí)并非如此闷旧。作用域(scope)我們上面已經(jīng)討論過(guò)了,而上下文(context)是用來(lái)指定代碼某些特定部分中this的值钧唐。作用域(scope) 是指變量的可訪問(wèn)性忙灼,上下文(context)是指this在同一作用域內(nèi)的值。我們也可以使用函數(shù)方法來(lái)改變上下文,將在稍后討論该园。 在全局作用域(scope)中上下文中始終是Window對(duì)象酸舍。(愚人碼頭注:取決于JavaScript 的宿主換環(huán)境,在瀏覽器中在全局作用域(scope)中上下文中始終是Window對(duì)象里初。在Node.js中在全局作用域(scope)中上下文中始終是Global對(duì)象)

JavaScript代碼:

// logs: Window {speechSynthesis: SpeechSynthesis, caches: CacheStorage, localStorage: Storage…}

console.log(this);

functionlogFunction(){

console.log(this);

}

// logs: Window {speechSynthesis: SpeechSynthesis, caches: CacheStorage, localStorage: Storage…}

// 因?yàn)?logFunction() 不是一個(gè)對(duì)象的屬性

logFunction();

如果作用域在對(duì)象的方法中啃勉,則上下文將是該方法所屬的對(duì)象。

ES6代碼:

classUser{

logName(){

console.log(this);

}

}

(newUser).logName();// logs User {}

(new User).logName() 是一種將對(duì)象存儲(chǔ)在變量中然后調(diào)用logName函數(shù)的簡(jiǎn)單方法双妨。在這里淮阐,您不需要?jiǎng)?chuàng)建一個(gè)新的變量。

您會(huì)注意到刁品,如果您使用new關(guān)鍵字調(diào)用函數(shù)泣特,則上下文的值會(huì)有所不同。然后將上下文設(shè)置為被調(diào)用函數(shù)的實(shí)例挑随±磁考慮上面的示例剔桨,通過(guò)new關(guān)鍵字調(diào)用的函數(shù)。

JavaScript代碼:

functionlogFunction(){

console.log(this);

}

newlogFunction();// logs logFunction {}

當(dāng)在嚴(yán)格模式(Strict Mode)中調(diào)用函數(shù)時(shí),上下文將默認(rèn)為undefined郭变。

執(zhí)行期上下文(Execution Context)

愚人碼頭注:這部分解釋建議先查看這篇文章,更加通俗易懂铜邮,http://www.css88.com/archives/7262

上面我們了解了作用域和上下文萨蚕,為了消除混亂,特別需要注意的是担猛,執(zhí)行期上下文中的上下文這個(gè)詞語(yǔ)是指作用域而不是上下文幕垦。這是一個(gè)奇怪的命名約定,但由于JavaScipt規(guī)范傅联,我們必須鏈接他們這間的聯(lián)系先改。

JavaScript是一種單線程語(yǔ)言,因此它一次只能執(zhí)行一個(gè)任務(wù)蒸走。其余的任務(wù)在執(zhí)行期上下文中排隊(duì)仇奶。正如我剛才所說(shuō),當(dāng) JavaScript 解釋器開始執(zhí)行代碼時(shí)比驻,上下文(作用域)默認(rèn)設(shè)置為全局该溯。這個(gè)全局上下文附加到執(zhí)行期上下文中,實(shí)際上是啟動(dòng)執(zhí)行期上下文的第一個(gè)上下文别惦。

之后狈茉,每個(gè)函數(shù)調(diào)用(啟用)將其上下文附加到執(zhí)行期上下文中。當(dāng)另一個(gè)函數(shù)在該函數(shù)或其他地方被調(diào)用時(shí)掸掸,會(huì)發(fā)生同樣的事情氯庆。

每個(gè)函數(shù)都會(huì)創(chuàng)建自己的執(zhí)行期上下文蹭秋。

一旦瀏覽器完成了該上下文中的代碼,那么該上下文將從執(zhí)行期上下文中銷毀堤撵,并且執(zhí)行期上下文中的當(dāng)前上下文的狀態(tài)將被傳送到父級(jí)上下文中仁讨。 瀏覽器總是執(zhí)行堆棧頂部的執(zhí)行期上下文(這實(shí)際上是代碼中最深層次的作用域)。

無(wú)論有多少個(gè)函數(shù)上下文粒督,但是全局上下文只有一個(gè)陪竿。

執(zhí)行期上下文有創(chuàng)建和代碼執(zhí)行的兩個(gè)階段。

創(chuàng)建階段

第一階段是創(chuàng)建階段屠橄,當(dāng)一個(gè)函數(shù)被調(diào)用但是其代碼還沒有被執(zhí)行的時(shí)族跛。 在創(chuàng)建階段主要做的三件事情是:

創(chuàng)建變量(激活)對(duì)象

創(chuàng)建作用域鏈

設(shè)置上下文(context)的值( `this` )

變量對(duì)象

變量對(duì)象,也稱為激活對(duì)象锐墙,包含在執(zhí)行期上下文中定義的所有變量礁哄,函數(shù)和其他聲明。當(dāng)調(diào)用函數(shù)時(shí)溪北,解析器掃描它所有的資源桐绒,包括函數(shù)參數(shù),變量和其他聲明之拨。包裝成一個(gè)單一的對(duì)象茉继,即變量對(duì)象。

JavaScript代碼:

'variableObject':{

// 包含函數(shù)參數(shù)蚀乔,內(nèi)部變量和函數(shù)聲明

}

作用域鏈

在執(zhí)行期上下文的創(chuàng)建階段烁竭,作用域鏈?zhǔn)窃谧兞繉?duì)象之后創(chuàng)建的。作用域鏈本身包含變量對(duì)象吉挣。作用域鏈用于解析變量派撕。當(dāng)被要求解析變量時(shí),JavaScript 始終從代碼嵌套的最內(nèi)層開始睬魂,如果最內(nèi)層沒有找到變量终吼,就會(huì)跳轉(zhuǎn)到上一層父作用域中查找,直到找到該變量或其他任何資源為止氯哮。作用域鏈可以簡(jiǎn)單地定義為包含其自身執(zhí)行上下文的變量對(duì)象的對(duì)象际跪,以及其父級(jí)對(duì)象的所有其他執(zhí)行期上下文,一個(gè)具有很多其他對(duì)象的對(duì)象喉钢。

JavaScript代碼:

'scopeChain':{

// 包含自己的變量對(duì)象和父級(jí)執(zhí)行上下文的其他變量對(duì)象

}

執(zhí)行期上下文對(duì)象

執(zhí)行期上下文可以表示為一個(gè)抽象對(duì)象姆打,如下所示:

JavaScript代碼:

executionContextObject={

'scopeChain':{},// 包含自己的變量對(duì)象和父級(jí)執(zhí)行上下文的其他變量對(duì)象

'variableObject':{},// 包含函數(shù)參數(shù),內(nèi)部變量和函數(shù)聲明

'this':valueOfThis

}

代碼執(zhí)行階段

在執(zhí)行期上下文的第二階段出牧,即代碼執(zhí)行階段穴肘,分配其他值并最終執(zhí)行代碼歇盼。

詞法作用域

詞法作用域意味著在一組嵌套的函數(shù)中舔痕,內(nèi)部函數(shù)可以訪問(wèn)其父級(jí)作用域中的變量和其他資源。這意味著子函數(shù)在詞法作用域上綁定到他們父級(jí)的執(zhí)行期上下文。詞法作用域有時(shí)也被稱為靜態(tài)作用域伯复。

JavaScript代碼:

functiongrandfather(){

varname='Hammad';

// likes 在這里不可以被訪問(wèn)

functionparent(){

// name 在這里可以被訪問(wèn)

// likes 在這里不可以被訪問(wèn)

functionchild(){

// 作用域鏈最深層

// name 在這里也可以被訪問(wèn)

varlikes='Coding';

}

}

}

你會(huì)注意到詞法作用域向內(nèi)傳遞的慨代,意味著name可以通過(guò)它的子級(jí)期執(zhí)行期上下文訪問(wèn)。但是啸如,但是它不能向其父對(duì)象反向傳遞侍匙,意味著變量likes不能被其父對(duì)象訪問(wèn)。這也告訴我們叮雳,在不同執(zhí)行上下文中具有相同名稱的變量從執(zhí)行堆棧的頂部到底部獲得優(yōu)先級(jí)想暗。在最內(nèi)層函數(shù)(執(zhí)行堆棧的最上層上下文)中,具有類似于另一變量的名稱的變量將具有較高優(yōu)先級(jí)帘不。

閉包(?Closures)

愚人碼頭注:這部分解釋建議先查看這篇文章说莫,更加通俗易懂,http://www.css88.com/archives/7262

閉包的概念與我們?cè)谏厦嬷v的詞法作用域密切相關(guān)寞焙。 當(dāng)內(nèi)部函數(shù)嘗試訪問(wèn)其外部函數(shù)的作用域鏈储狭,即在直接詞法作用域之外的變量時(shí),會(huì)創(chuàng)建一個(gè)閉包捣郊。 閉包包含自己的作用域鏈辽狈,父級(jí)的作用域鏈和全局作用域。

閉包不僅可以訪問(wèn)其外部函數(shù)中定義的變量呛牲,還可以訪問(wèn)外部函數(shù)的參數(shù)刮萌。

即使函數(shù)返回后,閉包也可以訪問(wèn)其外部函數(shù)的變量侈净。這允許返回的函數(shù)保持對(duì)外部函數(shù)所有資源的訪問(wèn)尊勿。

當(dāng)從函數(shù)返回內(nèi)部函數(shù)時(shí),當(dāng)您嘗試調(diào)用外部函數(shù)時(shí)畜侦,不會(huì)調(diào)用返回的函數(shù)元扔。您必須首先將外部函數(shù)的調(diào)用保存在單獨(dú)的變量中,然后將該變量調(diào)用為函數(shù)旋膳∨煊铮考慮這個(gè)例子:

JavaScript代碼:

functiongreet(){

name='Hammad';

returnfunction(){

console.log('Hi '+name);

}

}

greet();// 什么都沒發(fā)生,沒有錯(cuò)誤

// 從 greet() 中返回的函數(shù)保存到 greetLetter 變量中

greetLetter=greet();

// 調(diào)用? greetLetter 相當(dāng)于調(diào)用從 greet() 函數(shù)中返回的函數(shù)

greetLetter();// logs 'Hi Hammad'

這里要注意的是验懊,greetLetter函數(shù)即使在返回后也可以訪問(wèn)greet函數(shù)的name變量擅羞。 有一種方法不需要分配一個(gè)變量來(lái)訪問(wèn)greet函數(shù)返回的函數(shù),即通過(guò)使用兩次括號(hào)()义图,即()()來(lái)調(diào)用减俏,就是這樣:

JavaScript代碼:

functiongreet(){

name='Hammad';

returnfunction(){

console.log('Hi '+name);

}

}

greet()();// logs 'Hi Hammad'

公共作用域和私有作用域

在許多其他編程語(yǔ)言中,您可以使用公共碱工,私有和受保護(hù)的作用域來(lái)設(shè)置類的屬性和方法的可見性娃承∽喾颍考慮使用PHP語(yǔ)言的這個(gè)例子:

PHP代碼:

// Public Scope

public$property;

publicfunctionmethod(){

// ...

}

// Private Sccpe

private$property;

privatefunctionmethod(){

// ...

}

// Protected Scope

protected$property;

protectedfunctionmethod(){

// ...

}

來(lái)自公共(全局)作用域的封裝函數(shù)使他們免受脆弱的攻擊。但是在JavaScript中历筝,沒有公共或私有作用域酗昼。幸好,我們可以使用閉包來(lái)模擬此功能梳猪。為了保持一切與全局分離麻削,我們必須首先將我們的函數(shù)封裝在如下所示的函數(shù)中:

JavaScript代碼:

(function(){

// 私有作用域 private scope

})();

函數(shù)末尾的括號(hào)會(huì)告知解析器在沒有調(diào)用的情況下一旦讀取完成就立即執(zhí)行它。(愚人碼頭注:這其實(shí)叫立即執(zhí)行函數(shù)表達(dá)式)我們可以在其中添加函數(shù)和變量春弥,它們將不能在外部訪問(wèn)呛哟。但是,如果我們想在外部訪問(wèn)它們匿沛,也就是說(shuō)我們希望其中一些公開的竖共,另一些是私有的?我們可以使用一種稱為 模塊模式 的閉包類型俺祠,它允許我們使用對(duì)象中公共和私有的作用域來(lái)對(duì)我們的函數(shù)進(jìn)行調(diào)整公给。

模塊模式

模塊模式類似這樣:

JavaScript代碼:

varModule=(function(){

functionprivateMethod(){

// do something

}

return{

publicMethod:function(){

// can call privateMethod();

}

};

})();

Module中的return語(yǔ)句包含了我們公開的函數(shù)。私有函數(shù)只是那些沒有返回的函數(shù)蜘渣。沒有返回的函數(shù)不可以在Module命名空間之外訪問(wèn)淌铐。但是公開函數(shù)可以訪問(wèn)私有函數(shù),這使它們對(duì)于助手函數(shù)蔫缸,AJAX調(diào)用和其他事情很方便腿准。

JavaScript代碼:

Module.publicMethod();// 可以正常工作

Module.privateMethod();// Uncaught ReferenceError: privateMethod is not defined

私有函數(shù)一個(gè)慣例是用下劃線開始,并返回一個(gè)包含我們公共函數(shù)的匿名對(duì)象拾碌。這使得它們很容易在長(zhǎng)對(duì)象中管理吐葱。它看起來(lái)是這樣子的:

JavaScript代碼:

varModule=(function(){

function_privateMethod(){

// do something

}

functionpublicMethod(){

// do something

}

return{

publicMethod:publicMethod,

}

})();

立即執(zhí)行函數(shù)表達(dá)式(IIFE)

另一種類型的閉包是立即執(zhí)行函數(shù)表達(dá)式(IIFE)。這是一個(gè)在window上下文中調(diào)用的自動(dòng)調(diào)用的匿名函數(shù)校翔,這意味著this的值為window弟跑。暴露一個(gè)單一的全局接口來(lái)進(jìn)行交互。他是這樣的:

JavaScript代碼:

(function(window){

// do anything

})(this);

使用 .call(), .apply() 和 .bind() 改變上下文

.call()和.apply()函數(shù)用于在調(diào)用函數(shù)時(shí)改變上下文防症。這給了你令人難以置信的編程能力(和一些終極權(quán)限來(lái)駕馭代碼)孟辑。

要使用call或apply函數(shù),您只需要在函數(shù)上調(diào)用它蔫敲,而不是使用一對(duì)括號(hào)調(diào)用函數(shù)饲嗽,并將新的上下文作為第一個(gè)參數(shù)傳遞。

函數(shù)自己的參數(shù)可以在上下文之后傳遞奈嘿。(愚人碼頭注:call或apply用另一個(gè)對(duì)象來(lái)調(diào)用一個(gè)方法貌虾,將一個(gè)函數(shù)上下文從初始的上下文改變?yōu)橹付ǖ男聦?duì)象。簡(jiǎn)單的說(shuō)就是改變函數(shù)執(zhí)行的上下文裙犹。)

JavaScript代碼:

functionhello(){

// do something...

}

hello();// 通常的調(diào)用方式

hello.call(context);// 在這里你可以傳遞上下文(this 值)作為第一個(gè)參數(shù)

hello.apply(context);// 在這里你可以傳遞上下文(this 值)作為第一個(gè)參數(shù)

.call()和.apply()之間的區(qū)別在于尽狠,在.call()中榴鼎,其余參數(shù)作為以逗號(hào)分隔的列表,而.apply()則允許您在數(shù)組中傳遞參數(shù)晚唇。

JavaScript代碼:

functionintroduce(name,interest){

console.log('Hi! I\'m '+name+' and I like '+interest+'.');

console.log('The value of this is '+this+'.')

}

introduce('Hammad','Coding');// 通常的調(diào)用方式

introduce.call(window,'Batman','to save Gotham');// 在上下文之后逐個(gè)傳遞參數(shù)

introduce.apply('Hi',['Bruce Wayne','businesses']);// 在上下文之后傳遞數(shù)組中的參數(shù)

// 輸出:

// Hi! I'm Hammad and I like Coding.

// The value of this is [object Window].

// Hi! I'm Batman and I like to save Gotham.

// The value of this is [object Window].

// Hi! I'm Bruce Wayne and I like businesses.

// The value of this is Hi.

.call()的性能要比.apply()稍快。

以下示例將文檔中的項(xiàng)目列表逐個(gè)記錄到控制臺(tái)盗似。

HTML 代碼:

Things to learn

Things to Learn to Rule the World

    Learn PHP

    Learn Laravel

    Learn JavaScript

    Learn VueJS

    Learn CLI

    Learn Git

    Learn Astral Projection

    // 在listItems中保存頁(yè)面上所有列表項(xiàng)的NodeList

    varlistItems=document.querySelectorAll('ul li');

    // 循環(huán)遍歷listItems NodeList中的每個(gè)節(jié)點(diǎn)哩陕,并記錄其內(nèi)容

    for(vari=0;i

    (function(){

    console.log(this.innerHTML);

    }).call(listItems[i]);

    }

    // Output logs:

    // Learn PHP

    // Learn Laravel

    // Learn JavaScript

    // Learn VueJS

    // Learn CLI

    // Learn Git

    // Learn Astral Projection

    HTML僅包含無(wú)序的項(xiàng)目列表。然后 JavaScript 從DOM中選擇所有這些項(xiàng)目赫舒。列表循環(huán)悍及,直到列表中的項(xiàng)目結(jié)束。在循環(huán)中接癌,我們將列表項(xiàng)的內(nèi)容記錄到控制臺(tái)心赶。

    該日志語(yǔ)句包裹在一個(gè)函數(shù)中,該call函數(shù)包含在調(diào)用函數(shù)中的括號(hào)中缺猛。將相應(yīng)的列表項(xiàng)傳遞給調(diào)用函數(shù)缨叫,以便控制臺(tái)語(yǔ)句中的this關(guān)鍵字記錄正確對(duì)象的 innerHTML 。

    對(duì)象可以有方法荔燎,同樣的函數(shù)對(duì)象也可以有方法耻姥。 事實(shí)上,JavaScript函數(shù)附帶了四種內(nèi)置方法:

    Function.prototype.apply()

    Function.prototype.bind() ( ECMAScript 5 (ES5) 中引進(jìn))

    Function.prototype.call()

    Function.prototype.toString()

    Function.prototype.toString() 返回函數(shù)源代碼的字符串表示形式有咨。

    到目前為止琐簇,我們討論過(guò).call(),.apply()和toString()座享。與.call()和.apply()不同婉商,.bind()本身不調(diào)用該函數(shù),它只能用于在調(diào)用函數(shù)之前綁定上下文和其他參數(shù)的值渣叛。在上面的一個(gè)例子中使用.bind():

    JavaScript代碼:

    (functionintroduce(name,interest){

    console.log('Hi! I\'m '+name+' and I like '+interest+'.');

    console.log('The value of this is '+this+'.')

    }).bind(window,'Hammad','Cosmology')();

    // logs:

    // Hi! I'm Hammad and I like Cosmology.

    // The value of this is [object Window].

    .bind()就像.call()函數(shù)一樣丈秩,它允許你傳遞其余的參數(shù),用逗號(hào)分隔淳衙,而不是像apply癣籽,在數(shù)組中傳遞參數(shù)。

    結(jié)論

    這些概念是 JavaScript 的根本滤祖,對(duì)于了解高級(jí)語(yǔ)法很重要筷狼。我希望你能更好地了解JavaScript作用域和他相關(guān)的事情。如果沒用弄明白這些問(wèn)題匠童,歡迎在下面的評(píng)論中提問(wèn)埂材。

    學(xué)習(xí)更多

    原文地址:https://scotch.io/tutorials/understanding-scope-in-javascript

    最后編輯于
    ?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
    • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市汤求,隨后出現(xiàn)的幾起案子俏险,更是在濱河造成了極大的恐慌严拒,老刑警劉巖,帶你破解...
      沈念sama閱讀 218,755評(píng)論 6 507
    • 序言:濱河連續(xù)發(fā)生了三起死亡事件竖独,死亡現(xiàn)場(chǎng)離奇詭異裤唠,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)莹痢,發(fā)現(xiàn)死者居然都...
      沈念sama閱讀 93,305評(píng)論 3 395
    • 文/潘曉璐 我一進(jìn)店門种蘸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人竞膳,你說(shuō)我怎么就攤上這事航瞭。” “怎么了坦辟?”我有些...
      開封第一講書人閱讀 165,138評(píng)論 0 355
    • 文/不壞的土叔 我叫張陵刊侯,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我锉走,道長(zhǎng)滨彻,這世上最難降的妖魔是什么? 我笑而不...
      開封第一講書人閱讀 58,791評(píng)論 1 295
    • 正文 為了忘掉前任挪蹭,我火速辦了婚禮疮绷,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘嚣潜。我一直安慰自己冬骚,他們只是感情好,可當(dāng)我...
      茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
    • 文/花漫 我一把揭開白布懂算。 她就那樣靜靜地躺著只冻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪计技。 梳的紋絲不亂的頭發(fā)上喜德,一...
      開封第一講書人閱讀 51,631評(píng)論 1 305
    • 那天,我揣著相機(jī)與錄音垮媒,去河邊找鬼舍悯。 笑死,一個(gè)胖子當(dāng)著我的面吹牛睡雇,可吹牛的內(nèi)容都是我干的萌衬。 我是一名探鬼主播,決...
      沈念sama閱讀 40,362評(píng)論 3 418
    • 文/蒼蘭香墨 我猛地睜開眼它抱,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼秕豫!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
      開封第一講書人閱讀 39,264評(píng)論 0 276
    • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤混移,失蹤者是張志新(化名)和其女友劉穎祠墅,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體歌径,經(jīng)...
      沈念sama閱讀 45,724評(píng)論 1 315
    • 正文 獨(dú)居荒郊野嶺守林人離奇死亡毁嗦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
      茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
    • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了回铛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片狗准。...
      茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
    • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖勺届,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情娶耍,我是刑警寧澤免姿,帶...
      沈念sama閱讀 35,742評(píng)論 5 346
    • 正文 年R本政府宣布,位于F島的核電站榕酒,受9級(jí)特大地震影響胚膊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜想鹰,卻給世界環(huán)境...
      茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
    • 文/蒙蒙 一紊婉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧辑舷,春花似錦喻犁、人聲如沸。這莊子的主人今日做“春日...
      開封第一講書人閱讀 31,944評(píng)論 0 22
    • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至碌廓,卻和暖如春传轰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背谷婆。 一陣腳步聲響...
      開封第一講書人閱讀 33,060評(píng)論 1 270
    • 我被黑心中介騙來(lái)泰國(guó)打工慨蛙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人纪挎。 一個(gè)月前我還...
      沈念sama閱讀 48,247評(píng)論 3 371
    • 正文 我出身青樓期贫,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親异袄。 傳聞我的和親對(duì)象是個(gè)殘疾皇子唯灵,可洞房花燭夜當(dāng)晚...
      茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

    推薦閱讀更多精彩內(nèi)容