成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

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

tanglijun / 1757人閱讀

摘要:如果是或者,會(huì)將作為值。否則,被調(diào)用的函數(shù),進(jìn)行轉(zhuǎn)換后,作為值。又怎么操作這個(gè)很神奇。能轉(zhuǎn)換它的參數(shù)為到總共個(gè)整數(shù)中的一個(gè),這個(gè)函數(shù)遵循以下規(guī)則。不斷加入新方法的規(guī)范也是這個(gè)初衷。

今天看element-react源碼的時(shí)候,又看到了這張似曾相識(shí)卻又異常陌生的老面孔,那就是Function.prototype.apply()...

import React from "react";
import PropTypes from "prop-types";
import classnames from "classnames";

export default class Component extends React.Component {
  classNames(...args) {
         return classnames(args);
  }

  className(...args) {
         return this.classNames.apply(this, args.concat([this.props.className]));
  }

  style(args) {
         return Object.assign({}, args, this.props.style)
  }
}

Component.propTypes = {
  className: PropTypes.string,
  style: PropTypes.object
};

雖然高設(shè),犀牛書以及你不知道的Javascrit都看過(guò)apply的原理,但依然沒(méi)什么卵用,可能是缺少實(shí)踐的原因,看完就忘。

Q:
其中這句this.classNames.apply(this, args.concat([this.props.className])),又把我搞得暈頭轉(zhuǎn)向,this到底是誰(shuí)?沒(méi)猜錯(cuò)的話應(yīng)該是組件實(shí)例吧?
那為啥不直接 this.classNames(args.concat([this.props.className])),直接傳一個(gè)數(shù)組不就行了嗎?
為什么一定要用this.classNames.apply(this, args.concat([this.props.className]))?
同理,印象中apply用來(lái)取數(shù)組最大最小值很方便,Math.max.apply(null,[1,3,8,2]),但是Math.apply( [1,3,8,2] )卻返回傳參類型錯(cuò)誤?這又是為什么?

帶著這些問(wèn)題,我又一次開(kāi)始了啃規(guī)范之旅。

Function.prototype.apply (thisArg, argArray)
The apply method takes two arguments, thisArg and argArray, and performs a function call using the [[Call]] property of the object. If the object does not have a [[Call]] property, a TypeError exception is thrown. If thisArg is null or undefined, the called function is passed the global object as the this value. Otherwise, the called function is passed ToObject(thisArg) as the this value. If argArray is null or undefined, the called function is passed no arguments. Otherwise, if argArray is neither an array nor an arguments object (see 10.1.8), a TypeError exception is thrown. If argArray is either an array or an arguments object, the function is passed the (ToUint32(argArray.length)) arguments argArray[0], argArray[1], …, argArray[ToUint32(argArray.length)–1].

apply有2個(gè)方法,一個(gè)是thisArg,一個(gè)是argArray,然后再執(zhí)行對(duì)象的[[call]]屬性。如果對(duì)象沒(méi)有call屬性,一個(gè)TypeError會(huì)被扔出來(lái)。
如果thisArg是null或者undefined,會(huì)將global作為this值。否則,被調(diào)用的函數(shù),進(jìn)行ToObject(thisArg)轉(zhuǎn)換后,作為this值。
如果argArray是null或者undefined,被調(diào)用函數(shù)不傳入任何參數(shù)。否則,argArray如果既不是數(shù)組也不是arguments object(類數(shù)組對(duì)象),會(huì)報(bào)錯(cuò)TypeError.
如果 argArray是數(shù)組或者類數(shù)組對(duì)象,被調(diào)用函數(shù)傳入ToUnit32(argArray.length) arguments argArray[0],argArray[1]一直到argArray[ToUint32(argArray.length)–1].

ToObject內(nèi)部怎么操作?這個(gè)不用管。
ToUnit32又怎么操作?這個(gè)很神奇。

