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

資訊專欄INFORMATION COLUMN

魔幻語(yǔ)言 JavaScript 系列之 call、bind 以及上下文

cuieney / 3133人閱讀

摘要:那么,它到底是如何工作的呢讓我們從一種更簡(jiǎn)單的實(shí)現(xiàn)開始實(shí)際上這種實(shí)現(xiàn)代碼更短,并且更易讀是函數(shù)原型中的一個(gè)函數(shù),它調(diào)用函數(shù),使用第一個(gè)參數(shù)作為參數(shù),并傳遞剩余參數(shù)作為被調(diào)用函數(shù)的參數(shù)。

原文:The Most Clever Line of JavaScript

作者:Seva Zaikov

原文

最近 一個(gè)朋友 發(fā)給我一段非常有趣的 JavaScript 代碼,是他在某個(gè) 開源庫(kù)中 看到的:

addressParts.map(Function.prototype.call, String.prototype.trim);

一開始,我覺得這是一個(gè)“不錯(cuò)的嘗試”。但是,印象中 map 好像只接受一個(gè)參數(shù),這里卻出現(xiàn)第二個(gè)參數(shù),所以去查看了 MDN文檔,才知道可以傳一個(gè)上下文(context)作為第二個(gè)參數(shù)。在這時(shí)候,我還無(wú)法解釋這段代碼,運(yùn)行完之后感到更加困惑了,因?yàn)樗谷荒苋珙A(yù)期那樣工作。

我花了至少半個(gè)小時(shí)來(lái)嘗試?yán)斫膺@段代碼,這是一個(gè)很有趣的例子,可以用來(lái)說(shuō)明 JavaScript 是一門多么魔幻的語(yǔ)言,即使已經(jīng)寫了好幾年的 JS。當(dāng)然,你可以選擇自己去弄清楚,如果想看看我的理解,請(qǐng)繼續(xù)閱讀。

那么,它到底是如何工作的呢?讓我們從一種更簡(jiǎn)單的實(shí)現(xiàn)開始(實(shí)際上這種實(shí)現(xiàn)代碼更短,并且更易讀:)):

addressParts.map(str => str.trim());

Function.prototype.call 是 JavaScript 函數(shù)原型中的一個(gè)函數(shù),它調(diào)用函數(shù),使用第一個(gè)參數(shù)作為 this 參數(shù),并傳遞剩余參數(shù)作為被調(diào)用函數(shù)的參數(shù)。舉個(gè)例子:

// this function has `Function` in prototype chain
// so `call` is available
function multiply(x, y) {
  return x * y;
}

multiply.call(null, 3, 5); // 15
multiply(3, 5); // same, 15

map 第二個(gè)參數(shù)的典型用法如下所示,假設(shè)有一個(gè)基于類的 React 組件,其功能是渲染一個(gè)按鈕列表:

class ExampleComponent extends Component {
  renderButton({ title, name }) {
    // without proper `this` it will fail
    const { isActive } = this.props;
    return (
      
    );
  }

  render() {
    const { buttons } = this.props;

    // without second param our function won"t be able
    // to access `this` inside
    const buttonsMarkup = buttons.map(this.renderButton, this);
  }
}

但是,以我的經(jīng)驗(yàn)來(lái)看,這種使用第二個(gè)參數(shù)的做法并不常見,更常見的做法是使用類屬性或裝飾器來(lái)避免綁定。

譯者注:map 第二個(gè)參數(shù)的用法等同于

