this的秘密,密密不是秘密

200 阅读2分钟

前言

this因指向灵活多变、使用场景多样,一直是面试中的热点。

谁调用它,this就指向谁

this的指向是在调用的时候确定的,也就是说找this的指向等于找谁调用它。
可这并不全面,它有以下几条规律:

  • 在函数体系中,调用函数时,在严格模式下指向undefind,非严格模式指向window。(箭头函数除外)
  • 在箭头函数中,this指向由外层作用域来决定。
  • 一般用new方法构建函数时候,构建函数内的this会被绑定到新创建的对象上。
  • 通过call/apply/bind 方法显示调用函数时,this会被绑定到指定的对象上。
  • 一般通过上下文对象调用函数时,this会绑定到该对象上。

全局环境下的this

    function fn1() {
      console.log(this)
    }
    function fn2() {
      'use strict'
      console.log(this)
    }
    fn1(); // Window
    fn2(); // undefined

这种状况相对简单。

上下文对象中的this

面对以下代码,我们将不会疑惑,最终true

    const student = {
      name: 'xxx',
      fn: function() {
        return this;
      }
    }
    console.log(student.fn() === student) // true

当存在更多调用关系时,this会指向最后调用它的对象。

    const dog = {
      name: '二哈',
      fn: function() {
        return this.name;
      },
      child1: {
        name: '小虎',
        fn: function() {
          return this.name
        }
      },
      child2: {
        name: '小黄',
        fn: function() {
          const fn = this.name
          return fn
        }
      },
      child3: {
        name: '小3',
        fn: function() {
          const fn = dog.fn
          return fn()
        }
      },
      child4: {
        name: '小4',
        fn: function() {
          const fn = dog.child1.fn()
          return fn
        }
      }
    }
    console.log(dog.fn()); // 二哈
    console.log(dog.child1.fn()); // 小虎
    console.log(dog.child2.fn()); // 小黄
    console.log(dog.child3.fn()); // undefined
    console.log(dog.child4.fn()); // 小虎

在上面代码中,child3、child34提前做了赋值操作,将函数fn提前挂载到别的对象上。

通过bind、call、apply改变this指向

他们都是用来改变this指向的,但是call和apply是直接进行相关函数的调用,bind不会执行相关函数,而是返回一个新的函数,这个新的函数已经自动绑定this。说具体点call和apply之间区别在于形参上。

    const object1 ={
      name: 'xxx',
    }
    function fn() {
      console.log(this.name)
    }
    function fn1(p1, p2) {
        console.log(p1, p2)
    }
    fn.call(object1); // xxx
    fn1.call(object1, 1, 2) // 12
    fn.apply(object1); // xxx
    fn1.apply(object1, [1,2]) // 12
    fn.bind(object1)(); // xxx
    fn1.bind(object1, 1, 2)() // 12
```构造函数和this
关于构造函数,我们先看下代码:

```js
    function person() {
      this.name = '小明'
    }
    const p1 = new person();
    console.log(p1.name); // 小明

这可以简单看出构建函数this指向实列。但是如果在函数中使用了return,就要注意了:

    function person() {
      this.name = '小明';
      return {}
    }
    function person1() {
      this.name = '小红';
      return 0
    }
    const p1 = new person();
    const p2 = new person1();
    console.log(p1.name); // undefined
    console.log(p2.name); // 小红

结论:
构造函数中返回的是一个对象(复杂类型),那么this就指向这个对象;如果返回的是一个数字(基本类型),那么this仍然指向实列。

箭头函数中的this

先了解下箭头函数中的this:
箭头函数的this由其所属函数或全局作用域所决定。 如下巧用this:

    const object1 = {
      fn: function () {
        setTimeout(function() {
          console.log(this) // Window
        }, 1000);
      }
    }
    const object2 = {
      fn: function () {
        setTimeout(() => {
          console.log(this) // {fn: ƒ}
        }, 1000);
      }
    }
    object1.fn();
    object2.fn();

总结

谁跳用它,this就指向谁。