摘要:如果形參有設(shè)置默認(rèn)值,第二個(gè)就被建立,他針對(duì)的是函數(shù)體內(nèi)的聲明我們可以形象的理解為這是一個(gè)除了函數(shù)作用域和塊級(jí)作用域之外的第三作用域。
開(kāi)門(mén)見(jiàn)山,我們來(lái)看看下面這個(gè)有趣的例子
?對(duì)于上面這種用var的聲明方式,無(wú)論x的默認(rèn)值為什么,只要形參中出現(xiàn)了默認(rèn)值,zzz都會(huì)被當(dāng)作塊級(jí)作用域中的值。
?這是我偶然間遇到的一個(gè)問(wèn)題,起初我認(rèn)為這是chrome的bug,我將我的想法請(qǐng)教了一位朋友,他告訴我說(shuō)這不是bug,并讓我先看看這篇params default value & params environment & TDZ
?看完后我將我的想法進(jìn)一步告訴了他,我的想法可以用下面這5張圖來(lái)概括。
我認(rèn)為這是chrome的bug,如果說(shuō)是block,那么出了這個(gè)塊就不該被訪問(wèn)到,但是事實(shí)是能訪問(wèn)到
?而且從本身的語(yǔ)法來(lái)講,他也不應(yīng)該是block,而是function scope。
?他回答說(shuō)你沒(méi)看懂,并告知我沒(méi)看規(guī)范是很難理解,那么沒(méi)辦法了,讀讀規(guī)范吧,對(duì)規(guī)范已經(jīng)不陌生了,在我的前兩篇文章中,已經(jīng)引用了規(guī)范中的很多內(nèi)容。下面我們先來(lái)解釋下規(guī)范中對(duì)于這一問(wèn)題相關(guān)的解釋?zhuān)缓蟾鶕?jù)這些去解釋我們遇到的這一問(wèn)題。
?注:以下為ES6規(guī)范,ES6規(guī)范,ES6規(guī)范,重要的事情說(shuō)三遍,不是ES5噢~
8.1 詞法環(huán)境(LexicalEnvironment)?一個(gè)詞法環(huán)境是一種規(guī)范的類(lèi)型,用作定義基于JS代碼的嵌套詞法結(jié)構(gòu)中標(biāo)識(shí)符與變量或者函數(shù)間的關(guān)聯(lián)。一個(gè)詞法環(huán)境包括一個(gè)Environment Records(即作用域記錄,以下我們也簡(jiǎn)稱(chēng)ER)和一個(gè)可能為null的指向外部詞法環(huán)境的引用。
?通常一個(gè)詞法環(huán)境與JS代碼一些特殊的語(yǔ)法結(jié)構(gòu)想關(guān)聯(lián),如函數(shù)聲明,塊級(jí)語(yǔ)句,或者try語(yǔ)句中的catch從句。當(dāng)每次這些代碼被解析的時(shí)候,都會(huì)創(chuàng)建一個(gè)新的詞法環(huán)境。
?一個(gè)ER記錄了與它關(guān)聯(lián)的詞法環(huán)境的作用域中的標(biāo)識(shí)符綁定。所以稱(chēng)之為作詞法環(huán)境的ER。
?外部的詞法環(huán)境引用用作模擬邏輯上的詞法環(huán)境嵌套。一個(gè)詞法環(huán)境的外部引用也是一個(gè)引用,它指向圍繞或者說(shuō)包括當(dāng)前這個(gè)詞法環(huán)境的詞法環(huán)境。當(dāng)然,外部的詞法環(huán)境又有它自己的外部詞法環(huán)境,這就是我們常說(shuō)的作用域鏈。
?一個(gè)詞法環(huán)境可能作為多個(gè)內(nèi)部詞法環(huán)境共同的外部詞法環(huán)境。例如,一個(gè)函數(shù)聲明中有兩個(gè)內(nèi)嵌的函數(shù)聲明。一個(gè)語(yǔ)句塊中有兩個(gè)內(nèi)嵌的語(yǔ)句塊。
?一個(gè)全局環(huán)境是特殊的詞法環(huán)境,它沒(méi)有外部詞法環(huán)境,它的外部詞法環(huán)境引用為null。一個(gè)全局環(huán)境的ER也許會(huì)被用標(biāo)識(shí)符綁定進(jìn)行預(yù)填充,包含一些相關(guān)的全局對(duì)象,它的屬性提供一些全局環(huán)境下的標(biāo)識(shí)符綁定,即內(nèi)置對(duì)象,不同的JS宿主環(huán)境,內(nèi)置對(duì)象不同。
?這個(gè)全局對(duì)象就是全局環(huán)境下this的值。當(dāng)JS代碼運(yùn)行的時(shí)候,其他的屬性也許會(huì)被加入到全局對(duì)象中,最初的屬性可能會(huì)被修改。
?一個(gè)模塊環(huán)境是一個(gè)詞法環(huán)境,它包括對(duì)于一個(gè)模塊頂部聲明的綁定。它也包括對(duì)于通過(guò)模塊顯式導(dǎo)入(通過(guò)import)的模塊的綁定。一個(gè)模塊環(huán)境的外部環(huán)境為全局環(huán)境。
?調(diào)用一個(gè)函數(shù)的時(shí)候,一個(gè)函數(shù)環(huán)境也是一個(gè)詞法環(huán)境,與函數(shù)對(duì)象想對(duì)應(yīng)。一個(gè)函數(shù)環(huán)境也許會(huì)建立一個(gè)新的this綁定(比如構(gòu)造函數(shù),對(duì)象中的函數(shù)),注意這里的也許二字,因?yàn)閠his只有調(diào)用時(shí)才能確定。一個(gè)函數(shù)環(huán)境也會(huì)捕獲必要的狀態(tài)以支持調(diào)用父級(jí)方法。
?詞法環(huán)境和ER值是純粹的規(guī)范,它們不需要對(duì)應(yīng)于任何特定的ECMAScript實(shí)現(xiàn)。在ECMAScript程序中不可能直接訪問(wèn)或者操作它們。
8.1.1 Environment Records?在規(guī)范中,有兩種類(lèi)型的ER,聲明式ER(declarative Environment Records)和對(duì)象式ER(object Environment Records)。
?聲明式ER(declarative Environment Records)被用作定義ECMAScript(以下簡(jiǎn)稱(chēng)ES)語(yǔ)言中語(yǔ)法元素的作用,例如函數(shù)聲明,變量聲明,以及catch語(yǔ)句中把綁定的標(biāo)識(shí)符與ES語(yǔ)言中的值(Undefined, Null, Boolean, String, Symbol,Number, and Object中的一種,以下簡(jiǎn)稱(chēng)ES合法值)聯(lián)系在一起。
?對(duì)象式ER(object Environment Records)被用作定義例如with語(yǔ)句這類(lèi)把綁定的標(biāo)識(shí)符與某些對(duì)象聯(lián)系起來(lái)的ES元素。
?全局ER(Global Environment Records)和函數(shù)ER(function Environment Records)是專(zhuān)門(mén)用作全局腳本聲明和函數(shù)內(nèi)的頂部聲明(也就是我們常說(shuō)的聲明提升)。
?為了規(guī)范ER的值是Record規(guī)范類(lèi)型并且能夠存在于簡(jiǎn)單的面向?qū)ο髮哟谓Y(jié)構(gòu)中。可以認(rèn)為ER是一個(gè)抽象類(lèi),他有三個(gè)子類(lèi)-聲明式ER,對(duì)象式ER,全局ER。函數(shù)ER和模塊ER(module Environment Records)是聲明式ER的子類(lèi)。ER這個(gè)抽象類(lèi)包含許多抽象方法(見(jiàn)下表),這些抽象方法在不同的子類(lèi)中有不同的實(shí)現(xiàn)(既然是抽象方法,那么這是必然的)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?表1:ER中的抽象方法
Method | Purpose |
---|---|
HasBinding(N) | 判斷ER中是否綁定有N(即是否有標(biāo)識(shí)符N),有返回true,否則返回false |
CreateMutableBinding(N, D) | 在ER中創(chuàng)建一個(gè)新的未初始化的且可變的綁定(可以理解為聲明一個(gè)變量),N為標(biāo)識(shí)符名,D是可選參數(shù),如果為true,這個(gè)綁定隨后可能會(huì)被刪除。 |
CreateImmutableBinding(N, S) | 在ER中創(chuàng)建一個(gè)新的未初始化的且不可變的綁定,N為標(biāo)識(shí)符名。如果S為true,無(wú)論是否在嚴(yán)格模式下,在它初始化之前嘗試去訪問(wèn)它的值或者在他初始化后設(shè)置它的值都會(huì)拋出異常(就是我們用到的const)。S是可選參數(shù),默認(rèn)為false。 |
InitializeBinding(N,V) | 設(shè)置ER中已經(jīng)存在但是未初始化的綁定的值。N為標(biāo)識(shí)符名,V為ES合法值。 |
SetMutableBinding(N,V, S) | 設(shè)置ER中已經(jīng)存在但是未初始化的綁定的值。N為標(biāo)識(shí)符名,V為ES合法值。S為一個(gè)boolean類(lèi)型標(biāo)志,如果為true并且無(wú)法設(shè)置成你傳入的值,將拋出一個(gè)TypeError錯(cuò)誤。 |
GetBindingValue(N,S) | 返回一個(gè)ER中已經(jīng)存在的綁定。N為標(biāo)識(shí)符名。S被用作識(shí)別原始引用是否在嚴(yán)格模式中或者需要使用嚴(yán)格模式語(yǔ)義。如果S為true且綁定不存在,將拋出一個(gè)ReferenceError異常。如果綁定存在但是未初始化,無(wú)論S為何值,一個(gè)ReferenceError異常將被拋出。 |
DeleteBinding(N) | 從ER中刪除一個(gè)綁定。N為標(biāo)識(shí)符名,如果N存在,刪除并返回true。如果N存在但是不能被刪除返回false。如果N不存在,返回true。 |
HasThisBinding() | 判斷ER是否綁定了this。(就是我們常用的call和apply)。如果是返回true,否則返回false。 |
HasSuperBinding() | 判斷是否有父類(lèi)方法綁定。如果是返回true,否則返回false。 |
WithBaseObject () | 如果ER與with語(yǔ)句有關(guān)聯(lián),返回with的對(duì)象。否則,返回undefined |
?每個(gè)聲明式ER都與一個(gè)作用域想關(guān)聯(lián),這個(gè)作用域包含var,const,let,class,module,import或者function聲明。一個(gè)聲明式ER綁定它的作用域中定義的標(biāo)識(shí)符的集合。
有了上面的基本解釋?zhuān)覀兿旅鎭?lái)看與提問(wèn)有關(guān)的地方:
9.2.12 函數(shù)聲明實(shí)例化(FunctionDeclarationInstantiation(func, argumentsList))?請(qǐng)記住這里的func和argumentsList,在后面描述過(guò)程的時(shí)候我們會(huì)多次提到
?當(dāng)為了解析一個(gè)JS函數(shù)建議執(zhí)行上下文的時(shí)候,一個(gè)新的函數(shù)ER就被建立,并且綁定這個(gè)ER中每個(gè)實(shí)例化了的形參(這里的實(shí)例化應(yīng)該是指在執(zhí)行函數(shù)的時(shí)候,形參才能有值,有值之后就代表實(shí)例化了)。同時(shí)在函數(shù)體中的每個(gè)聲明也被實(shí)例化了。
?如果函數(shù)的形參不包含任何默認(rèn)值,那么函數(shù)體內(nèi)的聲明將與形參在同一ER中實(shí)例化。
?如果形參有設(shè)置默認(rèn)值,第二個(gè)ER就被建立,他針對(duì)的是函數(shù)體內(nèi)的聲明(我們可以形象的理解為這是一個(gè)除了函數(shù)作用域和塊級(jí)作用域之外的"第三作用域")。形參和本身的函數(shù)聲明是函數(shù)聲明實(shí)例化的一部分。所有其他的聲明在解析函數(shù)體的才會(huì)被實(shí)例化。
?其實(shí)到這里,我們就已經(jīng)能解釋我們提出的問(wèn)題了,用var聲明的變量在chrome中顯示為Block,并不是代表他為塊級(jí)作用域中的值,而僅僅是為了區(qū)分形參的ER和函數(shù)體的ER,形參的ER中的變量只能讀取形參ER中的變量或者函數(shù)外的變量,而函數(shù)體內(nèi)的變量可以讀取函數(shù)體內(nèi),形參,外部的變量。這里摘抄下上面提到的文章中的代碼片段:
let y =1; function foo(x = function(){console.log(y)},y=2) { x(); // 2 var y = 3; // if use let, then throw error: y is already declared, which is much more clear. console.log(y); //3 x(); // 2 } foo(); console.log(y); //1
這便是我們chrome為什么要區(qū)分形參的ER和函數(shù)體的ER的原因,是為了讓我們看得更加清晰。
?問(wèn)題雖然解決了,但是規(guī)范卻還意猶未盡,有興趣的同學(xué)可以接著往下將這規(guī)則中這一節(jié)的內(nèi)容看完。
?函數(shù)聲明實(shí)例化按照如下過(guò)程進(jìn)行。其中func為函數(shù)對(duì)象,argumentsList為參數(shù)列表。
?1. Let calleeContext 作為運(yùn)行時(shí)上下文/運(yùn)行時(shí)環(huán)境(Execution Contexts,見(jiàn)下)
Execution Contexts(原文為8.3節(jié)內(nèi)容,但是這里提到了,所以我們?cè)谶@里就一并解釋了):
一個(gè)運(yùn)行時(shí)上下文或者說(shuō)運(yùn)行時(shí)環(huán)境是用來(lái)跟蹤一個(gè)ECMAScript實(shí)現(xiàn)(注意ES實(shí)現(xiàn)不止JS一種)的代碼的運(yùn)行時(shí)解析。在運(yùn)行時(shí)的任意時(shí)間點(diǎn),最多只存在一個(gè)運(yùn)行時(shí)上下文,即當(dāng)前執(zhí)行的代碼。
一個(gè)棧被用作跟蹤運(yùn)行時(shí)環(huán)境,運(yùn)行時(shí)環(huán)境總是指向棧頂?shù)脑兀ㄒ簿褪俏覀兂Uf(shuō)的調(diào)用棧,chrome調(diào)試時(shí)的call stack)。無(wú)論何時(shí),只要運(yùn)行時(shí)環(huán)境從當(dāng)前運(yùn)行的代碼轉(zhuǎn)移到非當(dāng)前運(yùn)行時(shí)環(huán)境的代碼,就會(huì)創(chuàng)建一個(gè)新的運(yùn)行時(shí)環(huán)境,并將這個(gè)新的運(yùn)行時(shí)環(huán)境push到棧頂,成為當(dāng)前的運(yùn)行時(shí)環(huán)境。
為了跟蹤代碼的執(zhí)行過(guò)程,一個(gè)運(yùn)行時(shí)環(huán)境包含實(shí)現(xiàn)具體的狀態(tài)是有必要的。每一個(gè)運(yùn)行時(shí)環(huán)境都至少有下表列出的這幾種元素。
Component | Purpose |
---|---|
code evaluation state | 包含與運(yùn)行時(shí)環(huán)境相關(guān)的代碼所需的任何狀態(tài),如執(zhí)行中,暫停,繼續(xù)解析 |
Function | 如果運(yùn)行時(shí)環(huán)境正在解析一個(gè)函數(shù)對(duì)象,那么這個(gè)值就為那個(gè)函數(shù)對(duì)象。如果正在解析一個(gè)腳本(script)或者模塊(module),那么這個(gè)值為null |
Realm(域) | 來(lái)自相關(guān)代碼可以訪問(wèn)的ECMAScript resources的域。注:ECMAScript resources包含客戶(hù)端ECMAScript,ECMAScript核心標(biāo)準(zhǔn)庫(kù),擴(kuò)展自ECMAScript核心標(biāo)準(zhǔn)庫(kù)的服務(wù)端ECMAScript。域包括全局對(duì)象和內(nèi)置對(duì)象 |
運(yùn)行時(shí)環(huán)境的代碼解析可能會(huì)被各種各樣的情況打斷而導(dǎo)致暫停或者說(shuō)掛起。一旦運(yùn)行時(shí)環(huán)境切換到另一個(gè)不同的運(yùn)行時(shí)環(huán)境,那么這個(gè)不同的環(huán)境就可能成為當(dāng)前運(yùn)行時(shí)環(huán)境,并開(kāi)始解析代碼。一段時(shí)間過(guò)后,一個(gè)暫停的執(zhí)行環(huán)境也許會(huì)成為運(yùn)行時(shí)環(huán)境并且從之前的暫停點(diǎn)繼續(xù)解析代碼。運(yùn)行時(shí)環(huán)境的這種來(lái)回切換的狀態(tài)是通過(guò)類(lèi)棧結(jié)構(gòu)來(lái)過(guò)渡的。然而,一些ES特性需要非棧的過(guò)渡。
運(yùn)行時(shí)環(huán)境的Realm的值也被稱(chēng)作當(dāng)前域。運(yùn)行時(shí)環(huán)境的Function的值也被成為活動(dòng)函數(shù)對(duì)象。
ECMAScript的運(yùn)行時(shí)環(huán)境有額外的state元素(見(jiàn)下表)
Component | Purpose |
---|---|
LexicalEnvironment | 標(biāo)記用作解析當(dāng)前運(yùn)行時(shí)環(huán)境中代碼里的標(biāo)識(shí)符引用的詞法環(huán)境 |
VariableEnvironment | 標(biāo)記在當(dāng)前運(yùn)行時(shí)環(huán)境中詞法環(huán)境的ER包括var聲明創(chuàng)建的綁定的詞法環(huán)境 |
上表中的詞法環(huán)境(LexicalEnvironment)和變量環(huán)境(VariableEnvironment),在一個(gè)運(yùn)行時(shí)環(huán)境中總是表現(xiàn)為詞法環(huán)境。當(dāng)一個(gè)運(yùn)行時(shí)環(huán)境被創(chuàng)建的時(shí)候,它的詞法環(huán)境和變量環(huán)境初始化為相同的值。
可以參考下stackoverflow上的解釋1以及stackoverflow上的解釋2:
// VariableEnvironment (global) = { __outer__: null } // LexicalEnvironment = VariableEnvironment (global) (function foo() { // VariableEnvironment (A) = { x: undefined, __outer__: global } // LexicalEnvironment = VariableEnvironment (A) var x; (function bar(){ // VariableEnvironment (B) = { y: undefined, __outer__: A } // LexicalEnvironment = VariableEnvironment (B) var y; x = 2; // VariableEnvironment (A) = { x: 2, __outer__: global } // LexicalEnvironment is still the same as VariableEnvironment (B) })(); })();
對(duì)于構(gòu)造器的運(yùn)行時(shí)上下文,有額外的的state元素(見(jiàn)下表)
Component | Purpose |
---|---|
Generator | 當(dāng)前運(yùn)行時(shí)環(huán)境正在解析的構(gòu)造器對(duì)象 |
在大多數(shù)情況下,只有當(dāng)前運(yùn)行時(shí)環(huán)境(即運(yùn)行時(shí)環(huán)境棧的棧頂元素)直接被規(guī)范中的算法操作。
?2. Let env 作為calleeContext(當(dāng)前的運(yùn)行時(shí)上下文,也就是運(yùn)行時(shí)上下文的棧頂元素)的詞法環(huán)境(LexicalEnvironment)
?3. Let envRec 作為env的ER
?4. Let code 等于[[ECMAScriptCode]]這個(gè)func的內(nèi)嵌屬性的值(內(nèi)嵌屬性(兩個(gè)中括號(hào)包裹的屬性)并不是ES的一部分,由ES的具體實(shí)現(xiàn)來(lái)定義,它們純粹是為了展示,更重要的一點(diǎn),它們具有多態(tài)性。下面再看到中括號(hào)就不再解釋內(nèi)嵌屬性了)
[[ECMAScriptCode]]:類(lèi)型為Node。值為源代碼文件解析后的函數(shù)體,即函數(shù)對(duì)象有一個(gè)屬性[[ECMAScriptCode]]可以指向自身的函數(shù)體。
?5. Let strict 等于[[Strict]]的值
[[Strict]]: 類(lèi)型為boolean。如果為true代表這是一個(gè)嚴(yán)格模式下的函數(shù)
?6. Let formals 等于[[FormalParameters]]的值
[[FormalParameters]]:類(lèi)型為Node。指向函數(shù)的形參列表。
?7. Let parameterNames 等于formals的BoundNames,即如果形參為x, y那么parameterNames為["x", "y"]
?8. 如果parameterNames里有重復(fù)的,將hasDuplicates置為true,否則置為false
?9. Let simpleParameterList等于formals的IsSimpleParameterList
IsSimpleParameterList:如果形參為空或者只是普通的標(biāo)識(shí)符則返回true,其他的如形參為rest參數(shù)(...x),普通參數(shù)加rest參數(shù)(x, ...y),參數(shù)有默認(rèn)值,參數(shù)有解構(gòu)賦值等等,都返回false
?10. Let hasParameterExpressions等于formals的ContainsExpression的值
ContainsExpression:形參含有默認(rèn)值則為true,否則為false
?11. Let varNames等于函數(shù)的VarDeclaredNames(只包含函數(shù)體里的變量,不包含形參)的值
?12. Let varDeclarations等于函數(shù)的VarScopedDeclarations的值
VarDeclaredNames與VarScopedDeclarations的區(qū)別:VarDeclaredNames是一個(gè)類(lèi)型為Name的Set(Name只包含標(biāo)識(shí)符名,作用域等等)。而VarScopedDeclarations是一個(gè)類(lèi)型為StatementListItem的List(StatementListItem代表的是語(yǔ)句元素,ES一共有14種語(yǔ)句),就這里的語(yǔ)句而言,指的是VariableStatement,對(duì)于我們解析而已,是把語(yǔ)句(也就是Statement)當(dāng)作一個(gè)語(yǔ)法樹(shù)節(jié)點(diǎn)
?13. Let lexicalNames等于函數(shù)的LexicallyDeclaredNames(不包含var和function聲明)
?14. Let functionNames等于一個(gè)空的List
?15. Let functionsToInitialize等于一個(gè)空的List
?16. 對(duì)于變量varDeclarations其中的每個(gè)元素d,如果d既不是VariableDeclaration也不是ForBinding(for in或者for of結(jié)構(gòu)里面進(jìn)行聲明)。那么:
進(jìn)行Assert(斷言),判斷d是否是函數(shù)聲明或者構(gòu)造器聲明
Let fn等于d的BoundNames
如果fn不是functionNames里的元素,那么
將fn用頭插法插入functionNames
注意如果fn有多次重復(fù)出現(xiàn),則以最后一次為準(zhǔn)
將d用頭插法插入functionsToInitialize
?17. 聲明一個(gè)argumentsObjectNeeded,賦值為true
?18. 如果func的內(nèi)嵌屬性[[ThisMode]]的值為lexical,那么
將argumentsObjectNeeded賦值為false(注意箭頭函數(shù)沒(méi)有arguments對(duì)象)
[[ThisMode]]:作用是定義在函數(shù)形參和函數(shù)體內(nèi)如何解析this引用。值為lexical代表this指向詞法閉包的this值(詞法閉包就是我們常說(shuō)的閉包,具體可以看我的上一篇文章),strict代表this值完全由函數(shù)調(diào)用提供。global代表this值為undefined
?19. 否則(接上)如果arguments是parameterNames(在第7步聲明)的一個(gè)元素(也就是形參里面我們使用了arguments作為標(biāo)識(shí)符), 那么將argumentsObjectNeeded賦值為false
?20. 否則(接上)如果hasParameterExpressions(在第10步聲明)等于false,那么
如果arguments是functionNames(在第14步聲明)的一個(gè)元素,或者是lexicalNames(在第13步聲明)的一個(gè)元素,那么將argumentsObjectNeeded賦值為false
?21. 對(duì)于parameterNames(在第7步聲明)中每個(gè)元素paramName:
a.Let alreadyDeclared等于envRec.HasBinding(paramName)的值(即判斷當(dāng)前環(huán)境中是否綁定過(guò)paramName)
b.注意:早期的錯(cuò)誤檢查確保了多個(gè)重復(fù)的形參參數(shù)數(shù)名只可能出現(xiàn)在形參沒(méi)有默認(rèn)值和rest參數(shù)的非嚴(yán)格模式下的函數(shù)中:
1. function func(x, x = 2) {} // 報(bào)錯(cuò) 2. function func(x, ...x) {} // 報(bào)錯(cuò) 3. function func(x, x) {} // 不報(bào)錯(cuò) 4. "use strict"; function func(x, x) {} // 報(bào)錯(cuò)
c.如果alreadyDeclared等于false,那么:
c.1 Let status等于envRec.CreateMutableBinding(paramName)(表1中有這個(gè)方法)的值(即將聲明的參數(shù)綁定到函數(shù)的作用域中)
c.2 如果hasDuplicates(在第8步聲明)等于true,那么:
Let status等于envRec.InitializeBinding(paramName, undefined)(表1中有這個(gè)方法)的值
c.3 斷言:在上面兩步操作中(c.1和c.2),status不可能是一個(gè) abrupt completion(可以簡(jiǎn)單的理解為break,continue,return和throw操作)
?22. 如果argumentsObjectNeeded(第17-20步改變)等于true,那么:
a.如果strict(第5步聲明)等于true或者simpleParameterList(第9步聲明)等于false,那么:
a.1 Let ao等于CreateUnmappedArgumentsObject(argumentsList)的值
b.否則(接上面的a步驟):
b.1 注意:mapped argument(與上面的Unmapped對(duì)應(yīng))對(duì)象僅在非嚴(yán)格模式下且形參沒(méi)有rest參數(shù),默認(rèn)值,解構(gòu)賦值的函數(shù)中提供。(滿(mǎn)足這三個(gè)條件其實(shí)simpleParameterList就為true了)
b.2 Let ao等于CreateMappedArgumentsObject(func, formals, argumentsList, env)的值
注:CreateUnmappedArgumentsObject和CreateMappedArgumentsObject簡(jiǎn)單來(lái)說(shuō)就是根據(jù)參數(shù)形式的不同創(chuàng)建不同的arguments`對(duì)象
c.ReturnIfAbrupt(ao)
d.如果strict等于true,那么:
d.1 Let status等于envRec.CreateImmutableBinding("arguments")(表1中有介紹)的值
e.否則(接上面的c步驟),Let status等于envRec.CreateMutableBinding("arguments")(表1中有介紹)的值
f.斷言:status不可能是一個(gè) abrupt completion
g.執(zhí)行envRec.InitializeBinding("arguments", ao)(表1中有介紹)
h.向parameterNames(第7步中聲明)中appendarguments
?23. Let iteratorRecord等于Record {[[iterator]]: CreateListIterator(argumentsList), [[done]]: false}(即建立一個(gè)內(nèi)置迭代器屬性,讓arguments變成可迭代的)
?24. 如果hasDuplicates(第8步中聲明)等于true,那么:
a.Let formalStatus等于formals去調(diào)用IteratorBindingInitialization,用iteratorRecord和undefined作為參數(shù)的返回值
?25. 否則(接上面的24步驟):
a.Let formalStatus等于formals去調(diào)用IteratorBindingInitialization,用iteratorRecord和env作為參數(shù)的返回值(可以看到只有最后一個(gè)參數(shù)和24步不一樣)
IteratorBindingInitialization(iteratorRecord,environment):當(dāng)environment為undefined的時(shí)候,這意味著應(yīng)該用一個(gè)PutValue(即將一個(gè)值放入一個(gè)對(duì)象)操作去初始化值。這是針對(duì)非嚴(yán)格模式情況下的一個(gè)考慮(因?yàn)閲?yán)格模式下在24步應(yīng)該是false)。在這種情況下,形參被預(yù)初始化,目的是解決多個(gè)參數(shù)名相同的問(wèn)題。
?26. ReturnIfAbrupt(formalStatus)
?27. 如果hasParameterExpressions(第10步聲明)等于false,那么:
a.注意:對(duì)于形參和聲明提取的變量,僅僅只需要一個(gè)單一的詞法環(huán)境
b.Let instantiatedVarNames等于parameterNames的一個(gè)副本
c.對(duì)于varNames(第11步中聲明)的每個(gè)元素n:
c.1 如果n不是instantiatedVarNames里的元素,那么:
c.1.1 appendn到instantiatedVarNames中
c.1.2 Let status等于envRec.CreateMutableBinding(n)
c.1.3 斷言:status不可能是一個(gè) abrupt completion
c.1.4 執(zhí)行envRec.InitializeBinding(n, undefined)
d.Let varEnv等于env
e.Let varEnvRec等于envRec
?28. 否則(接上面的27步驟):
a.注意:一個(gè)多帶帶的ER是有必要的,目的是確保形參中的表達(dá)式創(chuàng)建的閉包對(duì)函數(shù)體的變量不具有可訪問(wèn)性(即我們提到的"第三作用域")
b.Let varEnv等于NewDeclarativeEnvironment(env)的值(即創(chuàng)建一個(gè)新的詞法環(huán)境,它的ER里沒(méi)有任何綁定,這個(gè)ER的外部或者說(shuō)父級(jí)詞法環(huán)境在這里就是env)
c.Let varEnvRec等于varEnv的ER
d.將calleeContext(第1步中聲明)的VariableEnvironment設(shè)為varEnv
e.Let instantiatedVarNames等于一個(gè)空的List
f.對(duì)于varNames中的每個(gè)元素n:
f.a 如果n不是instantiatedVarNames中的元素,那么:
f.a.1 appendn到instantiatedVarNames中
f.a.2 Let status等于varEnvRec.CreateMutableBinding(n)(varEnvRec在27.e步或者28.c步中聲明,CreateMutableBinding參考表1)的值
f.a.3 斷言:status不可能是一個(gè) abrupt completion
f.a.4 如果n不是parameterNames(第7步中聲明)中的元素,或者n是functionNames(第14步中聲明)中的元素,Let initialValue等于undefined
f.a.5 否則(接上面的f.a.4步驟):
f.a.5.1 Let initialValue等于envRec.GetBindingValue(n, false)(envRec在第3步中聲明,GetBindingValue參考表1)
f.a.5.2 ReturnIfAbrupt(initialValue)
f.a.6 執(zhí)行varEnvRec.InitializeBinding(n, initialValue)(varEnvRec在27.e步或者28.c步中聲明,InitializeBinding參考表1)
f.a.7 注意:形參中相同標(biāo)識(shí)符的變量,當(dāng)它們對(duì)應(yīng)的形參初始化的時(shí)候,它們的值是一樣的。(意思就是比如function func(x, x) {},調(diào)用時(shí)func(111),那么當(dāng)?shù)诙€(gè)x初始化的時(shí)候,第一個(gè)x也就變成undefined了,因?yàn)樗鼈兊闹狄3忠恢?,所以最后x為undefined)
?29. 注意:附錄B.3.3在這一點(diǎn)有額外的步驟(有興趣可以去看看,主要是介紹了瀏覽器宿主環(huán)境對(duì)于塊級(jí)函數(shù)聲明的解析和規(guī)范的差異)
?30. 如果strict等于false,那么:
a.Let lexEnv等于NewDeclarativeEnvironment(varEnv)的值(即創(chuàng)建一個(gè)新的詞法環(huán)境,它的ER里沒(méi)有任何綁定,這個(gè)ER的外部或者說(shuō)父級(jí)詞法環(huán)境在這里就是varEnv)
b.注意:非嚴(yán)格模式下的函數(shù)對(duì)于頂層聲明采用的是一個(gè)多帶帶的詞法作用域,因此直接調(diào)用eval(var a = eval; a(xx)這叫間接調(diào)用)能夠?qū)δ切┮呀?jīng)聲明過(guò)的會(huì)導(dǎo)致沖突。在嚴(yán)格模式下這是不需要的,因?yàn)閲?yán)格模式下的eval總是把聲明放到一個(gè)新的ER中
function qq(){var a = 1; eval("var a = 55;"); console.log(a);} // 輸出55 "use strict"; function qq(){var a = 1; eval("var a = 55;"); console.log(a);} // 輸出1
?31.否則(接上面的30步驟),Let lexEnv 等于varEnv(在27.d或者28.b中聲明)
?32. Let lexEnvRec等于lexEnv的ER
?33. 將calleeContext(第1步中聲明)的ER設(shè)置為lexEnv
?34. Let lexDeclarations等于函數(shù)的LexicallyScopedDeclarations
?35. 對(duì)于lexDeclarations中的每個(gè)元素d:
a.注意:一個(gè)詞法聲明的標(biāo)識(shí)符不能和函數(shù),產(chǎn)生器函數(shù),形參或者其他變量名相同。詞法聲明的標(biāo)識(shí)符只會(huì)在這里實(shí)例化而不是初始化。
b.對(duì)于BoundNames中的每個(gè)元素dn:
b.1 如果d是常量聲明,那么:
b.1.1 Let status等于lexEnvRec.CreateImmutableBinding(dn, true)
b.1.2 Let status等于lexEnvRec.CreateMutableBinding(dn, false)
c.斷言:status不可能是一個(gè) abrupt completion
?36. 對(duì)于functionsToInitialize中的每個(gè)解析過(guò)的語(yǔ)法短語(yǔ)(這里的短語(yǔ)指的是編譯原理里的短語(yǔ))f:
a.Let fn作為f的BoundNames的唯一元素
b.Let fo等于執(zhí)行InstantiateFunctionObject(f, lexEnv)的結(jié)果
InstantiateFunctionObject(f, lexEnv):
c.Let status等于varEnvRec.SetMutableBinding(fn, fo, false)
d.斷言:status不可能是一個(gè) abrupt completion
?37. 返回NormalCompletion(empty)(即返回 Completion{[[type]]: normal, [[value]]: empty, [[target]]:empty})
注意:附錄B.3.3關(guān)于上面的算法提供了一種擴(kuò)展,這種擴(kuò)展對(duì)于瀏覽器在ES2015之前實(shí)現(xiàn)ECMAScript向后兼容是有必要的。(也就是我們常說(shuō)的ployfill)
注意:形參的Initializers(即默認(rèn)值)也許包含eval表達(dá)式。任何在這個(gè)eval里面聲明的變量只能在這個(gè)eval內(nèi)才能訪問(wèn)。
寫(xiě)在結(jié)尾?在探索和翻譯的過(guò)程中,確實(shí)是遇到了一些困難,包括到現(xiàn)在也還有一些困惑仍未解決。經(jīng)過(guò)這次探索,想到一位大牛曾回答過(guò)"作為程序員,哪些網(wǎng)站是必須了解的"的問(wèn)題,他的回答是"除了github和stackoverflow,應(yīng)該沒(méi)有其他是必須的",算是比較深刻的體會(huì)到了一這點(diǎn),很多東西google和wiki都是找不到的,只能求助于so,沒(méi)有的話(huà)還需要自己提問(wèn)和gh上提issue。
?一條評(píng)論可能又會(huì)提到其他地方,其他地方又會(huì)鏈接到不同的人,不同的技術(shù),不同的想法。這樣都去瀏覽或者了解一番,便能開(kāi)闊眼界,從一個(gè)單一知識(shí)點(diǎn)入手,不單單是解決這一個(gè)問(wèn)題?;蛟S我們還能學(xué)到很多新的知識(shí),方式,想法,了解一些新的工具,認(rèn)識(shí)一些有趣的人。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/80958.html
摘要:中函數(shù)是一等公民。小明小明調(diào)用函數(shù)時(shí),傳遞給函數(shù)的值被稱(chēng)為函數(shù)的實(shí)參值傳遞,對(duì)應(yīng)位置的函數(shù)參數(shù)名叫作形參。所以不推薦使用構(gòu)造函數(shù)創(chuàng)建函數(shù)因?yàn)樗枰暮瘮?shù)體作為字符串可能會(huì)阻止一些引擎優(yōu)化也會(huì)引起瀏覽器資源回收等問(wèn)題。 函數(shù) 之前幾節(jié)中圍繞著函數(shù)梳理了 this、原型鏈、作用域鏈、閉包等內(nèi)容,這一節(jié)梳理一下函數(shù)本身的一些特點(diǎn)。 javascript 中函數(shù)是一等公民。 并且函數(shù)也是對(duì)象,...
摘要:函數(shù)的形參和函數(shù)體就是兩個(gè)不同的作用域。這里只聲明了還是形參這里改變了形參的值,所以返回是這題還有一個(gè)坑點(diǎn),我拿到里面去轉(zhuǎn)一下得到的結(jié)果是這里結(jié)果是這種神奇的代碼還是盡量不要寫(xiě)呀如果有理解錯(cuò)誤的地方,歡迎指正 把話(huà)說(shuō)完:90%面試官都不會(huì)問(wèn)的題,因?yàn)槊嬖嚬僖膊灰欢ǘ?直接來(lái)看一看今天要說(shuō)的題目: // 問(wèn)題:foo.x的值是什么?bar.x? var foo = { n: 1 }; ...
摘要:在中,每一個(gè)節(jié)點(diǎn)被稱(chēng)為回流重繪回流中部分全部元素的規(guī)模尺寸布局等改變而需要重新構(gòu)建頁(yè)面。而就是通過(guò)調(diào)用構(gòu)造函數(shù)創(chuàng)建的對(duì)象實(shí)例的原型對(duì)象原型所指的就是一個(gè)對(duì)象,實(shí)例繼承對(duì)象的屬性。 亂序 不間斷更新 絕大多數(shù)寫(xiě)的比較淺顯 看個(gè)樂(lè)子 display:none 和visibility:hidden的區(qū)別 display:none徹底消失將會(huì)隱藏它以及所有的后代元素占據(jù)的空間消失,瀏覽器不會(huì)...
摘要:在中,每一個(gè)節(jié)點(diǎn)被稱(chēng)為回流重繪回流中部分全部元素的規(guī)模尺寸布局等改變而需要重新構(gòu)建頁(yè)面。而就是通過(guò)調(diào)用構(gòu)造函數(shù)創(chuàng)建的對(duì)象實(shí)例的原型對(duì)象原型所指的就是一個(gè)對(duì)象,實(shí)例繼承對(duì)象的屬性。 亂序 不間斷更新 絕大多數(shù)寫(xiě)的比較淺顯 看個(gè)樂(lè)子 display:none 和visibility:hidden的區(qū)別 display:none徹底消失將會(huì)隱藏它以及所有的后代元素占據(jù)的空間消失,瀏覽器不會(huì)...
閱讀 965·2019-08-30 14:05
閱讀 1849·2019-08-30 11:08
閱讀 3355·2019-08-29 15:41
閱讀 3762·2019-08-23 18:31
閱讀 1665·2019-08-23 18:29
閱讀 681·2019-08-23 14:51
閱讀 2235·2019-08-23 13:53
閱讀 2278·2019-08-23 13:02