JavaScript中的this关键词是一个比较容易混乱的概念,在不同的场景下,this会指向不同的对象,在主流的面向对象的语言中(如Java, C#等),this含义是明确且具体的,即指向当前对象,一般在编译期就绑定了。而JavaScript中的this在运行期进行的绑定,这是JavaScript中this关键词具备多重含义的本质原因。
人们对JavaScript中this的绑定常常有两个误解,一是指向函数本身,二是指向函数作用域,这两种想法都是错误的。this并不指向函数本身,也不指向函数作用域
由于JavaScript中this在运行期进行绑定的特性,因此this可以指向全局对象,指向新创建的对象,指向dot之前的对象,这个完全取决于函数的调用方式。JavaScript函数的调用有以下几种方式:普通函数调用,构造函数调用,对象方法调用,和使用call和apply调用。
普通函数调用(指向全局对象)
在JavaScript中,最常见的函数调用类型就是普通函数调用,也就是“光秃秃”的调用this默认指向的是全局作用域。这条作为默认绑定规则
1 | function up8Wang() { |
默认绑定里的严格模式
在普通函数调用方式之前加上’use strict’, 则this不能绑定到全局对象。
1 | 'use strict' |
可以看出,在严格模式下,普通函数调用中的this实际上指向的是undefined
new 绑定(指向新创建的对象)
通过new操作符调用构造函数时发生的this绑定,关于构造函数,请移步阅读 JavaScript构造函数(constructor) 一文。在这篇文章中有段代码,我们拿过来继续分析分析
1 | function Person(name, age) { |
上面定义的Person函数,既可以作普通函数调用,又可以作构造函数调用,
普通函数: 当作普通函数调用时this绑定在了全局对象window上,此时全局对象上增加了name和age属性, 还增加了getName方法(因此可以直接调用geName函数),Person用作普通函数时,没有return语句,因此person2的值为undefined。
构造函数: person1对象是用new操作符调用构造函数Person创建的新对象,并且会把构造函数内的this绑定到这个对象上。
对象方法调用的隐式绑定
如果函数有所谓的“落脚点”,即有上下文对象时,隐式绑定规则会把函数中的this绑定到这个上下文对象。通俗点讲函数作为对象的一个属性
1 | var person1 = { |
上面的代码利用对象字面量创建的对象person1和person2对象就是getName函数的落脚点, 专业一点的说法就是上下文对象。
隐式绑定中的上下文丢失1
2
3
4
5
6
7
8
9
10
11
12var name = 'up8--global'
var person = {
name: 'Felix Cao',
getName: getName
}
var personName = person.getName // 设置一个简写 s1
personName(); // up8--global s2
function getName() {
console.log('this: ', this);
console.log('this.name: ', this.name);
}
输出的结果是up8–global, 在s1的位置,我们只是给person.getName换了个personName名字。就是这个换名赋值语句操作出事的,因为在javascript中,函数是对象,对象之间是引用传递而不是传值传递, 因此s1位置的赋值实际上是这样的: personName = person.getName = getName, personName = getName, personName最终引用的是getName函数的地址,而与person对象无关了,这就是所谓的丢失上下文。
显示绑定
JavaScript为每个函数提供三种显示绑定的方法: apply, call, bind, 而bind则返回一个新函数。关于三者的具体用法,请移步 JavaScript函数的call/apply/bind方法。
箭头函数中的this绑定
箭头函数是ES6里一个重要特性,其this绑定规则跟以上四条都不一样,ES6的this遵循着JS的词法作用域, 而不是在运行期进行绑定, 本篇也不做详细介绍,回头拉出来一篇做专门介绍。