JS中的this详解
简介
在绝大数情况下,函数的调用方式决定了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时指向的默认对象。

浙公网安备 33010602011771号