1. 程式人生 > >JavaScript函式實現--call、apply、bind

JavaScript函式實現--call、apply、bind

1、call函式實現

Function.prototype.newCall = function(context, ...args) {
  if (typeof context === 'object') {
    context = context || window;
  } else {
    context = Object.create(null);
  }

  const fn = Symbol();
  context[fn] = this;
  const res = context[fn](...args);
  delete context[fn];
  return res;
};

2、apply 函式實現

Function.prototype.newApply = function(context, parameter) {
  if (typeof context === 'object') {
    context = context || window
  } else {
    context = Object.create(null)
  }
  let fn = Symbol()
  context[fn] = this
  context[fn](parameter);
  delete context[fn]
}

3、bind 函式實現

(1)簡單版本,不支援 new 運算子操作

Function.prototype.bind = function(context, ...args) {
  // this 代表要繫結的函式
  const self = this;
  return function () {
    return self.call(context, ...args);
  }
};

(2)支援new操作

/**
 * 我們建立的每個函式都有一個 prototype 屬性,這個屬性是一個指標,指向一個物件,
 * 而這個物件的用途是包含可以由特定型別的所有例項共享的屬性和方法。
 *
 * 無論什麼時候,只要建立了一個新函式,就會根據一組特定的規則為該函式建立一個 prototype 屬性
 * 這個屬性指向函式的原型物件,在預設情況下,所有的原型物件都會自動活兒一個 constructor 屬性,這個屬性包含一個指向 prototype 屬性所在的函式的指標
 */


/**
 * bind() 方法會建立一個新函式。當這個新函式被呼叫時,bind() 的第一個引數將作為它執行時的 this,之後的一系列引數將會在傳遞的實參前傳入作為他的引數
 * 注意點:
 *  (1) 返回一個函式
 *  (2) 引數傳遞
 *  (3) 作為建構函式:
 *      一個繫結函式也能使用 new 操作符建立物件:這種行為就像把原函式當成構造器。提供的 this 被忽略,同時呼叫時的引數被提供給模擬函式
 */


Function.prototype.bind = function (context) {
  if (typeof this !== 'function') {
    throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
  }

  const self = this;
  const args = Array.prototype.slice.call(arguments, 1);
  const fNOP = function () {};

  const fBound = function () {
    self.apply(this instanceof self ? this : context, args.concat(Array.prototype.slice.call(arguments)));
  };

  fNOP.prototype = this.prototype;
  fBound.prototype = new fNOP();

  return fBound;
};