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

資訊專欄INFORMATION COLUMN

JavaScript函數(shù)的call,apply和bind

alighters / 2710人閱讀

摘要:它們有明確的和成員函數(shù)的定義,只有的實例才能調(diào)用這個的成員函數(shù)。用和調(diào)用函數(shù)里用和來指定函數(shù)調(diào)用的,即指針的指向。同樣,對于一個后的函數(shù)使用或者,也無法改變它的執(zhí)行,原理和上面是一樣的。

函數(shù)里的this指針

要理解call,apply和bind,那得先知道JavaScript里的this指針。JavaScript里任何函數(shù)的執(zhí)行都有一個上下文(context),也就是JavaScript里經(jīng)常說到的this指針,例如:

var obj = {
  a: 3,
  foo: function() {
    console.log(this.a);
  }
}

obj.foo();
// 執(zhí)行結(jié)果:打印出a的值3

這里foo的執(zhí)行context就是obj,即this指針的指向。當(dāng)然函數(shù)也可以直接定義,不加任何容器:

var bar = function() {
  console.log(this.b);
}

bar();  // 等價于global.bar()

如果我們調(diào)用bar(),那就相當(dāng)于調(diào)用global.bar(),也就是調(diào)用在了一個全局的object上。在不同的環(huán)境里這個全局object不一樣,在瀏覽器里是window,在node里則是global,這都是JavaScript執(zhí)行環(huán)境預(yù)設(shè)的。實際上我們定義的bar函數(shù)本身就是定義在了這個global上,即等價于:

global {
  bar: function() {
    console.log(this.b)
  }
}

所以直接執(zhí)行bar的時候,context當(dāng)然是global了。這和上面的obj和foo的例子是一樣的。

其實context的概念對于任何面向?qū)ο蟮恼Z言都是一樣的,比如Java和C++,它們的Class的成員函數(shù)在調(diào)用的時候第一個參數(shù)永遠(yuǎn)是一個隱式的this指針,然后才是真正的參數(shù)。那個this指針必須指向一個具體的這個Class的實例object。

然而Java和C++這樣的語言和JavaScript不一樣的地方就在于,它們對函數(shù)的調(diào)用更嚴(yán)格。它們有明確的Class和成員函數(shù)的定義,只有Class的實例object才能調(diào)用這個Class的成員函數(shù)。而JavaScript作為一門函數(shù)式編程語言則比較自由,函數(shù)的調(diào)用可以任意指定上下文context。例如上面的foo和bar,我們可以并不調(diào)用在obj和global上,而是自行指定,這就需要用到call和apply。

用call和apply調(diào)用函數(shù)

JavaScript里用call和apply來指定函數(shù)調(diào)用的context,即this指針的指向。call和apply都是定義在Function的prototype上,所以任何函數(shù)都繼承了它們,能直接使用,例如我們定義函數(shù)func:

function func(v1, v2) {
  console.log(this.a);

  console.log(v1);
  console.log(v2);
}

然后調(diào)用func:

var x = {
  a: 5
}

func.call(x,7, 8);

// 執(zhí)行結(jié)果:
// 打印出x.a的值5
// 打印出v1的值7
// 打印出v2的值8

這里我們用call來調(diào)用func函數(shù),call的第一個參數(shù)即是要綁定的上下文context,所以函數(shù)里的this.a就成了x.a,而x后面?zhèn)魅氲膮?shù)就是func函數(shù)本身的參數(shù)v1和v2。

apply的用法也是相似的,只不過apply把x后面的參數(shù)組織成一個數(shù)組:

func.apply(x,[7, 8]);

這里正因為我們用call和apply指定了func調(diào)用的context為x,因此它才可以打印出this.a的值,因為x里定義了a。如果直接調(diào)用func(),它就會調(diào)用在global上,此時a并沒有定義,則會打印出undefined。

bind的用法

有時候我們想指定函數(shù)調(diào)用的context,但并不想立即調(diào)用,而是作為callback傳給別人,這在JavaScript里司空見慣,因為到處都會有需要給監(jiān)聽事件綁定callbak函數(shù),這時候call和apply就不能用了,需要用到bind。