const buttonsMarkup = buttons.map(this.renderButton.bind(this);

還有一個(gè)類似的方法 -- Function.prototype.apply,工作原理與 call 相同,只是第二個(gè)參數(shù)應(yīng)該是一個(gè)數(shù)組(譯者注:或者是一個(gè)類數(shù)組),它將被轉(zhuǎn)換成一個(gè)參數(shù)列表,用逗號(hào)分隔。所以,讓我們看看如何使用它來(lái)計(jì)算最大值:

Math.max(1, 2, 3); // if we know all numbers upfront
// we can call it like that

Math.max([1, 2, 3]); // won"t work!

Math.max.apply(null, [1, 2, 3]); // will work!

// however, ES2015 array destructuring works as well:
Math.max(...[1, 2, 3]);

現(xiàn)在,我們重新創(chuàng)建一個(gè)可以解決問(wèn)題的函數(shù)調(diào)用方式。我們想刪除字符串兩端的空白字符,這個(gè)方法位于 String.prototype ,所以我們使用 . 操作符來(lái)調(diào)用它(雖然,字符串是原始值(primitive),但是當(dāng)我們進(jìn)行方法調(diào)用時(shí),會(huì)在內(nèi)部被轉(zhuǎn)換成對(duì)象)。我們繼續(xù):

// let"s try to imagine how trim method is implemented
// on String.prototype
String.prototype.trim = function() {
  // string itself is contained inside `this`!
  const str = this;
  // this is a very naive implementation
  return str.replace(/(^s+)|(s+$)/g, "");
};

// let"s try to use `.call` method to invoke `trim`
" aa ".trim.call(thisArg);

// but `this` is our string itself!
// so, next two calls are equivalent:
" aa ".trim.call(" aa ");
String.prototype.trim.call(" aa ");

我們現(xiàn)在距離答案更近一步,但是仍然沒有解釋清楚最初那段代碼:

addressParts.map(Function.prototype.call, String.prototype.trim);

讓我們自己來(lái)實(shí)現(xiàn) Function.prototype.call

Function.prototype.call = function(thisArg, ...args) {
  // `this` in our case is actually our function!
  const fn = this;

  // also, pretty naive implementation
  return fn.bind(thisArg)(...args);
};

現(xiàn)在,我們可以來(lái)理一理所有的東西。當(dāng)我們?cè)?.map 里面聲明函數(shù)的時(shí)候,我們給 Function.prototype.call 綁定String.prototype.trim 作為 this 上下文,然后我們?cè)跀?shù)組中的每個(gè)元素上調(diào)用這個(gè)函數(shù),把每個(gè)字符串作為 thisArg 參數(shù)的值傳遞給 call。這意味著,String.prototype.trim 將使用字符串作為 this 上下文來(lái)調(diào)用。我們已經(jīng)知道這樣做是有效的,看看下面的例子:

String.prototype.trim.call(" aa "); // "aa"

問(wèn)題解決了!但是,我認(rèn)為這并不是一個(gè)好的做法,至于應(yīng)該如何使用一種好的方式來(lái)完成這件事, 很簡(jiǎn)單,只需傳遞一個(gè)匿名函數(shù)就能搞定:

addressParts.map(str => str.trim()); // same effect
也談?wù)?JavaScript 中的 call、apply 和 bind

作者在最后這一段可能講得有些簡(jiǎn)略,尤其是對(duì)于 bind 的用法,談?wù)勎业睦斫馑悸罚?/p>

// 我們從常用的 slice 說(shuō)起
// 相信很多人都寫過(guò)這樣的代碼
// 我們稱之為方法借用
Array.prototype.slice.call([1, 2, 3], 1) // [ 2, 3]

// 也會(huì)有人這樣寫
[].slice.call([1, 2, 3], 1) // [2, 3]

// 但上面的例子其實(shí)不是其真實(shí)的使用場(chǎng)景,因?yàn)?[1, 2, 3] 本身就是一個(gè) array,可以直接調(diào)用 slice
[1, 2, 3].slice(1) // [2, 3]

// 之前比較常見的場(chǎng)景是處理 argumnents,通過(guò)這種方式將這種類數(shù)組轉(zhuǎn)換成真正的數(shù)組
Array.prototype.slice.call(arguments)

// 回到最上面的例子,我們已經(jīng)知道使用 call 可以讓你在某個(gè)特定上下文(context)調(diào)用函數(shù)(fn)
// fn.call(context [, ...args])
// 而對(duì) call 來(lái)說(shuō),它的上下文就是 fn
// 所以 call 本身也是有上下文的,那我們?yōu)槭裁床豢梢灾苯咏o call 指定一個(gè)上下文,就像這樣:
Function.prototype.call.call(Array.prototype.slice, [1, 2, 3], 1) // [2, 3]

// 或者是這樣,apply 接受一個(gè)數(shù)組
Function.prototype.call.apply(Array.prototype.slice, [[1, 2, 3], 1]) // [2, 3]

// 當(dāng)然,也可以使用一下 bind,這樣會(huì)返回一個(gè)新的函數(shù)
// 我們直接將 slice 綁定到 call 的上下文
var slice = Function.prototype.call.bind(Array.prototype.slice)
slice([1, 2, 3], 1) // [2, 3]

// 我們來(lái)稍微改動(dòng)一下,跟上述 slice 的例子一致
var trim = Function.prototype.call.bind(String.prototype.trim)

// 上述 slice 等同于 Array.prototype.slice.call
// 所以這里的 trim,等同于 String.prototype.trim.call
// 那么
trim(" node") // "node"

// 現(xiàn)在,在 map 里使用 trim
addressParts.map(Function.prototype.call.bind(String.prototype.trim))

