JavaScript跟别的语言大相径庭,JavaScript的this总是指向一个对象,而具体指向哪个对象是在运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境。
除去不常用的with和eval的情况,具体到实际应用中,this的指向大致可以分为以下4种:
-
作为对象的方法调用。
-
作为普通函数调用。
-
构造器调用。
-
Function.prototype.call 或 Function.prototype.apply调用。
- 作为对象的方法调用
当函数作为对象的方法被调用时,this指向该对象,代码如下:
var obj = {
a: 1,
getA: function() {
console.log(this === obj) // true
console.log(this.a) // 1
}
}
obj.getA()
2. 作为普通函数调用
当函数不作为对象的属性被调用时,也就是我们常说的普通函数方式,此时的this总是指向全局对象。在浏览器的JavaScript里,这里的全局对象时window对象。代码如下:
window.name = 'globalName'
var getName = function() {
return this.name
}
console.log(getName())// globalName
window.name = 'globalName'
var myObj = {
name: 'summer',
getName: function() {
return this.name
}
}
var getName = myObj.getName;
console.log(getName())// globalName
* 有一个局部的callback方法,callback被作为普通函数调用时,callback内部的this指向了window。代码如下:
<html>
<body>
<div id="div1">
我是一个div
</div>
</body>
<script>
window.id = 'window'
document.getElementById('div1').onClick = function() {
console.log(this.id) // div1
var callback = function() {
console.log(this.id) // window
}
callback();
}
// 用一个变量保存div节点的引用
document.getElementById('div1').onClick = function() {
console.log(this.id) // div1
var that = this;
var callback = function() {
console.log(this.id) // div1
}
callback();
}
</script>
</html>
在ECMAScript5的strict模式下,这种情况下的this被规定为不会指向全局对象,而是undefined。代码如下:
function func() {
'use strict';
console.log(this) // undefined
}
func()
3. 构造器调用
JavaScript中没有类,但是可以从构造器中创建对象,同时也提供了new操作符,使得构造器看起来更像一个类。
当用new运算符调用函数时,该函数总会返回一个对象,通常情况下,构造器里的this就会指向返回的这个对象。代码如下:
var MyClass = function() {
this.name = 'summer'
}
var obj = new MyClass()
console.log(obj.name)// summer
使用new调用构造器时,还要注意一个问题,如果构造器显式地返回了一个object类型的对象,那么此次运算结果最终会返回这个对象。代码如下:
var MyClass = function() {
this.name = 'summer'
return {
name: '夏天'
}
}
var obj = new MyClass()
console.log(obj.name) // 夏天
如果new调用构造器时,不显式地返回任何数据,或者是返回一个非对象类型的数据,就不会引起上述问题。代码如下:
// 不显式地返回任何数据
var MyClass = function() {
this.name = 'summer'
}
var obj = new MyClass()
console.log(obj.name) // summer
// 返回一个非对象类型的数据
var MyClass = function() {
this.name = 'summer'
return '夏天'
}
var obj = new MyClass()
console.log(obj.name) // summer
4. Function.prototype.call 或 Function.prototype.apply调用
跟普通函数调用相比,调用Function.prototype.call 或 Function.prototype.apply可以动态地改变this的指向。代码如下:
var obj1 = {
name: 'summer',
getName: function() {
return this.name
}
}
var obj2 = {
name: '夏天'
}
console.log(obj1.getName())// summer
console.log(obj1.getName.call(obj2))// 夏天
console.log(obj1.getName.apply(obj2))// 夏天
call和apply方法能很好地体现JavaScript的函数式特性,在JavaScript中,几乎每一次编写函数式风格的代码,都离不开call和apply。
参考书籍:《JavaScript设计模式与开发实践》