简介
在绝大数情况下,函数的调用方式决定了this
值。this
不能在执行期间被复制,并且在每次函数被调用时this
的值也可能会不同。
this
的值表示当前执行的环境对象,而与声明环境无关,所以this
代表的对象要等函数运行。类似定义函数时的参数列表,只有在函数调用时才传入真正的对象。
this
关键字虽然会根据环境变化,但它始终代表的是调用当前函数的对象。
全局环境
无论是否在严格模式下,在全局执行环境中(在任何函数体外部),this
都指向全局对象。
console.log(this === window); //true
函数环境中的this
在函数内部,this
的值取决于函数被调用的方式。
简单调用
在非严格模式下,this
的值不是由该调用设置的,所以this
的值默认指向全局对象。
function f1() {
return this;
}
f1() === window;
在严格模式下,this
将保持他进入执行环境时的值,所以this
将会默认为undefined
:
function f2() {
"use strict";
return this;
}
f2() === undefined;
在严格模式下,如果this
没有执行环境定义,那它将保持为undefined
。
如果想把this
的值从一个环境传到另一个,就要用call
或者apply
方法。
//将一个对象作为call和apply的第一个参数,this就会被绑定到这个对象。
var obj = {a: 'Custom '};
var a = "Global";
function whatsThis(arg) {
return this.a;
}
whatsThis(); //'Global'
whatsThis.call(obj); //'Custom'
whatsThis.apply(obj); //'Custom'
当一个函数在其主体中使用this
关键字时,可以使用函数继承自Function.prototype
的call
或apply
方法将this
值绑定到调用中的特定对象。
使用call
和apply
函数的时候,如果传递给this
的值不是一个对象,JavaScript会尝试使用内部toObject
操作将其转换为对象。
function bar() {
console.log(Object.prototype.toString.call(this));
}
bar.call(7); //[object Number]
bind方法
ES5引入了Function.prototype.bind
。调用f.bind(someObject)
会创建一个与f
具有相同函数体和作用域的函数,但是在这个新函数中,this
将永久的被绑定到了bind
的第一个参数,无论这个函数是如何被调用的。
function f() {
return this.a;
}
var g = f.bind({a:"azerty"});
console.log(g()); //azerty
var h = g.bind({a:"yoo"}); //bind只生效一次,所以此处bind无效
console.log(h()); //azerty
var o = {a:37, f:f, g:g, h:h};
console.log(o.f(), o.g(), o.h()); //37, azerty, azerty
箭头函数
在箭头函数中,this
与普通函数的指向不同,它与封闭词法环境的this
保持一致。在箭头函数中,this
指向创建时所在的环境,这同样适用于在其他函数内创建的箭头函数;在普通函数中,this
指向执行时的环境。
箭头函数被调用的时候,不会自动绑定一个this对象。箭头对象根本没有自己的this,它的this都是捕获自其所在上下文的this值。
举栗子:
var foo = (( => this));
console.log(foo() === window); //true
//作为对象的一个方法调用
var obj = {foo: foo};
console.log(obj.foo() === window); //ture
//使用call来设定this
console.log(foo.call(obj) === window); //true
//使用bind设定this
foo = foo.bind(obj);
console.log(foo() === window); //true
第二个栗子:
//bar返回一个函数,这个函数返回this
//这个返回的函数是以箭头函数创建的
//所以它的this被绑定到了外层函数的this。
var obj = {
bar: function() {
var x = (() => this);
return x;
}
};
//作为obj对象的一个方法来调用bar,把它的this绑定到obj。将返回的函数的引用复制给fn
var fn = obj.bar();
//直接调用fn而不设置this
//通常不适用箭头函数的情况,默认为全局对象
//若在严格模式则为undefined
console.log(fn() === obj); //true
//如果只是引用obj的方法,而没有调用它
var fn2 = obj.bar; //fn2 = function() {var x = (() => this); return x;}
//调用箭头函数后,this指向window,因为它从bar继承了this
console.log(fn2() === window); /true
作为对象的方法
当函数作为对象里的方法被调用时,它们的this
是调用该函数的对象。
栗子:
var o = {
prop: 37
f: function() {
return this.prop;
}
}
console.log(o.f()); //logs 37
函数时从o
的f
成员调用才是重点。
同样,this
的绑定只受最靠近的成员应用的影响。
o.b = {g: independent, prop: 42}
console.log(o.b.g()) //42
这个栗子说明,这与它是对象o
的成员没有什么关系,最靠近的引用才是最重要的。
原型链中的this
对于在对象原型链上某处定义的方法,同样的概念也使用。如果该方法存在于一个对象的原型链上,那么this
指向的是调用这个方法的对象,就像是该方法在对象上一样。
var o = {
f: function() {
return this.a + this.b;
}
};
var p = Object.create(o);
p.a = 1;
p.b = 4;
console.log(p.f()); //5
在这个栗子中,对象p
没有属于自己的属性,它的f
属性继承自它的原型。虽然在对于f
的查找过程中,最终是在o
中找到的f
属性,但是查找股从横首先从p.f
的引用开始,所以函数中的this
指向p
。也就是说f
是作为p
的方法调用的,所以它的this
指向了p
。这是JavaScript的原型继承中的一个有趣的特征。
getter与setter中的this
同样的概念适用于,当函数在一个getter
或者setter
中被调用。用作getter
或setter
的函数都会把this
绑定到设置或获取属性的对象。
function sum() {
return this.a + this.b + this.c;
}
var o = {
a: 1,
b: 2,
c: 3,
get average() {
return (this.a + this.b + this.c) / 3;
}
};
Object.defineProperty(o, 'sum', {
get: sum, enumerable: true, configurable: trye
})
console.log(o.average, o.sum); //logs 2, 6
作为构造函数
当一个函数用作构造函数时(使用new关键字),它的this
被绑定到正在构造的新对象。
虽然构造器返回的默认值是this
所指的对象,但是也可以手动地返回其他的对象(如果返回值不是一个对象,则返回this
对象)。
function C() {
this.a = 37;
}
var o = new C();
console.log(o.a); //logs 37
function C2() {
this.a = 37;
return {a:38};
}
o = new C2();
console.log(o.a); //logs 38
在这个栗子中,在调用构造函数的过程中,手动的设置了返回对象,所以默认对象就被丢弃了。this.a = 37;
这条语句虽然执行了,但是对于外部没有任何影响。
作为一个DOM事件处理函数
当函数被用作事件处理函数时,它的this
指向触发事件的元素。
作为一个内联事件处理函数
当代码被内联on-event处理函数调用时,它的this
指向监听器所在的DOM元素:
<button onclick="alert(this.tagName.toLowerCase());">
Show this
</button>
这个栗子的alert会显示button
。只有外层代码中的this
是这样设置的:
<button onclick="alert((function() {return this})());">
Show inner this
</button>
在这种情况下,没有设置内部函数的this
,所以它指向window/global对象,非严格模式下,调用的函数未设置this
时指向的默认对象。