亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

從規(guī)范去看Function.prototype.call到底是怎么工作的?

DandJ / 2583人閱讀

摘要:抽象操作是在調(diào)用函數(shù)對(duì)象的內(nèi)部的方法。指的是調(diào)用函數(shù),指的是的值,然后是傳入到內(nèi)部方法相應(yīng)參數(shù)的值。切換上下文執(zhí)行,為函數(shù)調(diào)用棧在尾部調(diào)用函數(shù)做準(zhǔn)備,切換運(yùn)行中執(zhí)行上下文,實(shí)現(xiàn)上下文的動(dòng)態(tài)改變。萬事具備,執(zhí)行,調(diào)用函數(shù)即可。

今天在學(xué)習(xí)前端工程化的過程中,遇到一個(gè)是實(shí)驗(yàn)中的css屬性:fullscreen,有這樣一個(gè)例子:fullscreen偽元素官方demo

:fullscreen Demo

This text will become big and red when the browser is in fullscreen mode.

其中有一段代碼:

function enterFullscreen() {
    fullscreenFunc.call(fullscreenDiv);
}?

雖然結(jié)合上下文能看出來是為了兼容瀏覽器的fullscreen API,但是其中的Function.prototype.call()我自己其實(shí)沒有特別深究過。

為什么不直接fullscreenFunc(),這樣不能使得fullscreenDiv全屏嗎?

大家都說call與apply都是為了動(dòng)態(tài)改變this的,僅僅是傳入?yún)?shù)的方式不同,call傳入(this,foo,bar,baz),而apply傳入(this,[foo,bar,baz])那么事實(shí)真如大家所說的那樣嗎?既然apply能動(dòng)態(tài)改變this,那么為什么還要多此一舉開放一個(gè)call?
這其中肯定隱藏著一些秘密,那就是有些事情是apply做不到,而call可以勝任的。
繼續(xù)我們的啃規(guī)范之旅,去深入到Function.prototype.call()的內(nèi)部,徹底把它搞清楚。

19.2.3.4 Function.prototype.call (thisArg , ...args)
When the?call?method is called on an object?func with argument,?thisArg?and zero or more?args, the following steps are taken:

If?IsCallable(func) is?false, throw a?TypeError?exception.

Let?argList?be an empty?List.

If this method was called with more than one argument then in left to right order, starting with the second argument, append each argument as the last element of?argList.

Perform?PrepareForTailCall().

Return?Call(func,?thisArg,?argList).

The?length?property of the?call?method is?1.

當(dāng)call方法在帶參數(shù)的對(duì)象的方法上調(diào)用時(shí),thisArg和零個(gè)或者對(duì)個(gè)參數(shù),會(huì)進(jìn)行如下的步驟:

如果IsCallable(func)返回false,拋出TypeError異常。

定義argList為一個(gè)空的列表。

如果方法按照從左到右傳入的參數(shù)個(gè)數(shù)不止一個(gè),從第二個(gè)參數(shù)開始,依次將每個(gè)參數(shù)從尾部添加到argList數(shù)組。

執(zhí)行PrepareForTailCall()

返回Call(func,thisArg,argList)

有3個(gè)點(diǎn)看不懂:

IsCallable(func)

PrepareForTailCall()

Call(func,thisArg,argList)

這些同樣在規(guī)范中有對(duì)應(yīng)描述:

7.2.3IsCallable ( argument )

The abstract operation IsCallable determines if?argument, which must be an?ECMAScript language valueor a?Completion Record, is a callable function with a [[Call]] internal method.

重點(diǎn)在于is a callable function with a [[Call]] internal method.,也就是說執(zhí)行isCallable(func)運(yùn)算的func,如果函數(shù)內(nèi)部有一個(gè)內(nèi)在的[[Call]]方法,那么運(yùn)算結(jié)果為true,也就是說這個(gè)函數(shù)是可調(diào)用的的。(callable)

14.6.3Runtime Semantics: PrepareForTailCall ( )

The abstract operation PrepareForTailCall performs the following steps:

Let?leafContext?be?the running execution context.

Suspend?leafContext.

Pop?leafContext?from?the execution context stack. The?execution context?now on the top of the stack becomes?the running execution context.

Assert:?leafContext?has no further use. It will never be activated as?the running execution context.

A tail position call must either release any transient internal resources associated with the currently executing function?execution context?before invoking the target function or reuse those resources in support of the target function.

ReturnIfAbrupt(argument).

If?Type(argument) is not Object, return?false.

If?argument?has a [[Call]] internal method, return?true.

Return?false.