// 回到最初的那段代碼,這里面包含一個(gè)隱式的 bind 操作,與上面的代碼等效
// 問(wèn)題到這里就已經(jīng)解決
addressParts.map(Function.prototype.call, String.prototype.trim)


// 如作者所言,這樣的代碼確實(shí)不容易閱讀,不過(guò)對(duì)于我們理解 call、bind 以及 context 的概念仍是個(gè)很好的例子
// 我們還可以寫得更復(fù)雜
// 不用擔(dān)心,下面這段代碼什么新東西都沒有,不過(guò)是給 map 綁定到 call 而已
Function.prototype.call.bind(Array.prototype.map)(addressParts, Function.prototype.call, String.prototype.trim)

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

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

相關(guān)文章

  • ELSE 技術(shù)周刊(2017.11.13期)

    摘要:騰訊空間超分辨率技術(shù)為用戶節(jié)省流量,處理效果和速度超谷歌技術(shù)在的標(biāo)準(zhǔn)下,處理速度在提升了,處理效果也有明顯提升。此外,也是業(yè)界首次實(shí)現(xiàn)移動(dòng)端使用深度神經(jīng)網(wǎng)絡(luò)進(jìn)行超分辨率,并保證圖片能夠?qū)崟r(shí)進(jìn)行處理。值得一提的是的對(duì)應(yīng)指標(biāo)也在名單里。 團(tuán)隊(duì)分享 魔幻語(yǔ)言 JavaScript 系列之 call、bind 以及上下文 從一行代碼來(lái)看看 JavaScript 是一門多么魔幻的語(yǔ)言,順便談?wù)?...

    caohaoyu 評(píng)論0 收藏0
  • 魔幻語(yǔ)言 JavaScript 系列類型轉(zhuǎn)換、寬松相等以及原始值

    摘要:通過(guò)使用其構(gòu)造函數(shù),可以將一個(gè)值的類型轉(zhuǎn)換為另一種類型。如果使用兩次,可用于將該值轉(zhuǎn)換為相應(yīng)的布爾值。 編譯自:[1] + [2] – [3] === 9!? Looking into assembly code of coercion.全文從兩個(gè)題目來(lái)介紹類型轉(zhuǎn)換、寬松相等以及原始值的概念: [1] + [2] – [3] === 9 如果讓 a == true && a == fa...

    li21 評(píng)論0 收藏0
  • 魔幻語(yǔ)言 JavaScript 系列 a == true && a == fals

    摘要:稍后我們?cè)僭敿?xì)剖析,接下來(lái)先看一個(gè)問(wèn)題。還內(nèi)建了一些在之前沒有暴露給開發(fā)者的,它們代表了內(nèi)部語(yǔ)言行為。使用,可能有不少朋友一開始就想到這種方式,簡(jiǎn)單貼一下閱讀更多 在 JavaScript 環(huán)境下,可以讓表達(dá)式 a == true && a == false 為 true 嗎? 就像下面這樣,可以在控制臺(tái)打印出 ’yeah: // code here if (a == true && ...

    BDEEFE 評(píng)論0 收藏0
  • JavaScript深入系列15篇正式完結(jié)!

    摘要:寫在前面深入系列共計(jì)篇已經(jīng)正式完結(jié),這是一個(gè)旨在幫助大家,其實(shí)也是幫助自己捋順底層知識(shí)的系列。深入系列自月日發(fā)布第一篇文章,到月日發(fā)布最后一篇,感謝各位朋友的收藏點(diǎn)贊,鼓勵(lì)指正。 寫在前面 JavaScript 深入系列共計(jì) 15 篇已經(jīng)正式完結(jié),這是一個(gè)旨在幫助大家,其實(shí)也是幫助自己捋順 JavaScript 底層知識(shí)的系列。重點(diǎn)講解了如原型、作用域、執(zhí)行上下文、變量對(duì)象、this、...

    fxp 評(píng)論0 收藏0
  • JS系列call & apply & bind

    摘要:參考鏈接在中,和是對(duì)象自帶的三個(gè)方法,都是為了改變函數(shù)體內(nèi)部的指向。返回值是函數(shù)方法不會(huì)立即執(zhí)行,而是返回一個(gè)改變了上下文后的函數(shù)。而原函數(shù)中的并沒有被改變,依舊指向全局對(duì)象。原因是,在中,多次是無(wú)效的。 參考鏈接:https://juejin.im/post/59bfe8... 在JavaScript中,call、apply和bind是Function對(duì)象自帶的三個(gè)方法,都是為了改變...

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

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

0條評(píng)論

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