ToUint32: (Unsigned 32 Bit Integer)
The operator ToUint32 converts its argument to one of 232 integer values in the range 0 through 232?1,
inclusive. This operator functions as follows:

Call ToNumber on the input argument.

If Result(1) is NaN, +0, ?0, +∞, or ?∞, return +0.

Compute sign(Result(1)) * floor(abs(Result(1))).

Compute Result(3) modulo 232; that is, a finite integer value k of Number type with positive sign and

less than 232 in magnitude such the mathematical difference of Result(3) and k is mathematically an
integer multiple of 232.

Return Result(4).

ToUnit32能轉(zhuǎn)換它的參數(shù)為0到231總共232個(gè)整數(shù)中的一個(gè),這個(gè)函數(shù)遵循以下規(guī)則。
1.對(duì)輸入?yún)?shù)調(diào)用ToNumber()函數(shù)
2.如果Result(1)是 NaN, +0, ?0, +∞, 或者 ?∞, return +0
3.計(jì)算sign(Result(1)*floor(abs(Result(1))))
4.計(jì)算Result(3) modulo 232;就是說(shuō) 一個(gè)無(wú)窮的正整數(shù)值k,大于0小于232,Result(3)與k的數(shù)學(xué)差異是232的整數(shù)倍。
5.返回Result(4)

A1:
多說(shuō)無(wú)益,舉個(gè)例子實(shí)在:
Math.max.apply(null,[1,3,2,""])

第一步:global值替換null,瀏覽器環(huán)境為window
Math.max.apply(window,[1,3,2,""])

第二步:[1,3,2,""].length傳入到ToUnit32()中
ToUnit32(4)

第三步:計(jì)算ToUnit32(4), 作為數(shù)組轉(zhuǎn)換成arguments類數(shù)組對(duì)象的編號(hào)
1.ToNumber(4)→Result(1)=4
2.Not pass the condition
3.Math.sign(4*Math.floor(Math.abs(4))) →Result(3)=1
4.1 Mod 232 → Result(4) =1
5.return 1

第四步:傳入arguments到Math.max中
第三步會(huì)生成(1)arguments 類數(shù)組對(duì)象(是否為純函數(shù)方式生成暫時(shí)未知),將這個(gè)arguments傳入到Math.max中,與直接這樣寫Math.max(1,3,2,"")的效果一樣,此時(shí)就會(huì)返回最大值3。

這里有一個(gè)很重要的坑,為什么arguments傳入,也可以像正常Math.max(1,3,2,"")一樣?
其實(shí)這個(gè)坑是因?yàn)镸ath.max(1,3,2,"")這個(gè)例子中的實(shí)參,在函數(shù)內(nèi)部的實(shí)參就是arguments,且這個(gè)arguments的長(zhǎng)度為4,這和絕大多數(shù)的函數(shù)一樣,除箭頭函數(shù)外的所有函數(shù),都有這個(gè)arguemnts實(shí)參array-like object。

看完這個(gè), Math.max.apply(null,[1,3,8,2])的內(nèi)部原理應(yīng)該也就清楚了吧。

A2:
回到源代碼中this.classNames.apply(this, args.concat([this.props.className]))。
1.為什么直接傳入["foo","bar"]數(shù)組不行?
因?yàn)楫?dāng)我們傳入["foo","bar"]到classNames時(shí),此時(shí)return classnames(args);中的args會(huì)變成一個(gè)二維數(shù)組[["foo","bar"]],與classnames模塊的預(yù)期值類型一維數(shù)組相悖。
2.為什么調(diào)用Function.prototype.apply后就可以?
因?yàn)槿缫?guī)范中的第三步中所示, this.classNames.apply(this, args.concat([this.props.className]))中,在apply的內(nèi)部算法中,會(huì)將args.concat([this.props.className])這個(gè)數(shù)組,轉(zhuǎn)換為一個(gè)實(shí)參類數(shù)組對(duì)象arguments,跳過(guò)形參賦值步驟,直接深入到 this.classNames()內(nèi)部,這樣一來(lái),就實(shí)現(xiàn)了一個(gè)一個(gè)傳值的效果,其中的...args就會(huì)成為一個(gè)一個(gè)獨(dú)立的參數(shù),而args會(huì)作為實(shí)參數(shù)組傳給classnames函數(shù)。

