this
This是JavaScript函数的一个关键字。在全局上下文中(任意函数体外部),this指代全局对象,而在函数体内部,this指的是调用函数的那个对象。大多数情况下,函数的调用方式决定了this的值。下面,本文将对几种调用情境下this的用法做一个大致的介绍。
直接调用
在非严格模式下直接调用,this的值会被默认为全局对象,如下:
var x = 0
function f() {
this.x = 1
}
f()
console.log(x)//打印结果为 1
以上这段代码中,this的值指代全局对象,函数运行时全局变量x被重新赋值为1,覆盖了第一次的赋值,最后打印结果为1。
在严格模式下,this将保持它进入执行环境时的值,如果上下文中没有定义,则它将会默认为undefined,如下:
var x = 0
function f() {
"use strict";
this.x = 1
}
f()
console.log(x)//执行时会报错:"Cannot set property 'x' of undefined"
同样的代码,加上"use strict"后,因为this的值是undefined,故在给this.x赋值时会发生错误。
对象方法中的this调用
当函数作为某个对象的方法调用时,this指代调用该函数的对象。
var o = {
x: 1,
f: function () {
return this.x
}
}
console.log(o.f())//打印结果为1
以上代码中,执行console.log(o.f())时,函数f被对象o调用,此时的this指代对象o。以上代码也可以写为如下的形式:
var o = {}
o.x = 1
o.f = function () {
return this.x
}
console.log(o.f())//同样打印1
也即,用何种方式在何处定义对象的调用函数并不会影响到this的值,确定this的值仅需考虑对象o与函数f的调用关系。
此外需注意,当函数的调用存在多层嵌套时,this的值仅受最靠近的调用成员的影响。
var o = {
x: 1,
b: {
x: 2,
f: function () {
return this.x
}
}
}
console.log(o.b.f())//打印结果为2
上述代码中的函数执行时,this指代离函数f最近的调用对象b。
另外,对于原型链中的this。如果一个方法是存在于一个对象的原型链中的,那么调用该方法时,this指的是调用这个方法的对象,如下:
var o = { }
o.f = function () {
return this.a
}
var p = Object.create(o)
p.a = 1
o.a = 2
console.log(o.f())//打印结果为2,this指代o
console.log(p.f())//打印结果为1,this指代p
构造函数中的调用
当我们通过构造函数生成一个新对象时,this将与这个新对象绑定。
function f() {
this.a = 1
}
var o = new f()
console.log(o.a)//打印结果为1
需要注意的是,当构造器的默认返回指是this引用的对象时,可以手动设置其他的返回对象。
function f() {
this.a = 1
return {b : 2}
}
var o = new f()
console.log(o.a)//打印结果为undefined
console.log(o.b)//打印结果为2
在调用函数的过程中,设置了返回对象,使得与this绑定的对象被取消。当返回值不是一个对象时,则返回this。
call和apply中的this
call()方法和apply()方法是函数从Function对象原型中继承的方法,这两个方法的作用一样,均是指定函数的调用对象,区别在于call()方法接受参数列表,而apply()方法接受一个包含多个参数的数组(或类数组对象)。当函数通过call()或apply()调用时,传递的第一个参数就是this,这个this也就是正在调用函数的那个对象。
a=3
b=4
function f() {
sum =this.a +this.b
console.log(sum)
}
var o = { a: 1, b: 2 }
f.call(o)//打印结果为3
f.apply(o)//打印结果为3
f.call()//打印结果为7
f.apply()//打印结果为7
当参数为空时,默认调用全局对象,故执行f.cal()与f.apply()的输出结果为7。
由此,可以获得一个启发,所有函数都可以转换为call形式来调用(或apply,下文仅讨论call),也即:
fun(parameters)//等价于
fun.call(undefined,parameters)
//---------------------------------
obj.child.method(parameters)//等价于
obj.child.method.call(obj.child,parameters)
统一表示为:
fun.call(context, parameters)
此处的context就是this。当需要判断函数的this为何值时,可以先将函数转换为call形式,对应的第一个参数context即为this。
需要注意的是,使用apply和call函数时,如果传递的this不是一个对象,那么JavaScript会尝试用内部的ToObject操作将其转换为一个对象。
function f() {
console.log(Object.prototype.toString.call(this));
}
f.call('this')//打印结果为[object String]
函数执行时,this被转换为了一个String对象。
bind方法
bind()方法是EcmaScript5中引入的一个方法,它的作用是创建一个新的函数(被称为绑定函数)。新函数与被绑定的目标函数拥有相同的函数体,this被绑定到了bind()函数的第一个参数上,无论函数被如何调用都具有同样的this值。
var a = 0
var b = { a: 2 }
function f1() {
return this.a
}
var f2 = f1.bind(b)
var o = { a: 1, f1: f1, f2: f2 }
console.log(o.f1())//打印结果为1,this为o
console.log(o.f2())//打印结果为2,this被绑定为b
var c = o.f1
console.log(c())//打印结果为0,this为全局变量
var d = o.f2
console.log(d())//打印结果为2,this依然为b
小结
以上,即为JavaScript中几种不同调用情境下this的不同应用。总而言之,当需要判断this的值是什么的时候,可以统一将函数转换为call或者apply的形式。而在DOM Event Handler中,jQuery Event Handler中,可以通过看源码中的函数是怎么被call或apply的,或者通过看文档,来确定this的值。如果实在无法确定,还有console.log大法,直接打印出来,this的值也就不再神秘了。