var funcBound = func.bind(x);

funcBound(7, 8);
// 等價于:
// func.call(x, 7, 8)

同樣是上面的func函數(shù),正如bind的字面意思,這次我們將x綁定到了func上,得到一個新的函數(shù)funcBound,此時funcBound的上下文就已經(jīng)被指定為了x,然后它再直接調(diào)用參數(shù)7和8,那么效果就相當(dāng)于之前的func.call(x, 7, 8)了。

bind也可以傳入多個參數(shù),此時第一個參數(shù)綁定為this,后面的參數(shù)則綁定為正常參數(shù),例如:

var funcBound = func.bind(x, 7);

funcBound(8);
// 等價于:
// func.call(x, 7, 8)

與bind作為比較,在JavaScript里為了控制函數(shù)的調(diào)用者,一種很方便的做法就是用閉包(Closure),因為閉包會保存任何外部的被使用到的變量的reference。不過閉包并沒有改變函數(shù)執(zhí)行的上下文,也就是說this指針并沒有改變,它能改變的只是函數(shù)體里某些具體的變量的指向,這實際上是一種強耦合,需要你在寫函數(shù)的時候本身就用到外部定義好的一些變量,這是一種不可擴展的函數(shù)定義方式。而如果是一個很通用的函數(shù),要想實現(xiàn)this指針的任意綁定,像call和apply那樣,則必須用bind。

bind在C++ 11里也有類似的用法,也是C++實現(xiàn)callback的一種主要方式,只不過就像之前說的,C++對類型的檢查更嚴(yán)格,綁定的不管是this還是其它參數(shù)都必須嚴(yán)格符合這個函數(shù)定義的形參的類型。而對于JavaScript,你可以隨意給任何函數(shù)綁定任何參數(shù),而且這樣的綁定經(jīng)常是隱式的,這也是造成JavaScript里this指針很容易混淆的原因,因為它的函數(shù)調(diào)用比較自由,而且往往很多時候底層調(diào)用的細(xì)節(jié)我們是不知道的。

實現(xiàn)一個簡單的bind

其實我們很容易用call和apply實現(xiàn)一個簡單的bind,來加深對bind的理解。這里假定你對JavaScript的Function,Array以及閉包有一定了解。

function myBind() {
  // bind的調(diào)用者,即原本的函數(shù)。
  var func = this;
  
  // 獲取bind的參數(shù)列表。
  var args = Array.prototype.slice.call(arguments);
  if (args.length === 0) {
    // 如果未傳入任何參數(shù),那就直接返回原本的函數(shù)。
    return func;
  }
  
  // 第一個參數(shù)是執(zhí)行上下文context。
  var context = args[0];
  // 后面的參數(shù)是正常的調(diào)用傳參,從第二個參數(shù)開始。
  var boundArgs = args.slice(1);

  // 定義bound后的函數(shù),這里用到了Closure。
  var boundFunc = function() {
    // 獲取實際調(diào)用時傳入的參數(shù),并且拼接在之前的boundArgs后面,成為真正的完整參數(shù)列表。
    var args = Array.prototype.slice.call(arguments);
    var allArgs = boundArgs.concat(args);
    
    // 這里用apply來調(diào)用原始的函數(shù)func,執(zhí)行上下文指向context,并傳入完整參數(shù)列表。
    return func.apply(context, allArgs);
  }

  // 返回bound函數(shù)。
  return boundFunc;
}

這樣就完成了一個簡單版本的bind,它已經(jīng)可以實現(xiàn)bind的基本功能,即綁定context和參數(shù)列表。具體的原理都在每一步的注釋里寫出來了,我就不做過多解釋了。如果把它加到Function的prototype里,那一個正常的函數(shù)例如foo就可以使用foo.myBind(...)來實現(xiàn)bind類似的功能。
-------

