原文鏈接:Understanding Scope in JavaScript
原文作者:Hammad Ahmed (@shammadahmed)
前言:
JavaScript有一個(gè)特性叫做作用域。誠(chéng)然,對(duì)于開發(fā)新手來(lái)說(shuō)作用域的概念不那么好理解,我將會(huì)盡我所能用最淺顯易懂的方式向你們解釋作用域的含義。理解作用域的用法可以讓你的代碼錯(cuò)誤率降低、形成優(yōu)雅的代碼風(fēng)格,使你編寫代碼起來(lái)更加的得心應(yīng)手。
什么是作用域:
作用域決定了在你代碼運(yùn)行時(shí),變量、函數(shù)以及對(duì)象是否可以被訪問(wèn)。換言之,作用域決定了這些變量和某些資源的可見(jiàn)性。
相關(guān)課程: Getting Started with JavaScript for Web Development
為什么要使用作用域?什么是最低訪問(wèn)原則
那么限制變量可訪問(wèn)性、不讓變量可隨時(shí)隨地被訪問(wèn)使用的意義在哪里呢?一個(gè)好處是,作用域?yàn)槟愕拇a保證了一定程度上的安全性。計(jì)算機(jī)安全的一個(gè)公共原則就是用戶每次操作只能訪問(wèn)到他們當(dāng)時(shí)需要的東西。
試想作為電腦管理員,他們擁有公司系統(tǒng)的很多控制權(quán)限,看起來(lái)為他們開通完整的訪問(wèn)用戶帳戶似乎是可以的。假設(shè)這個(gè)公司有三位管理員,他們每個(gè)人都有全部的系統(tǒng)權(quán)限并且一切都運(yùn)行正常。但是突然發(fā)生了一些異常,公司的系統(tǒng)之一被感染了惡意病毒。這時(shí)候就無(wú)法斷定這是誰(shuí)導(dǎo)致的問(wèn)題。現(xiàn)在你應(yīng)該知道你應(yīng)該為他們分配基礎(chǔ)賬戶,并只在需要的時(shí)候才賦予他們完全的訪問(wèn)權(quán)限。這將會(huì)幫助你追蹤變化,并記錄下誰(shuí)做了什么。這就被稱為最少訪問(wèn)原則。是不是很直觀?這個(gè)原則同樣被應(yīng)用于程序語(yǔ)言設(shè)計(jì),在包括JavaScript的眾多程序語(yǔ)言中,都被稱為是作用域,我們將在接下來(lái)的學(xué)習(xí)中遇到。
在你繼續(xù)你的編程之路的時(shí)候,你會(huì)發(fā)現(xiàn)使用作用域會(huì)讓你的代碼運(yùn)行更加有效率,讓你更好的減少bug以及debug。作用域的存在還可以讓你在不同作用域下用同名的變量。記住,不要將作用域(scope)與上下文(context)混淆,他們是JavaScript的不同特性。
JavaScript的作用域
在JavaScript中有兩種作用域類型:
- 全局作用域
- 局部作用域
在函數(shù)內(nèi)部定義的變量就是在局部作用域中;
在函數(shù)外部定義的變量就是在全局作用域中;
當(dāng)函數(shù)被調(diào)用時(shí)會(huì)產(chǎn)生一個(gè)新的作用域。
全局作用域
當(dāng)你在文檔中編寫JavaScript時(shí),你就已經(jīng)在全局作用域中。在一個(gè)js文檔中有且僅有一個(gè)全局作用域。如果在函數(shù)外定義一個(gè)變量,那么它的作用域就是全局的(全局都可訪問(wèn)到該變量)。
// 默認(rèn)作用域?yàn)槿肿饔糜?var name = 'Hammad';
在全局作用域中的變量可以在頁(yè)面任何的作用域里被訪問(wèn)和更改。
var name = 'Hammad';
console.log(name); // logs 'Hammad'
function logName() {
console.log(name); // 'name' 可在任何作用域中被訪問(wèn)
}
logName(); // logs 'Hammad'
局部作用域
在函數(shù)內(nèi)部定義的變量的作用域?yàn)榫植孔饔糜颉.?dāng)函數(shù)被不同的方法調(diào)用時(shí),變量也會(huì)獲得不同的作用域。這意味著我們可以在不同的函數(shù)中使用擁有同樣變量名的的變量。這是因?yàn)檫@些變量被綁定到了各自的函數(shù)中,有著自己獨(dú)立的作用域,彼此之間沒(méi)有聯(lián)系、不能互相訪問(wèn)到。
// 全局作用域
function someFunction() {
// 局部作用域 #1
function someOtherFunction() {
// 局部作用域 #2
}
}
// 全局作用域
function anotherFunction() {
// 局部作用域 #3
}
// 全局作用域
塊語(yǔ)句
塊語(yǔ)句,例如 if
和 switch
這樣的條件語(yǔ)句,或是 for
和 while
這樣的循環(huán)語(yǔ)句,不同于函數(shù)的是,它們不會(huì)創(chuàng)建出新的作用域。在塊語(yǔ)句內(nèi)定義的變量將會(huì)保留在全局作用域內(nèi)。
[譯者注]:由于JavaScript的變量作用域?qū)嶋H上是函數(shù)內(nèi)部,我們?cè)趂or循環(huán)等語(yǔ)句塊中是無(wú)法定義具有局部作用域的變量的。
if (true) {
// if條件語(yǔ)句不會(huì)創(chuàng)建新的作用域
var name = 'Hammad'; // name依舊在全局作用域中
}
console.log(name); // logs 'Hammad'
ECMAScript 6 引入了 let
和 const
關(guān)鍵字,這些關(guān)鍵字可以替代var
關(guān)鍵字。
var name = 'Hammad';
let likes = 'Coding';
const skills = 'Javascript and PHP';
不同于 var
的是,在塊語(yǔ)句中以let
和 const
代替 var
聲明變量,可以在塊語(yǔ)句中為其創(chuàng)建出局部作用域。``
if (true) {
// 使用if的條件語(yǔ)句不會(huì)創(chuàng)建出局部作用域
// 由于使用了var聲明變量,name的作用域是全局作用域
var name = 'Hammad';
// likes由let聲明,所以在塊語(yǔ)句的局部作用域中
let likes = 'Coding';
// skills由let聲明,所以在塊語(yǔ)句的局部作用域中
const skills = 'JavaScript and PHP';
}
console.log(name); // logs 'Hammad'
console.log(likes); // Uncaught ReferenceError: likes is not defined 引用錯(cuò)誤
console.log(skills); // Uncaught ReferenceError: skills is not defined 引用錯(cuò)誤
Global scope lives as long as your application lives. Local Scope lives as long as your functions are called and executed.
只要你的應(yīng)用在運(yùn)行,全局作用域就一直存在。而局部作用域只會(huì)在函數(shù)被調(diào)用執(zhí)行時(shí)才會(huì)存在。
上下文
許多開發(fā)者都會(huì)將作用域和上下文混淆,就好像它們引用相同的概念一樣。但并非如此,作用域我們?cè)谏厦孢M(jìn)行了闡述,上下文是在代碼的某些特定部分中引用到的一些外部參數(shù)值。([譯者注]:也就是代碼的執(zhí)行環(huán)境)。
作用域是指變量是否可讀,上下文是指在同一作用域內(nèi)的值。我們也可以。在全局作用作用域中,上下文永遠(yuǎn)是window對(duì)象。
每一段程序都有很多外部變量。只有像Add這種簡(jiǎn)單的函數(shù)才是沒(méi)有外部變量的。一旦你的一段程序有了外部變量,這段程序就不完整,不能獨(dú)立運(yùn)行。你為了使他們運(yùn)行,就要給所有的外部變量一個(gè)一個(gè)寫一些值進(jìn)去。這些值的集合就叫上下文。
// logs: Window {speechSynthesis: SpeechSynthesis, caches: CacheStorage, localStorage: Storage…}
console.log(this);
function logFunction() {
console.log(this);
}
// logs: Window {speechSynthesis: SpeechSynthesis, caches: CacheStorage, localStorage: Storage…}
// 因?yàn)閘ogfunction()不是一個(gè)對(duì)象的屬性
logFunction();
如果作用域在一個(gè)方法或者對(duì)象中,它的上下文將會(huì)是這個(gè)方法所在的對(duì)象。
class User {
logName() {
console.log(this);
}
}
(new User).logName(); // logs User {}
(new User).logName() 是一種簡(jiǎn)潔的調(diào)用方式,在一個(gè)變量中存儲(chǔ)對(duì)象,然后調(diào)用指定的函數(shù)就可以了。。這樣的話就不需要?jiǎng)?chuàng)建一個(gè)新的變量了。
你會(huì)注意到的一點(diǎn)是,如果你用 new
關(guān)鍵字調(diào)用函數(shù),它的上下文環(huán)境將會(huì)有所改變,它會(huì)被設(shè)置為被調(diào)用函數(shù)的實(shí)例的上下文。考慮上面的示例,通過(guò) new
關(guān)鍵字調(diào)用的函數(shù)。
function logFunction() {
console.log(this);
}
new logFunction(); // logs logFunction {}
在嚴(yán)格模式下調(diào)用函數(shù),它的上下文將會(huì)默認(rèn)為
undefined
。
執(zhí)行上下文
上面我們提到了作用域與上下文,為了消除歧義,“執(zhí)行上下文”這個(gè)詞中的“上下文”意指“作用域”而不是我們上面提到的“上下文”。這是一個(gè)怪異的命名規(guī)范,但是基于JavaScript的規(guī)范,我們得把它們聯(lián)系到一塊兒去。
JavaScript是一個(gè)單線程語(yǔ)言,所以一次只能執(zhí)行一個(gè)任務(wù)。其余的任務(wù)要在執(zhí)行上下文中排隊(duì)等待。正如我們上面說(shuō)到的,當(dāng)JavaScript編譯器開始執(zhí)行代碼時(shí),它的上下文環(huán)境將被設(shè)置為全局(也就是說(shuō)它的作用域是全局作用域)。這個(gè)全局上下文被插入到執(zhí)行上下文中,實(shí)際上也是第一個(gè)啟動(dòng)執(zhí)行上下文的上下文。
再之后,每個(gè)函數(shù)在調(diào)用時(shí)都會(huì)將它的上下文注入到執(zhí)行上下文中。當(dāng)另一個(gè)函數(shù)在其他地方或是在該函數(shù)中調(diào)用后,也會(huì)發(fā)生同樣的事情。
一個(gè)函數(shù)在被調(diào)用時(shí)都會(huì)創(chuàng)建出它自己的執(zhí)行上下文。
一旦瀏覽器執(zhí)行完那段上下文中的代碼后,那段上下文將會(huì)從執(zhí)行上下文中銷毀掉,且它在執(zhí)行上下文中的當(dāng)前狀態(tài)會(huì)被傳遞到它的父級(jí)上下文中。瀏覽器總是執(zhí)行執(zhí)行上下文堆棧頂部的上下文(這實(shí)際上是代碼中層次最深的作用域)。
無(wú)論函數(shù)的上下文有多少個(gè),代碼中有且僅有一個(gè)全局上下文。
執(zhí)行上下文分為創(chuàng)建和執(zhí)行兩個(gè)階段。
創(chuàng)建階段
創(chuàng)建階段是執(zhí)行上下文的第一個(gè)階段,它發(fā)生在函數(shù)被調(diào)用但尚未執(zhí)行的時(shí)候。以下是創(chuàng)建階段會(huì)做的操作:
- 創(chuàng)建變量對(duì)象
- 創(chuàng)建作用域鏈
*設(shè)置上下文環(huán)境。(this的值)
變量對(duì)象
變量對(duì)象,也被稱為是激活對(duì)象,它囊括了所有的變量、函數(shù)以及定義在執(zhí)行上下文其他分支中的聲明。當(dāng)一個(gè)函數(shù)被調(diào)用,瀏覽器解析器會(huì)預(yù)先加載所有資源,包括函數(shù)、變量和其他聲明。這些在被包裝成一個(gè)單獨(dú)的對(duì)象后,就會(huì)成為變量對(duì)象。
'variableObject': {
// 包含函數(shù)、參數(shù)、內(nèi)部變量和函數(shù)聲明
}
作用域鏈
在執(zhí)行上下文的創(chuàng)建階段,作用域鏈的創(chuàng)建在變量對(duì)象創(chuàng)建之后。作用域鏈本身包含了變量對(duì)象。作用域鏈用于解析這些變量。當(dāng)要解析一個(gè)變量時(shí),JavaScript會(huì)從代碼嵌套的最內(nèi)層開始并逐步向父級(jí)作用域查找,知道找到它要解析的變量或是相關(guān)的資源為止。作用域鏈可以被簡(jiǎn)單的定義為一個(gè)包含了變量對(duì)象和它的執(zhí)行上下文、以及他們的父級(jí)的執(zhí)行上下文,作用域鏈?zhǔn)且粋€(gè)擁有其他很多對(duì)象的對(duì)象。
'scopeChain': {
// 包含了它自己的變量對(duì)象以及它父級(jí)的變量對(duì)象的執(zhí)行上下文
}
執(zhí)行上下文對(duì)象
執(zhí)行上下文可以被抽象成這樣的一個(gè)對(duì)象:
executionContextObject = {
'scopeChain': {}, // 包含了它自身的變量對(duì)象和其父級(jí)執(zhí)行上下文內(nèi)的變量對(duì)象
'variableObject': {}, // 包含了函數(shù)、內(nèi)部變量和函數(shù)聲明
'this': this 的值
}
代碼執(zhí)行階段
代碼執(zhí)行階段為執(zhí)行上下文創(chuàng)建的第二階段,代碼和數(shù)值的操作將會(huì)被執(zhí)行。
詞法作用域
詞法作用域表示在一組嵌套的函數(shù)中,內(nèi)部函數(shù)可以訪問(wèn)到它外層父級(jí)函數(shù)中的變量和其他一些資源。這意味著子函數(shù)的詞法作用域綁定了它父級(jí)函數(shù)的執(zhí)行上下文。詞法作用域也被稱為靜態(tài)作用域。
function grandfather() {
var name = 'Hammad';
// likes 無(wú)法被訪問(wèn)
function parent() {
// name 可以被訪問(wèn)
// likes 無(wú)法被訪問(wèn)
function child() {
// 這是嵌套里的最深層級(jí)
// name 可以被訪問(wèn)
var likes = 'Coding';
}
}
}
你會(huì)發(fā)現(xiàn)詞法作用域只能向前(自外而內(nèi))作用,name
可以被嵌套的內(nèi)部函數(shù)的執(zhí)行上下文訪問(wèn)。但是這是單向的,不能向后(自內(nèi)而外),likes
不能被它的父級(jí)函數(shù)訪問(wèn)到。這也告訴我們不同的執(zhí)行上下文中的同名的變量會(huì)按照在執(zhí)行堆棧中的順序自上而下執(zhí)行。一個(gè)變量若和另一個(gè)變量同名,嵌套中最深層的函數(shù)(、執(zhí)行堆棧中最上層的上下文)會(huì)有更高的優(yōu)先權(quán)(先被賦值或使用)。
閉包
閉包的概念和我們上面說(shuō)過(guò)的詞法作用域很相似,閉包是在一個(gè)內(nèi)部函數(shù)試圖訪問(wèn)作用域鏈中它的外層函數(shù)、也就是詞法作用域之外的變量時(shí)產(chǎn)生的。閉包里包含它自己的作用域鏈,而作用域鏈里有它們父級(jí)以及全局的作用域。
閉包不僅可以訪問(wèn)它的外部函數(shù)中定義的變量,還可以訪問(wèn)外部函數(shù)的參數(shù)。
即使閉包的外部函數(shù)被返回了,閉包依舊可以訪問(wèn)外部函數(shù)中的變量。這使得被返回的函數(shù)也可以訪問(wèn)它外部函數(shù)中的資源。
當(dāng)從一個(gè)函數(shù)中返回它的一個(gè)內(nèi)部函數(shù),當(dāng)你試圖調(diào)用外部函數(shù)時(shí),那個(gè)被返回的函數(shù)不會(huì)被調(diào)用。我們必須先將要調(diào)用的外部函數(shù)保存在一個(gè)單獨(dú)的變量中,然后再調(diào)用這個(gè)函數(shù),可參考下面的例子:
function greet() {
name = 'Hammad';
return function () {
console.log('Hi ' + name);
}
}
greet(); // 什么也不會(huì)發(fā)生,也沒(méi)有錯(cuò)誤
// greet()中返回的函數(shù)被保存在greetLetter中
greetLetter = greet();
// 調(diào)用的greetLetter 函數(shù)調(diào)用了greet()函數(shù)中被返回的函數(shù)
greetLetter(); // logs 'Hi Hammad'
我們要注意的一點(diǎn)是,greetLetter
這個(gè)函數(shù)可以訪問(wèn)到 greet
這個(gè)函數(shù)中的 name
變量,及時(shí)它被返回了。一種不需要靠重新聲明變量就從 greet
函數(shù)中調(diào)用其返回的函數(shù)的方法是使用兩次括號(hào) ()
,用 ()()
進(jìn)行調(diào)用,如下:
function greet() {
name = 'Hammad';
return function () {
console.log('Hi ' + name);
}
}
greet()(); // logs 'Hi Hammad'
公共作用域和私有作用域
在其他很多編程語(yǔ)言中,你可以設(shè)置變量、方法或者類是否可被訪問(wèn),例如使用 public
,private
,protected
等關(guān)鍵詞去聲明。可參考如下的PHP代碼:
// 公共作用域
public $property;
public function method() {
// ...
}
// 私有作用域
private $property;
private function method() {
// ...
}
// 被保護(hù)的作用域
protected $property;
protected function method() {
// ...
}
在公共作用域(全局作用域)中封裝函數(shù)可以讓它們免于被攻擊。而在JavaScript中,并沒(méi)有公共作用域和私有作用域的概念。但是我們可以用閉包模擬這一特性。為了保持不被全局的資源訪問(wèn)的狀態(tài),我們需要像下面這樣封裝函數(shù):
(function () {
// 私有作用域
})();
函數(shù)末尾的圓括號(hào)會(huì)告訴瀏覽器,即使在沒(méi)有被調(diào)用的情況下,讀取完成后就立即執(zhí)行函數(shù)。我們可以在其中添加函數(shù)和變量,且它們不會(huì)被外界訪問(wèn)到。但如果我們想要在外面訪問(wèn)它,也就是說(shuō)我們希望它們中的一部分是公共的一部分是私有,要怎么辦呢?有一種閉包是我們可以使用的,它叫做模塊模式。這種方式允許我們?cè)谝粋€(gè)對(duì)象里既可以用公共的方式定義變量,又可以用私有的方式定義變量。
模塊模式
模塊模式的寫法如下:
var Module = (function() {
function privateMethod() {
// 主代碼
}
return {
publicMethod: function() {
// 可以調(diào)用 privateMethod();
}
};
})();
這個(gè)模塊里返回的語(yǔ)句中就包含了公共函數(shù),而私有的函數(shù)并沒(méi)有被返回,這就保證了在該模塊的命名空間下的外部函數(shù)無(wú)法訪問(wèn)沒(méi)有被返回的函數(shù)。但是公共函數(shù)可以訪問(wèn)私有函數(shù),這些私有函數(shù)可以做一些其他的操作輔助公共函數(shù),比如ajax請(qǐng)求等。
Module.publicMethod(); // 可執(zhí)行
Module.privateMethod(); // Uncaught ReferenceError: privateMethod is not defined 引用錯(cuò)誤,privateMethod并未定義
私有函數(shù)的命名有一個(gè)慣例,它們通常以下劃線_
作為開頭,然后返回一個(gè)包含公共函數(shù)的匿名對(duì)象。這使得它們?cè)谝粋€(gè)很長(zhǎng)的對(duì)象中非常便于管理。比如下面這樣:
var Module = (function () {
function _privateMethod() {
// 代碼
}
function publicMethod() {
// 代碼
}
return {
publicMethod: publicMethod,
}
})();
立即執(zhí)行函數(shù)(IIFE)
閉包還有一種類型,就是立即執(zhí)行函數(shù)(IIFE)。這是一個(gè)在window這個(gè)全局上下文中自執(zhí)行的匿名函數(shù),也就是說(shuō)它的this
指向的是window。它會(huì)暴露出一個(gè)可供交互的單一接口。如下所示:
(function(window) {
// 代碼
})(this);
用.call(), .apply() and .bind()來(lái)更改上下文
call
函數(shù)和 apply
函數(shù)被用來(lái)在調(diào)用一個(gè)函數(shù)時(shí)更改該函數(shù)的上下文。這可以讓你更好的編程(可擁有一些終極權(quán)限來(lái)駕馭代碼)。你只需要在要調(diào)用的函數(shù)名后使用 call
和 apply
去調(diào)用,并為 call
和 apply
傳入你指定的上下文作為第一個(gè)參數(shù)即可。而這個(gè)函數(shù)自身所需要的參數(shù)放在以上下文作為第一參數(shù)的參數(shù)之后即可。
function hello() {
// 代碼
}
hello(); // 我們常用的調(diào)用方式
hello.call(context); //在這里你可以將context(this的指向)作為第一個(gè)參數(shù)傳值
hello.apply(context); // 在這里你可以將context(this的指向)作為第一個(gè)參數(shù)傳值
.call()
and .apply()
的區(qū)別在于,在 .call()
中,傳遞其余的參數(shù)時(shí),以逗號(hào) '
作為間隔的字符串即可;而在 .apply()
中,其余的參數(shù)以數(shù)組的形式傳遞。
function introduce(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)用函數(shù)的方式
introduce.call(window, 'Batman', 'to save Gotham'); // 以逗號(hào)分隔的字符串形式傳遞參數(shù)
introduce.apply('Hi', ['Bruce Wayne', 'businesses']); // 以數(shù)組的形式包裝參數(shù)傳參
// Output:
// 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的執(zhí)行要比Apply稍快一些。
下面的這個(gè)例子會(huì)將文檔中的項(xiàng)目列表逐個(gè)打印到控制臺(tái)。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Things to learn</title>
</head>
<body>
<h1>Things to Learn to Rule the World</h1>
<ul>
<li>Learn PHP</li>
<li>Learn Laravel</li>
<li>Learn JavaScript</li>
<li>Learn VueJS</li>
<li>Learn CLI</li>
<li>Learn Git</li>
<li>Learn Astral Projection</li>
</ul>
<script>
// 將列表中所有項(xiàng)目的NodeList保存在listItems中
var listItems = document.querySelectorAll('ul li');
// 遍歷listItems NodeList中的每個(gè)節(jié)點(diǎn),并記錄下它的內(nèi)容
for (var i = 0; i < listItems.length; 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
</script>
</body>
</html>
這個(gè)HTML中只包含了無(wú)序的項(xiàng)目列表。接著JavaScript會(huì)將他們從DOM中全部選出。列表會(huì)被循環(huán)遍歷知道所有的條目都被記錄。在循環(huán)中,我們將每個(gè)條目的內(nèi)容都打印在控制臺(tái)中。
log語(yǔ)句被包裝在一個(gè)函數(shù)中,這個(gè)函數(shù)又被包裝在被調(diào)用的函數(shù)的括號(hào)中。相符合的列表項(xiàng)會(huì)被傳遞到調(diào)用的函數(shù)中,所以會(huì)在控制臺(tái)中打印出正確的結(jié)果。
Objects can have methods, likewise functions being objects can also have methods. In fact, a JavaScript function comes with four built-in methods which are:
對(duì)象可以擁有方法,就像函數(shù)作為對(duì)象擁有方法一樣。事實(shí)上,一個(gè)JavaScript函數(shù)擁有四種內(nèi)置方法:
- Function.prototype.apply()
- Function.prototype.bind() (ECMAScript 5 (ES5)中被引入)
- Function.prototype.call()
- Function.prototype.toString()
Function.prototype.toString()以字符串形式返回該函數(shù)的源代碼。
現(xiàn)在,我可以來(lái)聊聊 .call()
, .apply()
以及 toString()
。不像Call和Apply,Bind自身不會(huì)調(diào)用函數(shù),他只能在函數(shù)被調(diào)用之前用來(lái)為其綁定新的上下文。上述例子用Bind來(lái)操作,可如下:
(function introduce(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ù)相同,它可以用以逗號(hào)為分隔的字符串的方式進(jìn)行傳參,而不是和Apply一樣用數(shù)組的方式傳參。
結(jié)語(yǔ)
這些都是JavaScript中很基本的概念,如果你想要對(duì)JavaScript有更深層次的認(rèn)識(shí)與運(yùn)用,一定要好好理解。很希望你可以通過(guò)這篇文章對(duì)JavaScript中的作用域有更好的理解。如果你有任何問(wèn)題,都可以在下面的評(píng)論。
愿你們都能擁有美好的編程體驗(yàn)。
[譯者注:最后上一張?jiān)淖髡邘浾誡
<h1 style="text-align:center">Hammad Ahmed </h1>