雖然看不懂,但還是得硬著頭皮學(xué)習(xí)一波。
抽象操作PrepareForTailCall執(zhí)行以下幾個(gè)步驟:

讓葉子上下文成為運(yùn)行中的執(zhí)行上下文

暫停葉子上下文

頂葉子上下文來自執(zhí)行上下文的堆。當(dāng)前的在堆頂部的執(zhí)行上下文成為運(yùn)行中的執(zhí)行上下文

斷言:葉子上下文沒有其他作用。它再也不會(huì)作為運(yùn)行中執(zhí)行上下文被激活。

在調(diào)用目標(biāo)函數(shù)或者重用這些資源去支持目標(biāo)函數(shù)之前,尾部位置調(diào)用必須釋放與當(dāng)前執(zhí)行函數(shù)上下文相關(guān)的瞬態(tài)內(nèi)部資源。

ReturnIfAbrupt(argument).

如果Type(argument)不是對(duì)象,返回false。

如果argument含有[[call]]內(nèi)部方法,返回true。

返回 false

看懂一個(gè)大概,是為了在函數(shù)調(diào)用棧的尾部調(diào)用當(dāng)前函數(shù)做準(zhǔn)備,其中的運(yùn)行中執(zhí)行上下文,正是我們所說的this動(dòng)態(tài)改變的原因,因?yàn)楸举|(zhì)上this改變并不僅僅是指向的對(duì)象發(fā)生變化,而是連帶著與其相關(guān)的上下文都發(fā)生了變化。

所以說,這一步是this動(dòng)態(tài)改變的真正原因。

7.3.12Call(F, V, [argumentsList])

The abstract operation Call is used to call the [[Call]] internal method of a function object. The operation is called with arguments?F,?V?, and optionally?argumentsList?where?F?is the function object,?V?is an?ECMAScript language value?that is the?this?value of the [[Call]], and?argumentsList?is the value passed to the corresponding argument of the internal method. If argumentsList?is not present, an empty?List?is used as its value. This abstract operation performs the following steps:

ReturnIfAbrupt(F).

If?argumentsList?was not passed, let?argumentsList?be a new empty?List.

If?IsCallable(F) is?false, throw a?TypeError?exception.

Return?F.[[Call]](V,?argumentsList).

Call抽象操作是在調(diào)用函數(shù)對(duì)象的內(nèi)部的[[Call]]方法。這個(gè)操作參數(shù)類型包括F,V以及可選的argumentList。F指的是調(diào)用函數(shù),V指的是[[Call]]的this值,然后argumentsList是傳入到[[Call]]內(nèi)部方法相應(yīng)參數(shù)的值。如果argumentList不存在,那么argumentList將被置為一個(gè)空數(shù)組。這個(gè)方法按照下列幾步執(zhí)行:

ReturnIfAbrupt(F)

如果沒傳入argumentList,那么argumentList將會(huì)被置為一個(gè)空數(shù)組。

如果IsCallable(F)是false,返回TypeError異常。

返回 F.[[call]](V,argumentsList).

所以Function.prototype.call(this,...args)執(zhí)行過程現(xiàn)在很明了:

判斷傳入的func是否有[[call]]屬性,有[[call]]才意味著函數(shù)能被調(diào)用,否則拋出TypeError異常。

定義argList為一個(gè)空的列表。

傳參:如果方法按照從左到右傳入的參數(shù)個(gè)數(shù)不止一個(gè),從第二個(gè)參數(shù)開始,依次將每個(gè)參數(shù)從尾部添加到argList數(shù)組。

切換this上下文:執(zhí)行PrepareForTailCall(),為函數(shù)調(diào)用棧在尾部調(diào)用函數(shù)做準(zhǔn)備,切換運(yùn)行中執(zhí)行上下文,實(shí)現(xiàn)this上下文的動(dòng)態(tài)改變。

萬事具備,執(zhí)行Call(func,thisArg,argList),調(diào)用函數(shù)即可。

回到我們的例子:

fullscreenFunc.call(fullscreenDiv);

func為fullscreenDiv DOM 節(jié)點(diǎn)的方法:"requestFullscreen" || "mozRequestFullScreen" || "msRequestFullscreen"

|| "webkitRequestFullScreen",由于是fullscreen API,所以isCallable(func)返回true。

定義一個(gè)argList空數(shù)組用來傳參。

傳參:由于fullscreenFunc.call(fullscreenDiv);只有一個(gè)參數(shù),所以直接傳入argList空數(shù)組。

切換this上下文:停止當(dāng)前的this葉子上下文,也就是window,切換到fullscreenDiv的執(zhí)行上下文。

