【JS基础】面试基本功之this指向

106 阅读3分钟

前言

在学习this指向前,需要了解两个概念:词法作用域和动态作用域。词法作用域顾名思义,就是定义在代码书写阶段的作用域,也就是说我们编写代码时,声明的变量所处的作用域就已经确定下来了。词法作用域关注函数的声明位置,而动态作用域则是在程序运行时动态确定的作用域。它并不在意声明,而是看在哪里被调用,也就是说我们需要分析调用栈来确定作用域。js的中的作用域是词法作用域,但是this的机制跟动态作用域很类似。

绑定规则

1. 默认规则

默认绑定规则是最基础的规则,也就是将this指向全局对象。(如果代码运行在严格模式中,则不能绑定到全局对象上而是会绑定到undefined)

var name = 'Jack'

function getName() {
   console.log(this.name) // 'Jack'
}

getName()

2. 隐式绑定

隐式绑定规则需要关注函数被调用时是否存在上下文对象。当函数调用时存在上下文对象,函数内的this会绑定到这个上下文对象。观察下面代码,思考代码运行后三处的打印结果:

var name = 'Jack'

function getName() {
   console.log(this.name)
}

var obj = {
    name: 'Tom',
    getName: getName
}
// 1.
obj.getName()

var obj2 = {
    name: 'Mike',
    obj: obj,
    getName: obj.getName
}
// 2.
obj2.getName()
// 3.
obj2.obj.getName()
//4.
var fn = obj.getName
fn()

先看第1处调用,obj对象的属性getName拥有对在全局作用域中声明的函数getName的引用。因为函数调用时存在obj这个上下文对象,所以输出结果为‘Tom’。

第2处调用,obj2对象的属性getName拥有对obj.getName属性值的引用,也就是在全局作用域中声明的函数getName的引用。因为函数调用时存在obj2这个上下文对象,所以输出结果为‘Mike’。

第3处是一个对象属性引用链,只有最后一个调用位置起决定作用,所以我们直接看obj.getName(),因为函数调用时存在obj这个上下文对象,所以输出结果为‘Tom’。

第4处,是一个容易出错的场景,注意到我们分析几点都在强调“obj对象的属性getName拥有对在全局作用域中声明的函数getName的引用”,所以这里将obj.getName赋值给全局变量fn,其实是fn拥有了对全局声明的函数getName的引用。执行fn()时不存在调用的上下文对象,this应用的是默认规则,指向全局对象或undefined。

3. 显示绑定

不同于隐式绑定需要分析调用的上下文对象,显示绑定是指通过一定方式明确指定我们想要this绑定的上下文对象。最常见的call,apply,bind强制this绑定到指定的上下文中,也有一些api支持传入上下文,比如forEach的第二个接收参数 array.forEach(function(currentValue, index, arr), thisValue) 这里就call举个例子:

var num = 2
function getNum () {
    console.log(this.num)
}
var obj = {
    num: 1
}

getNum.call(obj) // 1

4. new绑定

首先来看下new的过程都做了哪些事:

  • 创建一个全新的对象
  • 新对象被执行Prototype连接
  • 函数中的this被绑定到新对象上
  • 如果函数没有返回对象,那么new表达式中的函数会自动返回这个对象 思考下面代码的输出:
function person (name) {
    this.name = name
}
var tom = new person('Tom')
console.log(tom.name) // Tom

这里使用new来调用person函数,new会创建一个对象并将person中的this绑定到这个新对象上。

总结

有时我们遇到的this问题比较复杂,下面按照规则的优先级从高到低总结了this的判断方法:

  1. 判断是否为new绑定
  2. 是否存在显示绑定(call,apply等)
  3. 是否存在引用绑定,即上下文对象调用
  4. 都不是则采用默认绑定

注意,我们上面讲述的场景不适用于箭头函数(()=>{}),箭头函数会继承外层最接近它的第一个非箭头函数的函数的this绑定。