手写JS中的call、apply、bind方法

320 阅读2分钟

相关知识点

  • 实现 call 方法
  • 实现 apply 方法
  • 实现 bind 方法

实现 call 方法

  • 首先 context 为可选参数,如果不传的话默认上下文为 window
  • 接下来给 context 创建一个 fn 属性,并将值设置为需要调用的函数;
  • 因为 call 可以传入多个参数作为调用函数的参数,所以需要将参数剥离出来;
  • 然后调用函数并将对象上的函数删除;
Function.prototype.myCall = function (context) {
  // this 为调用的函数
  // context 是参数对象
  // 判断调用者是否为函数
  if (typeof this !== "function") {
    throw new Error("Error");
  }
  // 不传参默认为 window
  context = context || window;
  // 新增 fn 属性, 将值设置为需要调用的函数
  context.fn = this;
  // 将 arguments 转化为数组将 call 的传参提取出来
  const args = Array.from(arguments).slice(1);
  // 或者使用 ...
  // const args = [...arguments].slice(1);
  // 传参调用函数
  context.fn(...args);
  // 删除函数 fn
  delete context.fn;
};

// 普通函数
function print(age) {
  console.log(this);
  console.log(this.name, age);
}

// 自定义对象
var obj = {
  name: "jack"
};

// 调用 myCall 方法
print.myCall(obj, 1, 2, 3);
/* 
  输出结果为:
    {name: "jack", fn: ƒ}
    jack 1
*/

print.myCall(obj, [1, 2, 3]);
/* 
  输出结果为:
    {name: "jack", fn: ƒ}
    jack [1, 2, 3]
*/

实现 apply 方法

Function.prototype.myApply = function (context) {
  // 判断调用者是否为函数
  if (typeof this !== "function") {
    throw new TypeError("Error");
  }
  // 不传参默认为 window
  context = context || window;
  // 新增 fn 属性, 将值设置为需要调用的函数
  context.fn = this;
  // 判断是否有参数传入
  if (arguments[1]) {
    // 判断参数是否为数组
    if (Object.prototype.toString.call(arguments[1]) === "[object Array]") {
      context.fn(arguments[1]);
    } else {
      throw new TypeError("参数只能为数组");
    }
  } else {
    context.fn();
  }
  // 删除函数
  delete context.fn;
};

// 普通函数
function print(age) {
  console.log(this.name, age);
}

// 自定义对象
var obj = {
  name: "jack"
};

// 调用函数的 apply 方法
print.myApply(obj, [10, 20, 30]); // jack [10, 20, 30]
print.myApply(obj); // jack undefined

实现 bind 方法

Function.prototype.myBind = function (context) {
  if (typeof this !== "function") {
    throw new TypeError("Error");
  }
  const _this = this;
  context = context || window;
  const args = [...arguments].slice(1);
  return function () {
    // console.log(this); // Window
    // console.log(_this); // print 方法
    return _this.apply(context, args.concat(...arguments));
  };
};

function print(args) {
  console.log(this.name, args);
}

var obj = {
  name: "Tom"
};

let fn = print.myBind(obj, [1, 3, 5]);
let fn2 = print.myBind(obj, 2, 4, 6);
fn(); // Tom  [1, 3, 5]
fn2(); // Tom  2