模擬實現bind

以前說了模擬實現 call 和 apply,下面就來實現 bind,首先先看一下定義 bind 定義app

bind()方法建立一個新的函數,在 bind()被調用時,這個新函數的 this 被 bind 的第一個參數指定,其他的參數將做爲新函數的參數供調用時使用。函數

初版

根據定義咱們嘗試寫一下post

Function.prototype.binds = function(con) {
  var fn = this;
  var args = Array.prototype.slice.call(arguments, 1);
  return function() {
    return fn.apply(con, args);
  };
};
複製代碼

咱們來測試一下上面的函數測試

var obj = {
  value: "zhangsan"
};
function foo(name, age) {
  console.log(this.value); //zhangsan
  console.log(name); // name
  console.log(age); // undefined
}
var f = foo.binds(obj, "name");
f(19);
複製代碼

age 輸出的跟咱們預期的不太一致,這是由於咱們沒有處理返回函數的參數,用 concat 把參數合併在一塊兒就能夠了。優化

Function.prototype.binds = function(con) {
  var fn = this;
  var args = Array.prototype.slice.call(arguments, 1);
  return function() {
    return fn.apply(con, args.concat(Array.prototype.slice.call(arguments)));
  };
};
複製代碼

第二版

上面簡單實現了函數的 bind,不過咱們來看一下 bind 與構造函數相遇會怎麼樣ui

var obj = {
  value: "zhangsan"
};
function Foo(name, age) {
  console.log(this.value); //undefined
  console.log(name);
  console.log(age);
}
var f = Foo.bind(obj, "name");
new f(19);
複製代碼

上面 this.value 的輸出爲 undefined,這是由於 調用綁定函數時做爲 this 參數傳遞給目標函數的值。 若是使用 new 運算符構造綁定函數,則忽略該值this

另外做爲構造函數是能夠獲取到綁定 bind 函數的 prototypespa

function Foo() {
  console.log(this.gender); //male
}
Foo.prototype.gender = "male";
var f = Foo.bind(null, "name");
new f();
複製代碼

下面就是來實現上面兩點:prototype

  • 怎麼來判斷是否經過 new 來調用呢? 能夠經過instanceof
function Foo() {
  if (!(this instanceof Foo)) {
    throw Error("請經過new調用");
  }
}
Foo();
複製代碼
  • 繼承的話咱們能夠經過寄生組合式繼承,這類文章比較多,這裏就簡單舉一個例子
function create(con) {
  var f = function() {};
  f.prototype = con;
  return new f();
}
function Par(name) {
  this.name = name;
}
Par.prototype.getName = function() {
  return this.name;
};
function Chi(name, age) {
  Par.call(this, name);
  this.age = age;
}
Chi.prototype = create(Par.prototype);
Chi.prototype.constructor = Chi;
Chi.prototype.getDetails = function() {
  var n = this.getName();
  console.log(n, this.age);
};
var d = new Chi("zhangsan", 16);
d.getDetails();
複製代碼

OK,上面兩點解決後咱們再來實現一版code

Function.prototype.binds = function(con) {
  var fn = this;
  var args = Array.prototype.slice.call(arguments, 1);
  var f = function() {};
  if (this.prototype) {
    f.prototype = this.prototype;
  }
  var result = function() {
    return fn.apply(
      this instanceof result ? this : con,
      args.concat(Array.prototype.slice.call(arguments))
    );
  };
  result.prototype = new f();
  return result;
};
複製代碼

優化

觀察上面的不難發現 bind 調用的必須是函數,而經過繼承咱們可能會調用到 bind 因此這裏顯示報錯一下

var obj = Object.create(Function.prototype);
console.log(obj.bind);
複製代碼

另外對於咱們上面定義的binds確定不太優雅,污染了命名空間,這裏能夠判斷一下若是存在 bind 就用 bind 不然在定義

最終

if (!Function.prototype.bind) {
  Function.prototype.bind = function(con) {
    if (typeof this !== "function") {
      throw new TypeError(
        "Function.prototype.bind - what is trying to be bound is not callable"
      );
    }
    var fn = this;
    var args = Array.prototype.slice.call(arguments, 1);
    var f = function() {};
    if (this.prototype) {
      f.prototype = this.prototype;
    }
    var result = function() {
      return fn.apply(
        this instanceof result ? this : con,
        args.concat(Array.prototype.slice.call(arguments))
      );
    };
    result.prototype = new f();
    return result;
  };
}
複製代碼
相關文章
相關標籤/搜索