摘要:等價(jià)與注意如果構(gòu)造函數(shù)有自己的返回,那么情況有所不同。,定義了的屬性,默認(rèn)是聲明的函數(shù)名,匿名函數(shù)是。匿名函數(shù)表達(dá)式和函數(shù)聲明都不會(huì)創(chuàng)建匿名作用域。
ECMAScript規(guī)范中對(duì)Function的文檔描述,我認(rèn)為是ECMAScript規(guī)范中最復(fù)雜也是最不好理解的一部分,它涉及到了各方面。光對(duì)Function就分了Function Definitions、Arrow Function Definitions、Method Definitions、Generator Function Definitions、Class Definitions、Async Function Definitions、Async Arrow Function Definitions這幾塊。我準(zhǔn)備花三章來(lái)介紹Function。這篇文章主要是理解ArrowFunction和GeneratorFunction,當(dāng)然還包括最基本最普通的Function Definitions。
Function在了解Function Definitions之前我們需要知道函數(shù)對(duì)象(Function Object)。我們都知道Function本質(zhì)上也是一個(gè)對(duì)象,所以普通對(duì)象有的方法Function對(duì)象都有,此外Function對(duì)象還有自己的內(nèi)部方法。所有Function對(duì)象都有一個(gè)[[Call]]的內(nèi)部方法,有了這個(gè)方法Function對(duì)象才能被用作函數(shù)調(diào)用,即你***()時(shí)內(nèi)部調(diào)用就是[[Call]]方法,當(dāng)然不是所有有[[Call]]方法的Function對(duì)象都可以進(jìn)行***()調(diào)用,常見(jiàn)的Map、Set等方法雖然有[[Call]]方法,但是你不能進(jìn)行Map()和Set(),這時(shí)候就用到了Function對(duì)象的另一個(gè)內(nèi)部方法[[Construct]],當(dāng)Function作為構(gòu)造函數(shù)調(diào)用時(shí),就會(huì)使用[[Construct]]方法。
[[Call]]注意:不是所有Function對(duì)象都有[[Construct]]方法。只有當(dāng)Function作為構(gòu)造函數(shù)調(diào)用時(shí),才會(huì)有[[Construct]]方法,比如ArrowFunction和GeneratorFunction只有[[Call]]方法沒(méi)有[[Construct]]方法。
先說(shuō)[[Call]]方法,看到這個(gè)名字很容易讓人想起Function.prototype中的call方法,沒(méi)錯(cuò)Function.prototype.call以及Function.prototype.apply都是顯示的調(diào)用了[[Call]]方法,之所以說(shuō)顯示調(diào)用是相比于***()調(diào)用,call和apply要簡(jiǎn)單直接的多。[[Call]]方法接受兩個(gè)參數(shù),一個(gè)是thisArgument,另一個(gè)是argumentsList,thisArgument即表示了function中的this對(duì)象,argumentsList代表了function中的參數(shù)列表,看!和Function.prototype.apply的調(diào)用方式是如此的相似。
function foo(){} foo(1,2) //當(dāng)執(zhí)行foo()方法時(shí),實(shí)際上內(nèi)部是如下調(diào)用 foo.[[Call]](undefined,? 1, 2 ?) //? ?表示ECMAScript的List規(guī)范類型 //注意,如果你是隱式調(diào)用function,那么thisArgument是undefined,不是常說(shuō)的全局對(duì)象window, //只是在[[Call]]內(nèi)部執(zhí)行時(shí)檢測(cè)到如果thisArgument是undefined或null, //且在非嚴(yán)格模式下才會(huì)變成全局對(duì)象,即foo(1,2)你可以認(rèn)為等價(jià)與下面的: foo.call(null,1,2) foo.apply(undefined,[1,2]) //------------------- var a={ foo:function(){} } a.foo(1,2) //等價(jià)與==> foo.[[Call]](a,? 1, 2 ?) //等價(jià)與==> foo.call(a,1,2) //等價(jià)與==> foo.apply(a,[1,2])
[[Construct]]這里有個(gè)建議,以后你遇到this指向問(wèn)題的時(shí)候,你把function轉(zhuǎn)成call或者apply模式,你就能清楚的明白this指向什么。
[[Construct]]內(nèi)部方法主要有new關(guān)鍵字調(diào)用Function時(shí)才會(huì)執(zhí)行[[Construct]]方法。[[Construct]]方法主要接受兩個(gè)參數(shù)一個(gè)是argumentsList, 還有一個(gè)是newTarget。newTarget正常調(diào)用下指向調(diào)用的function對(duì)象。比如foo(),newTarget就是foo,你可以在函數(shù)內(nèi)部用new.target訪問(wèn)到。構(gòu)造函數(shù)中的this對(duì)象與newTarget有關(guān),如果newTarget.prototype存在,且是Object對(duì)象,則this就是ObjectCreate(newTarget.prototype),ObjectCreate是Object.create內(nèi)部調(diào)用的方法,如果newTarget.prototype不存在或者不是Object對(duì)象,this相當(dāng)于ObjectCreate(Object.prototype)。
function Foo(){} var fooInstance = new Foo(1,2) //等價(jià)與==> var fooInstance = Foo.[[Construct]](? 1, 2 ?,Foo); fooInstance instanceof Foo //true Object.create(Foo.prototype) instanceof Foo //true //注意如果構(gòu)造函數(shù)有自己的return返回,那么情況有所不同。 //返回的是Object,則構(gòu)造函數(shù)的實(shí)例就是返回的對(duì)象 //返回的不是Object,相當(dāng)于默認(rèn)沒(méi)有返回 function Foo(){ return {a:1}} var fooInstance = new Foo(1,2) fooInstance instanceof Foo //false,注意不是true,fooInstance不是Foo的實(shí)例 Object.create(Foo.prototype) instanceof Foo //true //只要Foo.prototype存在且是對(duì)象,那么Object.create(Foo.prototype)永遠(yuǎn)是Foo的一個(gè)實(shí)例Function Definitions
Function Definitions包含了FunctionDeclaration和FunctionExpression,有一些早期錯(cuò)誤檢測(cè)添加到Function Definitions中,其中在function中的let、const和var聲明的變量規(guī)則參考上一篇文章var、let、const聲明的區(qū)別,另外有一些附加的早期錯(cuò)誤:
function中的參數(shù)被認(rèn)為是var聲明,因此:
function foo(a,b){ let a = 1; //SyntaxError,重復(fù)聲明a } foo();
如果函數(shù)體是嚴(yán)格模式而參數(shù)列表不是簡(jiǎn)單參數(shù)列表,則語(yǔ)法錯(cuò)誤:
//不是簡(jiǎn)單參數(shù)指的是包含解構(gòu)賦值 function foo(a=1,...c){ "use strict" //SyntaxError } //如果"use strict"在函數(shù)體外定義則沒(méi)有錯(cuò)誤 "use strict" function foo(a=1,...c){} //ok
函數(shù)體以及函數(shù)參數(shù)不能直接出現(xiàn)super
function foo(super){} //SyntaxError function foo(){ super();} //SyntaxErrorFunctionDeclaration
FunctionDeclaration分為帶變量名的函數(shù)聲明以及匿名函數(shù)聲明,匿名函數(shù)聲明只能在export中可用,其它任何地方使用匿名函數(shù)聲明都報(bào)錯(cuò)。
在進(jìn)行評(píng)估腳本和函數(shù)的時(shí)候會(huì)對(duì)包含在其中的函數(shù)聲明進(jìn)行InstantiateFunctionObject方法,即初始化函數(shù)對(duì)象。注:該方法是在執(zhí)行腳本和函數(shù)代碼之前進(jìn)行的。
InstantiateFunctionObject方法簡(jiǎn)單來(lái)說(shuō)做了三步:1.FunctionCreate 2.makeConstructor 3. SetFunctionName。分開(kāi)說(shuō)
FunctionCreate創(chuàng)建了一個(gè)Function對(duì)象F,包括初始化內(nèi)部插槽的值,比如上面提到的[[Call]],[[Construct]]方法的定義,原型對(duì)象[[Prototype]]的值,這里指的是Function.prototype,F(xiàn)的length屬性,指function的參數(shù)個(gè)數(shù)。
makeConstructor(F),這句話不是指創(chuàng)建構(gòu)造器,這里指定義了F的prototype屬性的值,以及prototype中constructor的值,普通函數(shù)prototype相當(dāng)于object.create(Object.prototype),constructor===F。
SetFunctionName,定義了F的name屬性,默認(rèn)是聲明的函數(shù)名,匿名函數(shù)是default。
function foo(a,b){}; foo.__proto__ === Function.prototype; foo.length === 2; foo.prototype.__proto__ === Object.prototype; foo.prototype.constructor === foo; foo.name === "foo";FunctionExpression
FunctionExpression也分為兩類,有變量名的函數(shù)表達(dá)式和匿名函數(shù)表達(dá)式。函數(shù)表達(dá)式在執(zhí)行時(shí)也會(huì)創(chuàng)建Function對(duì)象,步驟和函數(shù)聲明相似。其中匿名函數(shù)表達(dá)式不會(huì)定義屬性name,即不會(huì)執(zhí)行第三步中的SetFunctionName。有變量名的函數(shù)表達(dá)式與函數(shù)聲明以及匿名函數(shù)表達(dá)式的區(qū)別在于作用域鏈,我們都知道一旦函數(shù)表達(dá)式中定義了變量名,我們就可以在函數(shù)體內(nèi)通過(guò)該變量名調(diào)用函數(shù)自身??蓡?wèn)題來(lái)了,該函數(shù)變量名是定義在哪里呢?函數(shù)外還是在函數(shù)內(nèi)呢?
var func = function foo(){}; foo(); //Uncaught ReferenceError: foo is not defined //顯然沒(méi)有在函數(shù)外定義函數(shù)表達(dá)式的變量名,那么是定義在函數(shù)內(nèi)的? //我提到過(guò)在全局作用域和函數(shù)作用域中,var、function聲明的變量,let和const不能重復(fù)聲明。 var func = function foo(){ let foo = 1; //ok,可見(jiàn)函數(shù)表達(dá)式的變量名也不是在函數(shù)內(nèi)聲明的。 }; foo();
看到這可能有人會(huì)認(rèn)為函數(shù)表達(dá)式的變量名可能允許let和const進(jìn)行覆蓋。其實(shí)不是,有變量名的函數(shù)表達(dá)式在創(chuàng)建Function對(duì)象的時(shí)候,創(chuàng)建了一個(gè)匿名作用域,在該作用域中定義了函數(shù)表達(dá)式的變量名。按上面這個(gè)例子,foo函數(shù)的外部作用域并不是全局作用域,而是一個(gè)匿名作用域,匿名作用域的外部作用域才是真正的全局作用域。匿名函數(shù)表達(dá)式和函數(shù)聲明都不會(huì)創(chuàng)建匿名作用域。
ArrowFunctionArrowFunction(箭頭函數(shù))是ES6新增的一種新語(yǔ)法,主要是用來(lái)簡(jiǎn)化function的寫(xiě)法,更準(zhǔn)確的說(shuō)是簡(jiǎn)化匿名函數(shù)表達(dá)式的一種寫(xiě)法。因此匿名函數(shù)表達(dá)式的規(guī)則也適用于ArrowFunction,不過(guò)兩者還是有區(qū)別的,ArrowFunction中沒(méi)有規(guī)定不能直接出現(xiàn)super,也就是說(shuō)在ArrowFunction中可以用super方法,其次ArrowFunction內(nèi)部沒(méi)有[[Construct]]方法,因此不能作為構(gòu)造器調(diào)用,所以在創(chuàng)建Function對(duì)象時(shí)不執(zhí)行makeConstructor方法。最重要一點(diǎn)就是ArrowFunction沒(méi)有本地的this對(duì)象。
我們上面提道所有Function對(duì)象都有[[Call]]內(nèi)部方法,接受this對(duì)象和參數(shù)列表兩個(gè)字段。此外Function對(duì)象還有一個(gè)[[ThisMode]]內(nèi)部屬性,用來(lái)判斷是ArrowFunction還是非ArrowFunction,如果是ArrowFunction,那么不管[[Call]]中傳來(lái)的this是什么都會(huì)被丟棄。此外arguments, super和new.target和this也是一樣的。我在以前的文章中稍微提到過(guò)ArrowFunction中的this對(duì)象,我在這重新講一下:
var name = "outer arrow"; var obj = { name:"inner arrow", arrow: () => { console.log(this.name) } } obj.arrow(); //outer arrow,不是inner arrow
我們?cè)贏rrowFunction遇到this對(duì)象時(shí),你不要把this看成是ArrowFunction的一部分,你從ArrowFunction中拿出this放到ArrowFunction的外部,觀察外部的this對(duì)象是什么,外部的this對(duì)象就是ArrowFunction的this對(duì)象。此外還要清楚不管是call還是apply都是對(duì)ArrowFunction無(wú)效的,它們最終調(diào)用的都是[[Call]]內(nèi)部方法,當(dāng)然bind也是無(wú)效的。
我們看一下ArrowFunction中的super應(yīng)用,還是改編了MDN中的例子:
var obj1 = { method() { console.log("method 1"); } } var obj2 = { method() { console.log("method 2"); return ()=>{super.method();} } } Object.setPrototypeOf(obj2, obj1); var arrow = obj2.method() //method 2 arrow(); //method 1
注意:method1和method2其實(shí)就是Method Definitions。
如果單看arrow這個(gè)函數(shù),它本身是不可能有super的,因?yàn)闆](méi)有任何繼承關(guān)系,只是一個(gè)單一的ArrowFunction,但是你放在obj2中就有了意義,Object.setPrototypeOf(obj2, obj1);這句話把obj1變?yōu)閛bj2的原型對(duì)象,obj2繼承了obj1的屬性和方法,obj2的super對(duì)象就是obj1,因此ArrowFunction中的super參照this可知,該super是obj1。
總的一句話概括ArrowFunction中的this,arguments, super和new.target都是通過(guò)原型鏈來(lái)查找的,不是動(dòng)態(tài)創(chuàng)建的。
GeneratorFunction關(guān)于GeneratorFunction我不準(zhǔn)備講怎么用它,我只談一下它的工作原理。說(shuō)實(shí)話GeneratorFunction用到的情況實(shí)在太少了,我自己在做項(xiàng)目的時(shí)候基本不會(huì)用到GeneratorFunction,但這不妨礙我們學(xué)習(xí)GeneratorFunction。
GeneratorFunction也是Function的一種,F(xiàn)unction的規(guī)則也適用于GeneratorFunction,此外在GeneratorFunction的參數(shù)中不能出現(xiàn)yield表達(dá)式。
GeneratorFunction與普通的Function一樣,都會(huì)創(chuàng)建Function對(duì)象,但是區(qū)別也在這里,上面提到了Function的[[Prototype]]原型值是Function.prototype,但是GeneratorFunction不同,它的[[Prototype]]原型值是%Generator%,此外Function的prototype屬性是ObjectCreate(Object.prototype),但是GeneratorFunction的卻是ObjectCreate(%GeneratorPrototype%)而且prototype中沒(méi)有constructor屬性,不能作為構(gòu)造器調(diào)用。
注:Function.prototype也寫(xiě)作%FunctionPrototype%,Object.prototype也寫(xiě)作%ObjectPrototype%。%Generator%和%GeneratorPrototype%沒(méi)有全局名稱,不能直接訪問(wèn)。
執(zhí)行函數(shù)時(shí),內(nèi)部調(diào)用了[[Call]]方法,但是和Function不同的是GeneratorFunction返回的不是函數(shù)的執(zhí)行結(jié)果,而是一個(gè)對(duì)象,這個(gè)對(duì)象是GeneratorFunction的一個(gè)實(shí)例,這跟[[Construct]]方法很像。
function* gen(){} gen(); //返回的其實(shí)是Object.create(gen.prototype)對(duì)象。
你可以比較gen()和Object.create(gen.prototype)這兩個(gè)對(duì)象,你會(huì)發(fā)現(xiàn)它們很像,只是Object.create(gen.prototype)缺少了Generator對(duì)象的一些內(nèi)部狀態(tài)??梢哉f(shuō)雖然GeneratorFunction沒(méi)有[[Construct]]方法,不能作為構(gòu)造器調(diào)用,但是你可以認(rèn)為GeneratorFunction本身就是一個(gè)構(gòu)造器。
此外在創(chuàng)建GeneratorFunction對(duì)象時(shí),還做了一些其他操作,我們?cè)谝郧暗奈恼轮刑岬搅藞?zhí)行上下文,GeneratorFunction對(duì)象有個(gè)[[GeneratorContext]]內(nèi)部插槽,當(dāng)評(píng)估GeneratorFunction定義的代碼時(shí),GeneratorFunction對(duì)象把當(dāng)前正在運(yùn)行的執(zhí)行上下文存在了[[GeneratorContext]]中,并掛起了該正在運(yùn)行的執(zhí)行上下文,因此對(duì)GeneratorFunction中代碼的評(píng)估被暫停了,從而執(zhí)行其它代碼,當(dāng)你調(diào)用GeneratorFunction對(duì)象的next方法時(shí),他把[[GeneratorContext]]中保存的執(zhí)行上下文取出放到執(zhí)行上下文棧頂部,成為正在運(yùn)行的執(zhí)行上下文,此時(shí)GeneratorFunction中暫定評(píng)估的代碼又重新開(kāi)始執(zhí)行,直到執(zhí)行完畢或者遇到y(tǒng)ield表達(dá)式。當(dāng)遇到y(tǒng)ield表達(dá)式時(shí),它又把正在運(yùn)行的執(zhí)行上下文從棧中移除,暫停對(duì)GeneratorFunction代碼的執(zhí)行,等待下次next方法調(diào)用之后繼續(xù)執(zhí)行。
簡(jiǎn)單來(lái)說(shuō)GeneratorFunction的實(shí)現(xiàn)原理其實(shí)是運(yùn)行的執(zhí)行上下文之間不停來(lái)回切換。
GeneratorFunction基本就提到這里了,最后附上ECMAScript關(guān)于GeneratorFunction的一張關(guān)系圖片:
關(guān)于Function的簡(jiǎn)單介紹就暫時(shí)告一段落,在下一篇文章中我會(huì)來(lái)簡(jiǎn)單介紹Promise和AsyncFunction。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/92062.html
摘要:箭頭函數(shù)沒(méi)有綁定,意味著箭頭函數(shù)內(nèi)部的值只能通過(guò)查找作用域鏈來(lái)確定。無(wú)論此后箭頭函數(shù)在何處執(zhí)行,該對(duì)象都是可用的。 箭頭函數(shù) es6的箭頭函數(shù),顧名思義箭頭函數(shù)是使用一個(gè)箭頭( => )來(lái)定義的函數(shù),這很容易理解但是它有很多行為與傳統(tǒng)的js函數(shù)不同: 沒(méi)有 this 、 super 、 arguments 。 不能被使用 new 調(diào)用: 箭頭函數(shù)沒(méi)有 [[Construct]] 方法...
摘要:指向的改變構(gòu)造函數(shù)中的操作符會(huì)調(diào)用函數(shù)的內(nèi)部的方法,創(chuàng)建對(duì)象,之后調(diào)用函數(shù)的方法,把新創(chuàng)建對(duì)象作為值。調(diào)用函數(shù)時(shí)與設(shè)置的值以及箭頭函數(shù)皆為動(dòng)態(tài)的改變指針的方法。這一特性使得箭頭函數(shù)在中的函數(shù)中使用起來(lái)很方便。 原文地址 JavaScript中的this 原理 錯(cuò)誤的this指向 通常所說(shuō)的:如果是全局環(huán)境中,this指向全局對(duì)象,如果是對(duì)象的方法,這this指向這個(gè)對(duì)象。 例子1: ...
摘要:花點(diǎn)時(shí)間搞清楚中的分號(hào)規(guī)則吧不管你喜歡結(jié)尾帶分號(hào)或省略分號(hào)的模式分號(hào)允許的場(chǎng)景分號(hào)一般允許出現(xiàn)在大部分語(yǔ)句的末尾,比如等栗子僅有一個(gè)分號(hào)可以表示空語(yǔ)句在中合法,比如可解析為三個(gè)空語(yǔ)句空語(yǔ)句可用于輔助產(chǎn)生語(yǔ)法合法的解析結(jié)果,如如果沒(méi)有末尾的 花點(diǎn)時(shí)間搞清楚JS中的分號(hào)規(guī)則吧~~~不管你喜歡結(jié)尾帶分號(hào)或省略分號(hào)的模式 分號(hào)允許的場(chǎng)景 分號(hào)一般允許出現(xiàn)在大部分語(yǔ)句(statement)的末尾...
摘要:改動(dòng)函數(shù)的改變不算太大,都是一些其他語(yǔ)言早就有的功能,而一直比較欠缺的,比如函數(shù)參數(shù)默認(rèn)值,任意參數(shù)的表示法,最大的變化應(yīng)該是支持箭頭函數(shù)其他語(yǔ)言稱之為表達(dá)式,一種對(duì)匿名函數(shù)的一種簡(jiǎn)寫(xiě)方式,以下來(lái)探討一下函數(shù)在中的一些改變默認(rèn)參數(shù)任意參數(shù)操 ES6 functions改動(dòng) ????ES6函數(shù)的改變不算太大,都是一些其他語(yǔ)言早就有的功能,而Javascript一直比較欠缺的,比如函數(shù)參數(shù)...
摘要:如下在第一個(gè)例子中,被點(diǎn)擊元素是通過(guò),這個(gè)形式參數(shù)來(lái)代替的。它的作用和形式參數(shù)類似,其本質(zhì)上是一個(gè)對(duì)象的引用,它的特殊性在于不需要手動(dòng)傳值,所以使用起來(lái)會(huì)更加簡(jiǎn)單和方便。 無(wú)論在 javascript 的日常使用中還是前端面試過(guò)程中,this 的出鏡率都極高。這無(wú)疑說(shuō)明了,this 的重要性。但是 this 非常靈活,導(dǎo)致很多人覺(jué)得 this 的行為難以理解。本文從為什么要有 this...
閱讀 1144·2021-10-27 14:13
閱讀 2648·2021-10-09 09:54
閱讀 927·2021-09-30 09:46
閱讀 2436·2021-07-30 15:30
閱讀 2178·2019-08-30 15:55
閱讀 3422·2019-08-30 15:54
閱讀 2862·2019-08-29 14:14
閱讀 2783·2019-08-29 13:12