小菊花课堂之JavaScript关于this

关于this

上一章我们讲了关于作用域和闭包的相关知识,现在开始新一轮的学习,那就是JavaScript中最复杂的机制之一---this关键字。它是一个很特别的关键字,被自动定义在所有函数的作用域中。另外我们需要明确的一点就是,this在任何情况下都不指向函数的词法作用域。

那么我们来看看this到底是怎么样的机制。this是在运行时进行绑定的,它的上下文取决于函数调用时的各种条件。另外,this的绑定和函数声明的位置没有任何关系,之取决于函数的调用方式。

this的四条绑定规则

默认绑定

默认绑定就是最常用的独立函数调用时所绑定的。思考一下代码

function foo() {
    console.log(this.a);
}
var a = 2;
foo(); // 2

我们可以看到,当调用foo()时,this.a被解析成了全局变量a。
但是,如果使用严格模式,那么就不能将全局对象用于默认绑定,报错TypeError:this is undefined

隐式绑定

一个对那个内部包含一个指向函数的属性,并通过这个属性间接引用函数,从而把this隐式绑定到这个对象上。所以这一条就需要考虑调用位置是否有上下文对象。

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

这段代码中,当foo()被调用时,前面加上了对obj的引用。当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。另外,对象属性引用链中只有上一层或者说最后一层在调用位置中起作用。也就是说,this永远都是指向最近调用的位置。

显式绑定

前面说了隐式绑定的概念,那么我们如果不想在对象内部包含函数引用,而想在某个对象上强制调用函数,该如何做呢?
JavaScript中提供了两个方法,分别是call()apply(),那么我们应该怎么在实际中运用呢?
首先我们要搞清楚call()apply()的作用,这里引用MDN上的解释:

call() 方法调用一个函数, 其具有一个指定的this值和分别地提供的参数(参数的列表)。
apply() 方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数。
具体如何实现,这里不作展开说明,可以自行研究,加深理解。

可以看出,它们俩的差异仅在于所需参数的形式不同,ok回归正题。
请看下面代码:

function foo() {
    console.log(a);
}
var obj = {
    a: 2
}
foo.call(obj); // 2

这样,我们就可以在调用foo的时候强制把它的this绑定到obj上

new绑定

在传统的面向类语言中,使用new初始化类时会调用类中的构造函数。但是,JavaScript中的构造函数只是使用new操作符时被调用的函数。它们并不属于某个类,也不会实例化一个类。
JavaScript中使用new调用函数时,会自动执行下面操作

  1. 构造一个新对象
  2. 新对象会被执行[[prototype]]连接
  3. 新对象会绑定函数调用的this
  4. 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象

判断this

那么我们在判断函数在某个调用位置应该应用哪条规则呢,优先级如何判断呢?有下面四条法宝供参考

  1. 函数是否在new中调用?如果是,则this绑定的是新对象
    var bar = new foo()
  2. 函数是否通过call、apply调用?如果是,则this绑定的是指定对象
    var bar = foo.call(obj)
  3. 函数是否在某个上下文对象中调用?如果是,则this绑定的是上下文对象
    var bar = obj.foo()
  4. 如果都不是的话,使用默认绑定。注意,在严格模式下,就绑定到undefined,否则就绑定到全局对象
    var bar = foo()

当然,也许会有例外的情况发生,暂时先留着,大家一起思考一下,看看会在什么情况下出现这种例外情况。

那么,今天就先到这里啦
see u ~ again

相关推荐