手写 call、apply 及 bind 函数

220 阅读2分钟

手写 call

Function.prototype.myCall=function(context){
  if(typeof this !== 'function'){
    throw new TypeError('not function');
  }
  // 首先 context 为可选参数,如果不传的话默认上下文为 window
  context =context || window
  // 接下来给 context 创建一个 fn 属性,并将值设置为需要调用的函数
  context.fn=this;
  // 因为 call 可以传入多个参数作为调用函数的参数,所以需要将参数剥离出来
  let args=[...arguments].slice(1);
  // 然后调用函数并将对象上的函数删除
  let fullName=context.fn(...args);//核心
  delete context.fn;
  return fullName; 
}
var person = {
  fullName: function(city, country) {
    return this.firstName + " " + this.lastName + "," + city + "," + country;
  }
}
var person1 = {
  firstName:"Bill",
  lastName: "Gates"
}
person.fullName.myCall(person1, "Seattle", "USA");

//"Bill Gates,Seattle,USA"

apply 的实现也类似,区别在于对参数的处理

Function.prototype.myApply=function(context){
  if(typeof this !== 'function'){
    throw new TypeError('not function');
  }
  // 首先 context 为可选参数,如果不传的话默认上下文为 window
  context =context || window
  // 接下来给 context 创建一个 fn 属性,并将值设置为需要调用的函数
  context.fn=this;
  // 因为 apply 可以传入参数数组
  let fullName;
  if(Array.isArray(arguments[1]) && arguments[1].length!==0){ 
  //fullName=context.fn(arguments[1]); 输出:Bill Gates,Seattle,USA,undefined"
    fullName=context.fn(...arguments[1]);
  }else{
    fullName=context.fn();
  }
  delete context.fn;
  return fullName; 
}
var person = {
  fullName: function(city, country) {
    return this.firstName + " " + this.lastName + "," + city + "," + country;
  }
}
var person1 = {
  firstName:"Bill",
  lastName: "Gates"
}
person.fullName.myApply(person1, ["Seattle", "USA"]); 

bind使用:

let myWrite=document.write
myWrite()
VM1026:2 Uncaught TypeError: Illegal invocation
    at <anonymous>:2:1
(anonymous) @ VM1026:2
myWrite.bind(document)
ƒ write() { [native code] }


Function.prototype.myBind = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  const _this = this
  const args = [...arguments].slice(1)
  // 返回一个函数
  return function F() {
    //此处为闭包,注意this:_this = this 
    // 因为返回了一个函数,我们可以 new F(),所以需要判断
    if (this instanceof F) {
      //对于 new 的情况来说,不会被任何方式改变 this,所以对于这种情况我们需要忽略传入的 this:不用传context
      return new _this(...args, ...arguments)
    }
    //bind 可以实现类似这样的代码 f.bind(obj, 1)(2),所以需要参数拼接
    return _this.apply(context, args.concat(...arguments))
  }
}
this.num = 9; 
var module = {
  num: 81,
  getNum: function() { return this.num; }
};

module.getNum(); // 81

var getNum = module.getNum;
getNum(); // 9, 因为在这个例子中,"this"指向全局对象

// 创建一个'this'绑定到module的函数
var boundGetNum = getNum.myBind(module);
boundGetNum(); // 81
81
getNum();
9


bind使用:

function f(y, z){
    return this.x + y + z;//x:1 y:2 z:3
}
var m = f.bind({x : 1}, 2);
console.log(m(3));

new

新生成了一个对象
链接到原型
绑定 this
返回新对象

function create() {
  let obj = {}//创建空对象
  let Con = [].shift.call(arguments)//获取构造函数
  obj.__proto__ = Con.prototype//链接原型链
  let result = Con.apply(obj, arguments)//绑定 this 并执行构造函数
  return result instanceof Object ? result : obj
}