由于當(dāng)前瀏覽器為chrome,因此執(zhí)行 fullscreenDiv.webkitRequestFullscreen.[[call]](this,[])。

因此我們之前提的那個(gè)為什么不直接fullscreenFunc(),這樣不能使得fullscreenDiv全屏嗎?,答案就很清楚了?不能。
為什么呢?

var fullscreenFunc   = fullscreenDiv.requestFullscreen;
if (!fullscreenFunc) {
     ["mozRequestFullScreen", "msRequestFullscreen","webkitRequestFullScreen"].forEach(function (req) {
        fullscreenFunc = fullscreenFunc || fullscreenDiv[req];
     });
}

下面的代碼,僅僅是獲得了fullscreenDiv對(duì)象的fullscreen request API的引用,而fullscreenFunc的作用域是全局的window對(duì)象,也就是this的當(dāng)前指向?yàn)閣indow。

而我們是想觸發(fā)window的子對(duì)象fullscreenDiv的全屏方法,所以需要將this上下文切換為fullscreenDiv,這就是不直接調(diào)用fullscreenFunc(),需要fullscreenFunc.call(fullscreenDiv)的原因。

最近在看龍書,第一章講到動(dòng)態(tài)語言與靜態(tài)語言的區(qū)別,龍書中講到"運(yùn)行時(shí)決定作用域的語言是動(dòng)態(tài)語言,在編譯時(shí)指定作用域的預(yù)言是靜態(tài)語言"。例子中的以function關(guān)鍵字定義的類,this運(yùn)行中執(zhí)行上下文的切換,恰恰證明了javascript是一門動(dòng)態(tài)語言;再舉個(gè)形象的靜態(tài)語言的例子,java會(huì)使用class關(guān)鍵字構(gòu)建類,在類內(nèi)部使用private,public等關(guān)鍵字去指定作用域,編譯時(shí)就會(huì)去約束其作用域,具有非常強(qiáng)的約束性,this始終指向當(dāng)前類。

剛才和一個(gè)java后端同事確認(rèn),java也有this關(guān)鍵字,但是僅能使用當(dāng)前類中的方法,B類可以調(diào)用A類中的方法,比如通過super實(shí)現(xiàn)對(duì)父類的繼承,但是當(dāng)前類中的this指向是不會(huì)變的。

js中的this,是可以通過call或者apply進(jìn)行動(dòng)態(tài)切換從而去調(diào)用其他類中的方法的,B類不能調(diào)用A類中的方法。(注意:我們這里的類指的是以function關(guān)鍵字進(jìn)行定義的類,暫時(shí)不考慮es6的class關(guān)鍵字構(gòu)造類的方式。)

說了這么多,我們?cè)賮韽?qiáng)調(diào)下重點(diǎn):

加粗的部分是重點(diǎn)!
加粗的部分是重點(diǎn)!
加粗的部分是重點(diǎn)!

拋開V8引擎內(nèi)部執(zhí)行call和apply的原理不說,二者最終實(shí)現(xiàn)的都是this上下文的動(dòng)態(tài)切換,所以就像大家所說的那樣,都是動(dòng)態(tài)改變this。我們只要心里知道,其實(shí)二者在背后實(shí)現(xiàn)動(dòng)態(tài)切換this的操作部分有很大的不同就可以了,當(dāng)出現(xiàn)由于內(nèi)部實(shí)現(xiàn)細(xì)節(jié)引起的問題時(shí),我們可以快速定位。

That"s it !

2019.8.20更新

js忍者秘籍給出的精簡(jiǎn)解釋是:“js可以通過apply和call顯示指定任意對(duì)象作為其函數(shù)上下文?!睆?qiáng)烈建議閱讀P52~P55。言簡(jiǎn)意賅,通俗易懂。

主要有兩個(gè)用途:

普通函數(shù)中指定函數(shù)上下文

回調(diào)函數(shù)中強(qiáng)制指定函數(shù)上下文

回調(diào)函數(shù)強(qiáng)制指定函數(shù)上下文很好地體現(xiàn)了函數(shù)式編程的思想,創(chuàng)建一個(gè)函數(shù)接收每個(gè)元素,并且對(duì)每個(gè)元素做處理。

本質(zhì)上,apply和call都是為了增強(qiáng)代碼的可擴(kuò)展性,提升編程的效率。

我想這也是js中每一個(gè)方法或者api的初衷,提供更加便利的操作,解放初更多的生產(chǎn)力。不斷加入新方法的es規(guī)范也是這個(gè)初衷。

由于我使用vue比較多,所以根據(jù)以上的應(yīng)用場(chǎng)景出1個(gè)單文件組件示例和1個(gè)普通示例供參考:

// 普通函數(shù)中指定函數(shù)上下文
// 通過Math.max()獲得數(shù)組中的最大項(xiàng)
// 回調(diào)函數(shù)中強(qiáng)制指定函數(shù)上下文
// 手動(dòng)實(shí)現(xiàn)一個(gè)Array.prototype.filter
const numbers = [1, 2, 3, 4];
function arrayFilter(array, callback) {
  const result = [];
  for (let i = 0; i < array.length; i++) {
    const validate = callback.call(array[i], array[i]);
    if (validate) {
      result.push(array[i]);
    }
  }
  return result;
}

const evenArrays = arrayFilter(numbers, (n) => n % 2 === 0);
console.log(evenArrays);// [2, 4]

期待和大家交流,共同進(jìn)步,歡迎大家加入我創(chuàng)建的與前端開發(fā)密切相關(guān)的技術(shù)討論小組:

SegmentFault專欄:趁你還年輕,做個(gè)優(yōu)秀的前端工程師

Github博客: 趁你還年輕233的個(gè)人博客

掘金主頁:趁你還年輕233

SegmentFault技術(shù)圈: ES新規(guī)范語法糖

知乎專欄:趁你還年輕,做個(gè)優(yōu)秀的前端工程師

前端開發(fā)交流群:660634678

努力成為優(yōu)秀前端工程師!

加油,前端同學(xué)們!

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/94785.html

相關(guān)文章

  • 規(guī)范去看Function.prototype.apply到底怎么工作

    摘要:如果是或者,會(huì)將作為值。否則,被調(diào)用的函數(shù),進(jìn)行轉(zhuǎn)換后,作為值。又怎么操作這個(gè)很神奇。能轉(zhuǎn)換它的參數(shù)為到總共個(gè)整數(shù)中的一個(gè),這個(gè)函數(shù)遵循以下規(guī)則。不斷加入新方法的規(guī)范也是這個(gè)初衷。 showImg(https://segmentfault.com/img/remote/1460000012563719); 今天看element-react源碼的時(shí)候,又看到了這張似曾相識(shí)卻又異常陌生的老...

    tanglijun 評(píng)論0 收藏0
  • 【2】this

    摘要:否則如果是或,則設(shè)綁定為全局對(duì)象。令為解釋執(zhí)行的結(jié)果。返回一個(gè)值類型的引用,其基值為且其引用名為,嚴(yán)格模式標(biāo)記為。進(jìn)入函數(shù)代碼,為,非嚴(yán)格模式下將賦值為全局對(duì)象。內(nèi)置函數(shù)如何使用的內(nèi)置函數(shù)修改是通過給的內(nèi)置方法傳遞來實(shí)現(xiàn)的。 this 說到this,需要明確三方面內(nèi)容: this何時(shí)被賦值 this被賦了什么值 內(nèi)置函數(shù)如何使用this的 this何時(shí)被賦值 進(jìn)入函數(shù)代碼 當(dāng)控制流...

    Alex 評(píng)論0 收藏0
  • 搞懂JavaScriptFunction.prototype.bind[譯]

    摘要:搞懂的譯可能是初學(xué)的人最不關(guān)心的函數(shù),當(dāng)你意識(shí)到需要保持在其他函數(shù)中的上下文,實(shí)際上你需要的是。這就是問題所在。整合事件綁定和一個(gè)重大提高就是,和等等。然而,并沒有原生添加事件到多個(gè)節(jié)點(diǎn)的方式。能力有限,如有疑問,紕漏,速指出,感謝你 搞懂JavaScript的Function.prototype.bind[譯] Ben Howdle binding可能是初學(xué)Javascript的人最...

    Pandaaa 評(píng)論0 收藏0
  • javascript中Function、ArrowFunction和GeneratorFunctio

    摘要:等價(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 D...

    cyixlq 評(píng)論0 收藏0
  • 深入call apply bind

    摘要:眾所周知,這三個(gè)函數(shù)都是改變執(zhí)行上下文的,那么我們來捋一捋,這些函數(shù)內(nèi)部到底做了什么。 前言 稍微翻了一下call,apply, bind 的各種論壇上的文章, 發(fā)現(xiàn)講的都太淺了,大部分都只講了個(gè)用法, 對(duì)于實(shí)現(xiàn)的原理卻都沒有提,因此,在這里,我寫下這篇文章, 希望能讓大家認(rèn)識(shí)到原理所在。 眾所周知, 這三個(gè)函數(shù)都是改變執(zhí)行上下文的 , 那么我們來捋一捋,這些函數(shù)內(nèi)部到底做了什么。 c...

    Alex 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<