說(shuō)了這么多,總結(jié)起來(lái)其實(shí)就一句話:

callObject.method.apply(thisArg,thisArray),可以將thisArray轉(zhuǎn)化為arguments,傳入到callObject.method內(nèi)部。

加粗加斜的這句真的很重要!
加粗加斜的這句真的很重要!
加粗加斜的這句真的很重要!

參考:
1.The mathematical function sign(x) yields 1 if x is positive and ?1 if x is negative. The sign function is not
used in this standard for cases when x is zero
2.求模
對(duì)于整數(shù)a,b來(lái)說(shuō),取模運(yùn)算或者求余運(yùn)算的方法要分如下兩步:
1.求整數(shù)商:c=a/b
2.計(jì)算?;蛘哂鄶?shù):r=a-(c*b)
求模運(yùn)算和求余運(yùn)算在第一步不同
取余運(yùn)算在計(jì)算商值向正無(wú)窮方向舍棄小數(shù)位
取模運(yùn)算在計(jì)算商值向負(fù)無(wú)窮方向舍棄小數(shù)位
例如:4/(-3)約等于-1.3
在取余運(yùn)算時(shí)候商值向0方向舍棄小數(shù)位為-1
在取模運(yùn)算時(shí)商值向負(fù)無(wú)窮方向舍棄小數(shù)位為-2
所以
4rem(-3)=1
4mod(-3)=-2
3.C和JS中%表示取余,python中表示取模
python中%表示取模,have a try 1 Mod 232 →1

Merry Christmas ~
That"s it !

2019.8.20更新

js忍者秘籍給出的精簡(jiǎn)解釋是:“js可以通過(guò)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ù)上下文
// 通過(guò)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)建的與前端開(kāi)發(fā)密切相關(guān)的技術(shù)討論小組:

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

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

掘金主頁(yè):趁你還年輕233

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

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

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

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

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

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

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

相關(guān)文章

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

    摘要:抽象操作是在調(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)改變。萬(wàn)事具備,執(zhí)行,調(diào)用函數(shù)即可。 showImg(https://segmentfault.com/img/bVbwBui?w=657&h=382); 今天在學(xué)習(xí)前端工程化的過(guò)程中,遇到一個(gè)是實(shí)驗(yàn)...

    DandJ 評(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ù)修改是通過(guò)給的內(nèi)置方法傳遞來(lái)實(shí)現(xiàn)的。 this 說(shuō)到this,需要明確三方面內(nèi)容: this何時(shí)被賦值 this被賦了什么值 內(nèi)置函數(shù)如何使用this的 this何時(shí)被賦值 進(jìn)入函數(shù)代碼 當(dāng)控制流...

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

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

    Alex 評(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
  • 看完這篇文章還不會(huì)call/apply/bind來(lái)找我。

    摘要:關(guān)于的指向的問(wèn)題請(qǐng)參照我的學(xué)習(xí)筆記。那么在這里事實(shí)上都改變了函數(shù)方法被調(diào)用時(shí)的指向。那么回調(diào)函數(shù)在執(zhí)行的時(shí)候指向還是。大家看完之后應(yīng)該已經(jīng)懂了把還是不懂的話在評(píng)論區(qū)留言,我給大家解答。 先從一個(gè)小題目開(kāi)始吧: 要實(shí)現(xiàn)一個(gè)加法函數(shù),這個(gè)時(shí)候向函數(shù)當(dāng)中傳遞個(gè)數(shù)大于0的若干個(gè)整形數(shù)據(jù),求所有這些數(shù)據(jù)的和。 Function.prototype.call Function.prototype...

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

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

0條評(píng)論

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