一文详解JavaScript中this:从绑定规则到箭头函数

0 阅读3分钟

对于许多JavaScript的初学者来说,this是一个让人又爱又恨的关键词,它的指向灵活多变,稍有不慎就会出现意想不到的结果。但是掌握this的绑定规则却是写出优雅,可复用的代码的必经之路。所以,本文将带你系统梳理this的来龙去脉,彻底搞懂它的工作机制。

一.为什么要有this

this 是 JS 中的一个关键字,它提供了一种更优雅的方式隐式的传递一个对象的引用,可以让代码更简洁易于复用。

// 优雅的 this
const alice = {
  name: 'Alice',
  age: 25,
  introduce() {
    console.log(this.name);
  }
};
alice.introduce(); // this隐式传递了 alice 对象

二. this 可以出现在哪里?

1.全局

在浏览器中,全局中 this 指向 window 对象。

console.log( this === window);// true

2.函数体内

函数内部的 this 指向取决于如何调用,而不是在哪里定义,这也是 this 最复杂也是最灵活的地方。

三.this 的绑定规则

1.默认绑定

当函数独立调用时,函数中的 this 指向 window 对象

function foo(){
  console.log(this);
}

foo(); // window

2.隐式绑定

当一个函数被一个上下文对象所拥有,并被该对象调用,函数中的 this 指向该对象。

const obj = {
  name: 'obj',
  say() {
    console.log(this.name);
  }
};
obj.say(); // 'obj'

当一个函数被多层对象调用,函数中的 this 指向最近的那个对象

function foo() {
  console.log(this.a);
}
var obj = {
  a: 1,
  foo: foo,
};

var oo = {
  a: 2,
  foo: obj,
};

oo.foo.foo(); // 1  离 foo 最近的对象是 obj 所以 this 指向 obj 对象

3.显示绑定

JavaScript中提供了三种方法 call, applybind,可以强制指定函数中的this

  • call :立即调用函数,参数逐个传递。
    fn.call(obj, x, y)

  • apply:立即调用函数,参数以数组方式传递。
    fn.apply(obj, [x, y]

  • bind:返回一个新的函数。
    fn.bind(obj, x, y)()

4.new 绑定

当一个函数被用作构造函数时, this 会指向新创建的那个实例对象。new的过程如下:

  1. 创建一个全新的空对象。

  2. 将这个空对象的 __proto__ 指向构造函数的 prototype

  3. 将构造函数内的 this 绑定到该新对象;

  4. 执行构造函数代码;

  5. 如果构造函数没有返回对象,则返回这个新对象。

function Person(name) {
  this.name = name;
  this.say = function() {
    console.log(this.name);
  };
}
const alice = new Person('Alice');
alice.say(); // 'Alice',this 指向 alice 实例

四.箭头函数中的 this

箭头函数没有 this 这个概念,所以写在箭头函数中的 this ,是它外层非箭头函数的。

function foo() {
  var fn = () => {
    this.a = 2;
  };
  fn();
}

var obj = {
  a: 1,
  bar: foo,
};
obj.bar(); // this 指向的是 obj 对象,所以相当于最后执行obj.a = 2,obj 对象中 a 的值变为 2
console.log(obj);  //obj { a: 2, bar: foo}

注意:如果箭头函数外面也没有普通函数,那么 this 指向全局

const fn = () => {
  console.log(this); // window
};
fn();

五.一表总结

绑定类型规则简述示例场景
默认绑定独立调用函数,this 指向全局fn()
隐式绑定通过对象调用,this 指向该对象(最近的那个)obj.fn()
显式绑定使用 call/apply/bind 强制指定 thisfn.call(obj)
new 绑定作为构造函数调用,this 指向新实例new Foo()
箭头函数没有自己的 this,继承外层非箭头函数的 this() => console.log(this)