this

95 阅读2分钟

引言:this的指向,这是一个面试经典问题,也是 ES5中众多坑中的一个

  • this 是执行上下文中的一个属性,它指向最后一次调用这个方法的对象。在实际开发中,this 的指向可以通过四种调用模式来判断。

this和函数类型的关系

  • 普通函数
  • 箭头函数
  • bind,call和apply重新指定后的函数

this的指向与是否开启严格模式的关系

  • 非严格模式:执行环境中没有设置this,则指向window
  • 严格模式:执行环境中没有设置this,则保持undefined

this与环境的关系(非严格模式下,没有谁调用)

  • Node: globalThis
  • 浏览器: window;

接下来就让我们结合例子开始正式学习this的指向吧 (· _ ·)

一、普通函数

1、使用 new 关键字创建的函数,this会指向被创建出来的实例

构造函数中的this会指向new出来的实例对象

// 浏览器环境
function F(name, age) {
  this.name = name;
  this.age = age;
  console.log(this); // {name: 'tom', age: 18}
};
let f = new F('tom', 18);

2、没有使用 new 关键字的函数,取决于调用方式(谁调用指向谁)

  • Object.func() 对象调用的,指向这个对象
  • func() 非严格模式下,直接调用,指向window
  • 异步调用 非严格模式下,指向window
// 浏览器环境,非严格模式
// 定义一个对象
let obj = {
  // obj对象内的name
  name: 'tom',
  speak() {
    console.log(this.name);
  },
  verdict() {
    console.log(this === window);
  },
};
// window上的name
var name = 'jack';
// 将函数赋值给a
let a = obj.speak;
// 输出obj的name
obj.speak(); // tom
// 输出window上的name
a(); // jack
// 输出window上的name
setTimeout(() => {
  console.log(this.name); // jack
}, 100);
let b = obj.verdict;
// this指向obj
obj.verdict(); // false
// this指向window
b(); // true
setTimeout(() => {
  console.log(this === window); // true
}, 100);

二、箭头函数

  • 箭头函数没有自己的this,是继承而来的
  • 箭头函数的this指向它外层包裹它的最近的非箭头函数的this
  • 箭头函数使用bind、apply和call重新绑定this的时候,会自动忽略掉第一个参数,即重新绑定的this对箭头函数是没有影响的
// 浏览器环境
// 在全局作用域中自调用的箭头函数,this指向window
(() => console.log(this === window))(); // true
let obj = {
  foo() {
    console.log(this === obj); // true
    // 箭头函数的this指向它外层包裹它的最近的非箭头函数的this
    // 因为外层最近的非箭头函数是 foo  而foo的this指向obj
    return () => {
      console.log(this === obj); // true
    };
  },
};
obj.foo()();

三、bind,call和apply重新指定后的函数,

  • 若第一个参数为对象,指向第一个参数
  • 若第一个参数为空,就和普通函数一样
// 浏览器环境
let obj = {
  name: 'tom',
  foo() {
    console.log(this.name);
  },
};
let obj2 = {
  name: 'jack',
};
let name = 'bob';
// 谁调用的指向谁
obj.foo(); // tom
// 使用call、apply和bind后,this指向指向第一个参数
obj.foo.call(obj2); // jack
obj.foo.apply(obj2); // jack
obj.foo.bind(obj2)(); // jack
obj.foo.call(); // tom
obj.foo.apply(); // tom
obj.foo.bind()(); // tom