你不知道的JavaScript之this篇(一)
From 《你不知道的JavaScript》上卷
this关键字是JavaScript中非常复杂的机制之一,它是一个很特别的关键字,被自动定义在所有函数的作用域中,我们平时会经常用到它,但是往往不求甚解,以致于在很多地方滥用this甚至根本弄不清this的真正含义。
- 为什么要用this
function identify(context){
return context.name.toUpperCase();
}
function speak(context){
var greeting = "hello,I'm "+ identify(context);
console.log(greeting);
}
var me = { name:'ht'}
var you = {name:'xm'}
identity(you);//XM
speak(me);//hello,I'm HT
下面使用this来重写这两个函数
function identify(){
return this.name.toUpperCase();
}
function speak(){
var greeting = "hello,I'm "+ identify.calll(this);
console.log(greeting);
}
identify.call(me);//HT
identify.call(you);//XM
speak.call(me);//hello,I'm HT
speak.call(you);hello,I'm XM
通过使用this我们可以在不同的上下文对象中重复使用identify()和speak(),不需要再显示的传入一个上下文对象,也许这个例子太简单了,让你看不出太多的差别,但随着代码的复杂度增加,显示传递上下文会让js代码变得越来越复杂,如果函数可以自动引用合适的上下文对象,我们可以将API设计得更加简洁并且易于复用。
-
误解
有两种常见的对于this的解释,但它们都是错误的。
(1)、指向函数对象自身。人们很容易把this理解成指向函数自身,因为在js中,所有的函数都可以看作对象,错误的认为this便指向当前函数对象,如下
function foo(num){
console.log("foo:" + num);
this.count ++;
}
foo.count = 0;
for(let i=0;i<10;i++){
if(i>5){
foo(i);
}
};
//foo:6
//foo:7
//foo:8
//foo:9
console.log(foo.count);//0
很显然,这里的foo(...)被调用了4次,但是foo.count的值并不是预期的4,而是0,说明了这里的this并不是指向函数自身,显然从字面意思来理解this是错误的。执行 foo.count=0
时,的确向函数对象添加了一个属性count,但是函数内部的this.count并不是指向当前函数对象的count属性,虽然属性名相同,根对象却不相同,实际上这里的this指向全局对象。
如果并不想深究为什么而只是想要解决问题,可以使用其它方式
function foo(num){
console.log('foo:'+num);
data.count++;
}
var data = { count:0 }
for(let i=0;i<10;i++){
if(i>5){
foo(i);
}
};
console.log(data.count);//4
上面使用了词法作用域来解决问题,显然这是有效的,但它避免了使用this,并没有真正理解this的含义和工作原理。如果要从函数对象内部引用它自身,那只使用this是不够的,一般来说需要通过一个指向函数对象的词法标识符(变量)来引用它。
function foo(num){
console.log('foo:'+num);
foo.count++;
}
foo.count=0;
for(let i=0;i<10;i++){
if(i>5){
foo(i);
}
};
console.log(foo.count);//4
然而这种方法同样回避了this的问题,并且完全依赖于变量foo的词法作用域。其实还有一种方法是强制this指向foo函数对象。
function foo(num){
console.log('foo:' + num);
this.count++;
}
foo.count=0;
for(let i=0;i<10;i++){
if(i>5){
foo.call(foo,i);
}
}
console.log(foo.count);//4
(2)、指向它的作用域。第二种常见的误解是,this指向函数的作用域。这个问题有点复杂,因为在某种情况下它表现是正确的,但是在其它情况下它却是错误的。需要明确的是,this在任何情况下都不指向函数的词法作用域。在JavaScript内部,作用域确实和对象类似,可见的标识符都是它的属性。但是作用域“对象”无法通过JavaScript代码访问,它存在于JavaScript引擎内部。
-
this到底是什么
this其实是在运行时绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。
当一个函数被调用时,会创建一个活动记录(有时候也被称为执行上下文)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方式、传入的参数等信息,this就是这个记录的一个属性,会在函数执行的过程中用到。总之,this实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。
突然想说一句,js真的是门坑爹的语言~