手写Call、Apply、Bind、New函数

41 阅读1分钟
Function.prototype.MyCall = function (context) {
  // 判断调用对象
  if (typeof this !== "function") {
    throw new TypeError("this is not a function");
  }
  // 首先获取参数
  console.log(arguments, "arguments1");
  const args = [...arguments].slice(1);
  // 判断 context 是否传入,如果没有传就设置为 window
  context = context || window;
  // 将被调用的方法设置为 context 的属性
  // this 即为我们要调用的方法
  const fnSymbol = Symbol()
  context[fnSymbol] = this;
  // 执行要被调用的方法
  const result = context[fnSymbol](...args);
  // 删除手动增加的属性方法
  delete context[fnSymbol];
  // 将执行结果返回
  return result;
};

Function.prototype.MyApply = function (context) {
  if (typeof this !== "function") {
    throw new TypeError("this is not a function");
  }
  let result = null;
  const fnSymbol = Symbol();
  context[fnSymbol] = this;
  if (arguments[1]) {
    result = context[fnSymbol](...arguments[1]);
  } else {
    result = context[fnSymbol]();
  }
  delete context[fnSymbol]
  return result;
};

const obj = {
  value: "123",
};

function bar(name, age) {
  console.log(name, age);
  console.log(this.value, "value");
}

bar.MyCall(obj, '小明', 24);
bar.MyApply(obj, ['小明', 24]);
Function.prototype.MyBind = function (context) {
  if (typeof this !== "function") {
    throw new TypeError("this is not a function");
  }

  console.log(arguments, "MyBind");
  const fn = this;
  const args = [...arguments].slice(1);
  // 创建一个空对象
  const fNOP = function () {};
  const fBound = function () {
    console.log(arguments, "fBound");
    return fn.apply(this instanceof fNOP ? this : context, [
      ...args,
      ...arguments,
    ]);
  };
  // 空对象的原型指向绑定函数的原型
  fNOP.prototype = this.prototype;
  // 空对象的实例赋值给 fBound.prototype
  fBound.prototype = new fNOP();
  return fBound;
};
let value = 2;
let foo = {
  value: 1,
};
function bar(name, age) {
  this.habit = "shopping";
  console.log(this.value);
  console.log(name);
  console.log(age);
}
bar.prototype.friend = "kevin";

let bindFoo = bar.MyBind(foo, "xaioming"); // 返回一个函数
bindFoo(10);

let bindFoo1 = bar.MyBind(foo, "Jack");
let obj1 = new bindFoo1(20);

console.log(obj1.habit, "obj1.habit");

console.log(obj1.friend, "obj1.friend");
1.  首先创建一个新的空对象。
2.  根据原型链,设置空对象的 `__proto__` 为构造函数的 `prototype` 。
3.  构造函数的 this 指向这个对象,执行构造函数的代码(为这个新对象添加属性)。
4.  判断函数的返回值类型,如果是引用类型,就返回这个引用类型的对象,否则返回这个对象。

function MyNew(context) {
  const obj = {};
  obj.__proto__ = context.prototype;
  const res = context.apply(obj, [...arguments].slice(1));
  return typeof res === "object" ? res : obj;
}

function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}

const car1 = MyNew(Car, "Eagle", "Talon TSi", 1993);

console.log(car1.make);
console.log(car1.model);
console.log(car1.year);