JavaScript專題之模擬實現bind

本文共 1100 字,讀完只需 4 分鐘javascript

概述

前一篇文章咱們嘗試模擬實現了 call 和 apply 方法,其實 bind 函數也能夠用來改變 this 的指向。bind 和 call和 apply 二者的區別在於,bind 會返回一個被改變了 this 指向的函數。java

本文介紹如何模擬實現 bind 函數:數組

首先觀察 bind 函數有什麼特色:閉包

var person = {
    name: 'jayChou'
}

function say(age, sex) {
    console.log(this.name, age, sex);
}

var foo = say.bind(person, '男', 39);

foo();  // jayChou 男 39
複製代碼
  1. 返回一個函數
  2. 函數參數以逗號的形式傳入
  3. 改變了 this 的指向

1、call 簡單實現

既然 bind 內部也要用改變 this 指向,咱們能夠用現成的 call 函數來實現這一功能。app

Function.prototype.newBind = function(context) {
    if(typeof this !== 'function') {
        throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }
    var self = this;
    return function () {
        return self.call(context)
      }
}
複製代碼

說明一下:
if 判斷是爲了校驗,只能讓函數來調用此方法,並拋出錯誤。 第二個 return 是爲了讓返回的函數有返回值的功能。函數

測試一下:post

var person = {
    name: 'jayChou'
};

var say = function() {
    console.log(this.name);
}

var foo = say.newBind(person);
foo();  // jayChou
複製代碼

成功啦。測試

2、傳遞參數

剛纔的函數是沒有傳遞參數,固然不行,因此咱們想辦法把函數的參數也傳遞進去。ui

bind 函數有個特色,就是在綁定的時候能夠傳參,返回的函數還能夠繼續傳參。this

var person = {
    name: 'jayChou'
};

var say = function(p1, p2) {
    console.log(this.name, p1, p2);
}

var foo = say.bind(person, 18);
foo(20);  // jayChou 18 20
複製代碼

還能夠寫成:

say.bind(person, 18)(20); // jayChou 18 20
複製代碼

好,進入正題,考慮傳參的事。在前面的文章,咱們講過 arguments 類數組對象的一些特性,不能直接調用數組的方法,可是能夠用原型方法間接來調用,咱們採用 apply 的方式來傳遞參數。

Function.prototype.newBind = function(context) {
    if(typeof this !== 'function') {
        throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }
    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);  // 間接調用數組方法,獲取第一次傳的參數
    
    return function () {
        var innerArgs = Array.prototype.slice.call(arguments);
        return self.apply(context, args.concat(innerArgs))
      }
}


var person = {
    name: 'jayChou'
};

var say = function(p1, p2) {
    console.log(this.name, p1, p2);
}

var foo = say.newBind(person, 18);  // 第一次傳參
foo(20);  // 第二次傳參
複製代碼

最後輸出:

jayChou 18 20

結果正確,以上就完成了 bind 函數基本功能的實現。

可是!

3、做爲構造函數時?

bind 函數其實還有一個很是重要的特色:

bind返回的函數若是做爲構造函數,搭配new關鍵字出現的話,咱們的綁定this就須要「被忽略」。

意思就是指:當使用 nuw 關鍵字把 bind 返回的函數做爲構造函數,以前改變了指向的 this 就失效了。返回的函數的 this 就關聯到了構造函數的實例對象上。

針對這個特色,須要再作一些修改:

Function.prototype.newBind = function(context) {
    if(typeof this !== 'function') {
        throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }
    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);  // 間接調用數組方法,獲取第一次傳的參數
    
    let tempFn = function {};  // 利用一個空函數做爲中轉
    
    tempFn.prototype = this.prototype;  // 修改返回函數的 prototype 爲綁定函數的 prototype,實例就能夠繼承綁定函數的原型中的值
    
    var resultFn = function () {
        var innerArgs = Array.prototype.slice.call(arguments);
        if (this instanceof tempFn) {  // 若是 返回函數被當作構造函數後,生成的對象是 tempFn 的實例,此時應該將 this 的指向指向建立的實例。
            return self.apply(this, args.concat(innerArgs));
        } else {
            return self.apply(context, args.concat(innerArgs))
        }
      }
      
    resultFn = new tempFn();
    return resultFn;
}
複製代碼

總結

本文嘗試模擬實現了 bind 函數,bind 函數與 call,apply 函數的區別在於,bind 函數返回一個指定了 this 的函數,函數並未執行。其次,當返回的函數做爲構造函數時,以前綁定的 this 會失效。

歡迎關注個人我的公衆號「謝南波」,專一分享原創文章。

掘金專欄 JavaScript 系列文章

  1. JavaScript之變量及做用域
  2. JavaScript之聲明提高
  3. JavaScript之執行上下文
  4. JavaScript之變量對象
  5. JavaScript之原型與原型鏈
  6. JavaScript之做用域鏈
  7. JavaScript之閉包
  8. JavaScript之this
  9. JavaScript之arguments
  10. JavaScript之按值傳遞
  11. JavaScript之例題中完全理解this
  12. JavaScript專題之模擬實現call和apply
  13. JavaScript專題之模擬實現bind
相關文章
相關標籤/搜索