国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

JavaScript函數的call,apply和bind

alighters / 2693人閱讀

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

函數里的this指針

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

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

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

這里foo的執行context就是obj,即this指針的指向。當然函數也可以直接定義,不加任何容器:

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

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

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

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

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

其實context的概念對于任何面向對象的語言都是一樣的,比如Java和C++,它們的Class的成員函數在調用的時候第一個參數永遠是一個隱式的this指針,然后才是真正的參數。那個this指針必須指向一個具體的這個Class的實例object。

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

用call和apply調用函數

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

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

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

然后調用func:

var x = {
  a: 5
}

func.call(x,7, 8);

// 執行結果:
// 打印出x.a的值5
// 打印出v1的值7
// 打印出v2的值8

這里我們用call來調用func函數,call的第一個參數即是要綁定的上下文context,所以函數里的this.a就成了x.a,而x后面傳入的參數就是func函數本身的參數v1和v2。

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

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

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

bind的用法

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

var funcBound = func.bind(x);

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

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

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

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

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

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

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

實現一個簡單的bind

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

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

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

  // 返回bound函數。
  return boundFunc;
}

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

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

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

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/92882.html

相關文章

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

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

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

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

    chaosx110 評論0 收藏0
  • JS中callapplybind方法詳解

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

    zombieda 評論0 收藏0
  • JS基礎篇--callapplybind方法詳解

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

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

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

    JohnLui 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<