有一個有趣的問題,就是如果對一個函數(shù)進(jìn)行多次bind會發(fā)生什么事情?會不會context被不斷更新?答案是不會,只有第一次bind的context會作為最后調(diào)用的實際的this指針。關(guān)于這一點,只要對照上面的實現(xiàn)就能理解,不管bind多少次,最里層的apply永遠(yuǎn)只作用在第一次的傳入的context,也就是說原始函數(shù)func的調(diào)用對象只能是第一次的那個context。

同樣,對于一個bind后的函數(shù)使用call或者apply,也無法改變它的執(zhí)行context,原理和上面是一樣的。在實際編程中也不會有人這樣寫,但是初學(xué)者有時候可能會不小心犯下這樣的錯誤。實際上我就是初學(xué)者。。。也是在這里記錄一下我學(xué)JavaScript過程中的一些問題和想法。

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

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

相關(guān)文章

  • 理解 JavaScript call()/apply()/bind()

    摘要:理解文章中已經(jīng)比較全面的分析了在中的指向問題,用一句話來總結(jié)就是的指向一定是在執(zhí)行時決定的,指向被調(diào)用函數(shù)的對象。與和直接執(zhí)行原函數(shù)不同的是,返回的是一個新函數(shù)。這個新函數(shù)包裹了原函數(shù),并且綁定了的指向為傳入的。 理解 JavaScript this 文章中已經(jīng)比較全面的分析了 this 在 JavaScript 中的指向問題,用一句話來總結(jié)就是:this 的指向一定是在執(zhí)行時決定的,...

    duan199226 評論0 收藏0
  • 理解JavaScriptcall,applybind方法

    摘要:輸出的作用與和一樣,都是可以改變函數(shù)運行時上下文,區(qū)別是和在調(diào)用函數(shù)之后會立即執(zhí)行,而方法調(diào)用并改變函數(shù)運行時上下文后,返回一個新的函數(shù),供我們需要時再調(diào)用。 前言 js中的call(), apply()和bind()是Function.prototype下的方法,都是用于改變函數(shù)運行時上下文,最終的返回值是你調(diào)用的方法的返回值,若該方法沒有返回值,則返回undefined。這幾個方法...

    chaosx110 評論0 收藏0
  • JS中call、applybind方法詳解

    摘要:不能應(yīng)用下的等方法。首先我們可以通過給目標(biāo)函數(shù)指定作用域來簡單實現(xiàn)方法保存,即調(diào)用方法的目標(biāo)函數(shù)考慮到函數(shù)柯里化的情況,我們可以構(gòu)建一個更加健壯的這次的方法可以綁定對象,也支持在綁定的時候傳參。原因是,在中,多次是無效的。 bind 是返回對應(yīng)函數(shù),便于稍后調(diào)用;apply 、call 則是立即調(diào)用 。 apply、call 在 javascript 中,call 和 apply 都是...

    zombieda 評論0 收藏0
  • JS基礎(chǔ)篇--call、applybind方法詳解

    摘要:首先我們可以通過給目標(biāo)函數(shù)指定作用域來簡單實現(xiàn)方法保存,即調(diào)用方法的目標(biāo)函數(shù)考慮到函數(shù)柯里化的情況,我們可以構(gòu)建一個更加健壯的這次的方法可以綁定對象,也支持在綁定的時候傳參。原因是,在中,多次是無效的。而則會立即執(zhí)行函數(shù)。 bind 是返回對應(yīng)函數(shù),便于稍后調(diào)用;apply 、call 則是立即調(diào)用 。 apply、call 在 javascript 中,call 和 apply 都是...

    lastSeries 評論0 收藏0
  • call,apply and bind in JavaScript

    摘要:文章盡量使用大量實例進(jìn)行講解,它們的使用場景。在嚴(yán)格模式下,函數(shù)被調(diào)用后,里面的默認(rèn)是后面通過調(diào)用函數(shù)上的和方法,該變指向,函數(shù)里面的指向。利用,可以傳入外層的上下文。同樣適用的還有,里面的對象,它也是一種類數(shù)組對象。 call,apply and bind in JavaScript 在ECMAScript中,每個函數(shù)都包含兩個繼承而來的方法:apply() 和 call(),這兩個...

    JohnLui 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<