【面试爱问】- 关于 this 指向的总结

83 阅读2分钟

全局作用域:

1. 在浏览器全局作用域下

  1. 在浏览器的全局作用域中进行 var 声明: this 的指向是统一的,都是 window (self, frames, globalThis)
var a = 'aa';

var b = function () { return 'function b'; }

console.log(a); // 'aa'
console.log(b()); // 'function b'

console.log(this.a); // 'aa'
console.log(this.b()); // 'function b'

console.log(window.a); // 'aa'
console.log(window.b()); // 'function b'

2.在对象作用域下, window 和 this 的指向是不同的

var a = 'global a';

var obj = {
  a: 'obj a',
  foo: function () {
    console.log(this.a);
    console.log(window.a, self.a, frames.a);
  }
};

obj.foo();
  1. 在严格模式下 ('use strict'; )和 ESModule 中,函数中的 this 指向为 undefined
  2. DOM 绑定的事件处理函数中,this 指向绑定的元素

2. 在 node 环境的作用域下

  • var 声明的变量, 不会加挂到 global (或者 globalThis )中
  • 在 Node.js 模块中,函数中的 this 指向为 module.exports

类 / 构造函数下

  1. 类与构造函数是相似的,this 指向那个被类 / 构造函数实例化出来的对象
function Person1(a, b, c) {
  this.a = a;
  this.b = b;
  this.c = c;
}
Person1.prototype.d = function () {}
Person1.prototype.e = [
  'welcome',
  'to'
];

class Person2 {
  constructor(a, b, c) {
    this.a = a;
    this.b = b;
    this.c = c;
  }
  
  d() {
    // ...
  }
}
Person2.prototype.e = ['', ''];
  1. extends 继承: this 指向实例化的类示例对象
class Father {
  constructor(fName) {
    this.fName = fName;
  }
}

class Son extends Father {
  constructor(sName) {
    super('Micheal');

    this.sName = sName;
  }
}

const s = new Son('Jackie');

补充 - super 的本质

  1. 调用了父类上的 constructor
  2. 生成了 this 绑定 -> Father this -> Son 实例能够访问
  3. this -> new Father() -> {/**/}

注意

super 调用之前, 不可以访问 class 的 this (为了指向同一个对象引用)

强制改变 this 指向

举个例子:

  • 使用 bind 改变 this 指向
function foo(b, c) {
  console.log(this.a, b, c);
}

var obj1 = {
  a: 1
};
var obj2 = {
  a: 5
};

var foo1 = foo.bind(obj1, 2, 3);
foo1(); // 1 2 3

var foo2 = foo1.bind(obj2, 2, 3);
foo2(); // 1 2 3 // bind只会生效一次 (foo.bind(obj1, 2, 3).bind(obj2, 2, 3))

关于箭头函数中的 this

const foo1 = () => {
  console.log(this, this.a);
}

var a = 2;

var obj1 = {
  a: 1,
};

foo1(); // window ('use strict'也是window)

foo1.call(obj1); // 2 
foo1.apply(obj1); // 2
var foo2 = foo1.bind(obj1);
foo2(); // 2

  • 箭头函数是忽略任何形式的 this 指向的改变 (无法使用 call, apply, bind 进行修改)
  • 箭头函数中看到的 this 表现出静态性
  • 箭头函数一定不是一个构造函数 (不能被实例化), 所以箭头函数本身是不存在 this
  • 箭头函数中的 this 不是谁绑定就指向谁
  • 箭头函数的 this 指向上下文 (外层作用域的 this 指向), 但是外层函数不能是箭头函数

规律总结

  1. this 指向的基本原则, 谁调用 this 的宿主, this 就指向谁
  2. 对象方法中的 this 指向通常是寻找最近的引用
var obj2 = {
  a: 1,
  b: 2,
  c: function () {
    // 独立的函数
    function d1 () {
      console.log(this);
    }
    obj2.d2 = function () {
      console.log(this);
    }
    this.d3 = function () {
      console.log(this);
    }
    d1();
  }
};

obj2.c();
obj2.d2();
obj2.d3();
class Father {
  constructor() {
    this.eat = this.eat.bind(this);
  }
  
  get fruit() {
    return 'orange';
  }
  
  eat() {
    console.log('eating fruits : ', this.fruit);
  }
}

class Son {
  get fruit() {
    return 'apple';
  }
}

let f = new Father();
let s = new Son();

s.eat = f.eat;

s.eat();