this关键字是JavaScript中一个复杂的机制,它被自动定义在所有的函数作用域中。
1. 为什么要用this
function identify() {
return this.name.toUpperCase()
}
function speak() {
var greeting = 'hello, i'am' + identify.call(this)
console.log(greeting)
}
var me = {
name: 'Kyle'
}
var you = {
name: 'Reader'
}
identify.call(me); //KYLE
identify.call(you) //READER
speak.call(me) //Hello, 我是KYLE
speak.call(you) //Hello, 我是READER
上面这部分代码在不同的上下文对象中重复使用identify()和speak(),不用针对每个对象编写不同版本的函数。
如果不使用this,那就需要给identify()和speak()显示传入一个上下文对象。
function identify(context) {
return context.name.toUpperCase();
}
function speak(context) {
var greeting = 'Hello i'am' + identify(context)
console.log(greeting)
}
identify(you) //READER
speak(me) //hello, 我是KYLE
然而,this提供了一种更优雅的方式来隐式传递一个对象引用,因此可以将API设计的更加简洁且易于复用。
当你的代码越来越复杂的时候,显示的传递上下文对象会变得很混乱。
2. 关于this的误解之
误解之this是指向自身
通常会将this理解成指向函数自身。平常我们会在函数内部调用自身(例如递归)。在JavaScript中函数也是一个对象,那么我们可以在调用函数的时候存储状态(属性的值)。
我们看下以下代码,会发现this并没有指向函数本身:
//记录foo的调用次数
function foo(num) {
console.log("foo: "+ num)
this.count ++
}
foo.count = 0;
var i;
for(i=0; i<10; i++) {
if(i>5) {
foo(i)
}
}
//foo: 6
//foo: 7
//foo: 8
//foo: 9
//foo 被调用了多少次?
console.log(foo.count) //0
可以看到foo()执行了4次,但是foo.count仍然是0,所以从字面上理解this指向的是当前函数自身就是错误的!
在执行foo.count=0的时候,确实向函数对象foo添加了一个count属性,但是函数内部的this.count的this并不是指向那个函数对象(其实是window对象)。
那么增加的是哪个count?这是创建在全局变量的一个count,值为NaN.
如果要从函数对象内部引用它自身,那只使用this是不够的。一般你需要通过一个指向函数对象的词法标识符来引用。
function foo() {
foo.count = 4 //foo指向它自身
}
setTimeout(function() {
//匿名函数无法指向自身
}, 10)
第一个函数被称为具名函数,在它内部可以使用foo来引用自身。但是在第二个例子中,传入setTimeout(..)的回调函数没有名称标识符,因此无法从函数内部引用自身。
还有一种方法是通过强制this指向foo函数对象:
function foo(num) {
console.log("foo:" + num)
this.count ++
}
foo.count = 0
var i;
for(i=0; i<10; i++) {
if(i>5) {
foo.call(foo, i)
}
}
如上,我们强制this指向了foo,这样就可以获得我们想要的答案了。
误解之它的作用域
第二种错误的理解是this指向函数的作用域。这个问题有些复杂,因为在某种情况下它是正确的。
需要明确的是,this在任何情况下都不指向函数的词法作用域。在JavaScript内部,作用域确实很像对象,可见的标识符都是它的属性。但是作用域对象是无法通过JavaScript代码进行访问,它是在JavaScript引擎内部。
function foo() {
var a = 2;
this.bar();
}
function bar() {
console.log(this.a)
}
foo(); // a is not defined
以上代码运行是不会得到你理想的结果的,因为你不能使用this来引用一个词法作用域内部的东西。
3. this到底是什么
this是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,取决于函数的调用方式。
当一个函数调用时,会创建一个活动记录(有时候也称为执行上下文)。这个记录 会包含函数在哪里被调用(调用栈),函数的调用方法,传入的参数信息。this就是记录的其中一个属性,会在函数执行的过程中用到。
总结
这里我们要明白this既不是指向函数自身,也不是指向函数的词法作用域。这是理解this的前提。
this实际上时在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。