this 指向问题解惑

686 阅读3分钟

前言

参考学习以下文章

再来40道this面试题酸爽继续(1.2w字用手整理)

首先this的绑定方式有如下几种

  • 默认绑定(非严格模式下this指向全局对象, 严格模式下this会绑定到undefined)

  • 隐式绑定(当函数引用有上下文对象时, 如 obj.foo()的调用方式, foo内的this指向obj)

  • 显示绑定(通过call()或者apply()方法直接指定this的绑定对象, 如foo.call(obj))

  • new绑定

  • 箭头函数绑定(this的指向由外层作用域决定的)

函数中的this指向

立即执行函数,this执行window

1、普通函数

this 永远指向最后调用它的那个对象

var name = 'window'
function Person (name) {
  this.name = name
  this.obj = {
    name: 'obj',
    foo1: function () {
      console.log(this.name);
      return function () {
        console.log(this.name)
      }
    },
  
  }
}
var person1 = new Person('person1')
var person2 = {name: 'person2'}

person1.obj.foo1() // 最后一个调用的对象时obj,输出obj
person1.obj.foo1()()  // obj 、 window ,第一个()函数执行输出obj,return是一个函数,第二个()是立即执行函数,this执行window ,
person1.obj.foo1.call(person2)()
person1.obj.foo1().call(person2)

输出

image.png

2、箭头函数

它里面的this是由外层作用域来决定的,且指向函数定义时的this而非执行时

var name = 'window'
function Person (name) {
  this.name = name
  this.obj = {
    name: 'obj',
    foo2: function () {
    console.log(this.name);
      return () => {
        console.log(this.name)
      }
    }
  }
}
var person1 = new Person('person1')
var person2 = new Person('person2')

person1.obj.foo2()() // 第一个()执行输出obj,最后调用的是obj, 第二个()调用执行箭头函数,其上一层作用域是foo2函数的,this指向obj
person1.obj.foo2.call(person2)()
person1.obj.foo2().call(person2)

输出

image.png

解惑

1、 use strict 严格模式下的this指向

不是全部指向undefined,全局的this还是指向window ,在函数内的this 会指向undefined

"use strict";
var a = 10;
function foo () {
  console.log('this1', this) // this1 undefined
  console.log(window.a) // 10
  console.log(this.a) // this 是undefined 所以报错
}
console.log(window.foo) // 打印函数
console.log('this2', this) // this 指向window
foo();

输出结果

image.png

2、 var 和let 和const 声明的变量的差异

let 和const 声明的变量 不会绑定到window 对象上,因此 此时this指向window ,而 window上没有该变量,自然就是undefined

let a = 1
const b = 2

function foo () {
  console.log(this.a)
  console.log(this.b)
}
foo();
console.log(window.a)

执行结果

image.png

3、 隐式绑定的隐式丢失问题

下面两种情况会发生丢失

  • 1、 使用另一个变量来给函数取别名
function foo () {
  console.log(this.a)
};
var obj = { a: 1, foo };
var a = 2;
var foo2 = obj.foo;

obj.foo(); // 1
foo2();  // 2

obj.foo() this指向最后调用这个函数的对象,所以自然指向obj 输出 1

foo2() 隐式丢失了指向, 最后调用的是window,所以指向window 输出 2

  • 2、 将函数作为参数传递时会被隐式赋值,回调函数丢失this绑定
function foo () {
  console.log(this.a)
}
function doFoo (fn) {
  console.log(this)
  fn()
}
var obj = { a: 1, foo }
obj2 = { a: 3, doFoo}
var a = 2
doFoo(obj.foo)
obj2.doFoo(obj.foo)

输出

image.png

  • 1 、doFoo(ob.foo()执行时,doFoo() this指向window,
  • 2、obj.foo() doFoo 里面的参数fn()隐式丢失 ,this指向window

  • 3 、 obj2.doFoo(obj.foo) 执行时 , doFoo()的this 指向obj2,
  • 4、里面的参数fn()隐式丢失 ,this指向window,所以隐式丢失和外层的this指向没有关系,丢失后fn()是被window调用的,this指向window。

4、计时器包函数也有隐式丢失问题

var obj1 = {
  a: 1
}
var obj2 = {
  a: 2,
  foo1: function () {
    console.log(this.a)
  },
  foo2: function () {
    setTimeout(function () {
      console.log(this) // window
      console.log(this.a) // 3
    }, 0)
  }
}
var a = 3

obj2.foo1() // 2
obj2.foo2()