this绑定规则的学习以及call、apply、bind函数的手写

39 阅读2分钟

一、结论

  • this指向什么,跟函数所处的位置没有关系,跟函数被调用的方式有关系
// 同一个函数的三种不同调用方式
function foo() {
  console.log(this);
}

// 1. 直接调用
foo(); //window

// 2. 通过对象调用
const obj = {
  foo: foo,
};
obj.foo(); //obj

// 3. 通过call调用
foo.call({ name: "123" }); //{ name: "123" }对象

二、this的绑定规则

  1. 默认绑定:独立函数调用
function foo() {
  console.log(this);
}

foo(); //window

const obj = {
  name: "obj",
  foo: foo,
};

const bar = obj.foo;
bar(); //window
  1. 隐式绑定:通过某个对象进行调用的
// 隐式绑定
//1. 案例一
function foo() {
  console.log(this);
}

const obj = {
  name: "foo",
  foo: foo,
};

obj.foo(); //obj

// 案例二
const obj1 = {
  foo: function () {
    console.log(this);
  },
};

const obj2 = {
  name: "obj2",
  foo: obj1.foo,
};

obj2.foo(); //obj2
  1. 显式绑定:通过call、apply、bind调用
function foo(num1, num2) {
  console.log(this, num1, num2);
}

const obj = {
  name: "obj",
};

// call、apply、bind可以指定this的绑定对象,
// call和apply之间的区别在于传递参数的形式不同
// bind返回一个新的函数
foo.call(obj, 10, 20); //obj
foo.apply(obj, [10, 20]); //obj
const bar = foo.bind(obj, 10, 20);
bar();  //obj
  1. new绑定:js中的构造函数可以通过new方式来调用
/**
 * 使用new关键字来调用函数,会执行如下的操作
 * 1. 创建一个新对象
 * 2. 新对象的__proto__属性指向构造函数的prototype
 * 3. this指向这个新对象,指向构造函数中的代码
 * 4. 如果函数没有返回其他对象,返回这个新对象
 */

function Person(name, age) {
  this.name = name;
  this.age = age;
  console.log(this);
}

const p1 = new Person("name1", 18); //p1
const p2 = new Person("name2", 19); //p2

三、this绑定规则的优先级

new绑定>显式绑定>隐式绑定>默认绑定

  1. 默认绑定优先级最低
  2. 显式绑定高于隐式绑定
// 显式绑定高于隐式绑定
const obj = {
  name: "obj",
  foo: function () {
    console.log(this);
  },
};

obj.foo.call("abc"); //String{'abc}
obj.foo.apply("abc"); //String{'abc}
  1. new绑定高于隐式绑定
const obj = {
  name: "obj",
  foo: function () {
    console.log(this);
  },
};

const instance = new obj.foo(); //instance
  1. new绑定高于显式绑定
// new关键字不能和apply/apply一起使用
function foo() {
  console.log(this);
}

const bar = foo.bind("aaa");
const obj = new bar();  //foo{}

四、箭头函数

  1. 箭头函数不绑定this,this指向上层作用域
  2. 箭头函数不能通过new调用

手写call、apply、bind函数

//call函数的实现
Function.prototype.myCall = function (thisArg, ...args) {
  let fn = this;
  if (thisArg === undefined || thisArg === null) {
    thisArg = window;
  } else {
    thisArg = Object(thisArg);
  }
  thisArg.fn = fn;
  const result = thisArg.fn(...args);
  delete thisArg.fn;
  return result;
};

//apply函数的实现
Function.prototype.myApply = function (thisArg, args) {
  let fn = this;
  if (thisArg === undefined || thisArg === null) {
    thisArg = window;
  } else {
    thisArg = Object(thisArg);
  }
  thisArg.fn = fn;
  if (!args) {
    args = [];
  }
  const result = thisArg.fn(...args);
  delete thisArg.fn;
  return result;
};

//bind函数的实现
Function.prototype.myBind = function (thisArg, ...args1) {
  let fn = this;
  if (thisArg === undefined || thisArg === null) {
    thisArg = window;
  } else {
    thisArg = Object(thisArg);
  }
  thisArg.fn = fn;

  return function (...args2) {
    const result = thisArg.fn(...args1, ...args2);
    delete thisArg.fn;
    return result;
  };
};