一. 全局 this
1.在浏览器中,在一个全局环境中,this
就是window
对象。
<script type="text/javascript">
console.log(this === window); //true
</script>
2.在浏览器中,在全局中使用var
相当于分配给this
或者window
<script type="text/javascript">
var foo = "bar";
console.log(this.foo); //logs "bar"
console.log(window.foo); //logs "bar"
</script>
3.假如你创建一个新的变量,不使用var
或者let
(ECMAScript6),你是添加或者改变全局this
的属性
<script type="text/javascript">
foo = "bar";
function testThis() {
foo = "foo";
}
console.log(this.foo); //logs "bar"
testThis();
console.log(this.foo); //logs "foo"
</script>
4.在node中使用repl,this
是最顶级的命名空间,你可以认为是global
> this
{ ArrayBuffer: [Function: ArrayBuffer],
Int8Array: { [Function: Int8Array] BYTES_PER_ELEMENT: 1 },
Uint8Array: { [Function: Uint8Array] BYTES_PER_ELEMENT: 1 },
...
> global === this
true
5.在node中执行脚本,在全局中this
是一个空对象,而不与global
相等
test.js:
console.log(this);
console.log(this === global);
$ node test.js
{}
false
6.在node中,全局环境中的var
并非像在浏览器中执行脚本一样,分配给this
。
test.js:
var foo = "bar";
console.log(this.foo);
$ node test.js
undefined
但是在repl中是一样的
> var foo = "bar";
> this.foo
bar
> global.foo
bar
7.在node中,使用脚本执行,不用var
或者let
创建的变量会添加到global
而不是this
.
test.js
foo = "bar";
console.log(this.foo);
console.log(global.foo);
$ node test.js
undefined
bar
在repl中,它是分配到这两个上的。
二. 函数中的this
除了DOM事件处理程序或者一个thisArg
已经设置的情况外,在node和浏览器中,函数中(不实例化new
)的this是全局范围的。
<script type="text/javascript">
foo = "bar";
function testThis() {
this.foo = "foo";
}
console.log(this.foo); //logs "bar"
testThis();
console.log(this.foo); //logs "foo"
</script>
test.js:
foo = "bar";
function testThis () {
this.foo = "foo";
}
console.log(global.foo);
testThis();
console.log(global.foo);
$ node test.js
bar
foo
除非你使用user strict
,this
会变为underfined
<script type="text/javascript">
foo = "bar";
function testThis() {
"use strict";
this.foo = "foo";
}
console.log(this.foo); //logs "bar"
testThis(); //Uncaught TypeError: Cannot set property 'foo' of undefined
</script>
当你new
一个函数的时候,this
会成为一个新的上下文,不等同于全局this
<script type="text/javascript">
foo = "bar";
function testThis() {
this.foo = "foo";
}
console.log(this.foo); //logs "bar"
new testThis();
console.log(this.foo); //logs "bar"
console.log(new testThis().foo); //logs "foo"
</script>
三. 原型中的this
函数对象有一个特殊的属性prototype
,当你创建一个函数实例,可以访问prototype
属性,可以使用this
进行访问
function Thing() {
console.log(this.foo);
}
Thing.prototype.foo = "bar";
var thing = new Thing(); //logs "bar"
console.log(thing.foo); //logs "bar"
加入创建多个实例化,它们共享原型上的值,this.foo
都会返回相同的值,除非你在实例化函数上进行覆盖。
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
console.log(this.foo);
}
Thing.prototype.setFoo = function (newFoo) {
this.foo = newFoo;
}
var thing1 = new Thing();
var thing2 = new Thing();
thing1.logFoo(); //logs "bar"
thing2.logFoo(); //logs "bar"
thing1.setFoo("foo");
thing1.logFoo(); //logs "foo";
thing2.logFoo(); //logs "bar";
thing2.foo = "foobar";
thing1.logFoo(); //logs "foo";
thing2.logFoo(); //logs "foobar";
this
在一个实例中是一个特殊的对象,this
实际是一个关键字,可以认为this
作为一种方法去访问prototype
,直接分配给this
,将会覆盖原来prototype
上的方法。你可以删除this
挂接的方法,从而恢复访问默认prototype
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
console.log(this.foo);
}
Thing.prototype.setFoo = function (newFoo) {
this.foo = newFoo;
}
Thing.prototype.deleteFoo = function () {
delete this.foo;
}
var thing = new Thing();
thing.setFoo("foo");
thing.logFoo(); //logs "foo";
thing.deleteFoo();
thing.logFoo(); //logs "bar";
thing.foo = "foobar";
thing.logFoo(); //logs "foobar";
delete thing.foo;
thing.logFoo(); //logs "bar";
或者直接引用函数对象的原型。
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
console.log(this.foo, Thing.prototype.foo);
}
var thing = new Thing();
thing.foo = "foo";
thing.logFoo(); //logs "foo bar";
创建的实例都共享相同的属性和方法,如果给prototype
分配一个数组,所有实例都能够访问。
function Thing() {
}
Thing.prototype.things = [];
var thing1 = new Thing();
var thing2 = new Thing();
thing1.things.push("foo");
console.log(thing2.things); //logs ["foo"]
在prototype
上分配一个数组通常是一个错误,如果希望每个实例都有自己的数组,那在函数中创建。
function Thing() {
this.things = [];
}
var thing1 = new Thing();
var thing2 = new Thing();
thing1.things.push("foo");
console.log(thing1.things); //logs ["foo"]
console.log(thing2.things); //logs []
this
可以通过原型链找到相应的方法。
function Thing1() {
}
Thing1.prototype.foo = "bar";
function Thing2() {
}
Thing2.prototype = new Thing1();
var thing = new Thing2();
console.log(thing.foo); //logs "bar"
在javascript中可以使用原型链模拟传统面向对象继承。
使用函数内含有绑定this
的方法或者属性去创建原型链,将会隐藏上层原型链定义的内容。
function Thing1() {
}
Thing1.prototype.foo = "bar";
function Thing2() {
this.foo = "foo";
}
Thing2.prototype = new Thing1();
function Thing3() {
}
Thing3.prototype = new Thing2();
var thing = new Thing3();
console.log(thing.foo); //logs "foo"
我喜欢叫绑定在原型上的函数为methods.在methods中使用this
绑定某个值,将会覆盖原型上的相关定义。
function Thing1() {
}
Thing1.prototype.foo = "bar";
Thing1.prototype.logFoo = function () {
console.log(this.foo);
}
function Thing2() {
this.foo = "foo";
}
Thing2.prototype = new Thing1();
var thing = new Thing2();
thing.logFoo(); //logs "foo";
在JavaScript嵌套函数中,虽然可以捕获到父函数中的变量,但是不继承this
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
var info = "attempting to log this.foo:";
function doIt() {
console.log(info, this.foo);
}
doIt();
}
var thing = new Thing();
thing.logFoo(); //logs "attempting to log this.foo: undefined"
函数doIt中的this
指向global
,在use strict
下则为undefined
,这是很多不熟悉this
用法的人痛苦的根源之一。
更坏的情况是,将一个实例方法作为参数传入函数。this
将指向global
,在use strict
下则为undefined
。
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
console.log(this.foo);
}
function doIt(method) {
method();
}
var thing = new Thing();
thing.logFoo(); //logs "bar"
doIt(thing.logFoo); //logs undefined
一些人把this
赋值给一个变量,通常叫self,能够避免this
指向global
这个问题。
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
var self = this;
var info = "attempting to log this.foo:";
function doIt() {
console.log(info, self.foo);
}
doIt();
}
var thing = new Thing();
thing.logFoo(); //logs "attempting to log this.foo: bar"
但是这种方法在将一个实例方法作为参数传入函数情况下,不起作用
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
var self = this;
function doIt() {
console.log(self.foo);
}
doIt();
}
function doItIndirectly(method) {
method();
}
var thing = new Thing();
thing.logFoo(); //logs "bar"
doItIndirectly(thing.logFoo); //logs undefined
解决这个方法,可以使用函数绑定的方法bind
。
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
console.log(this.foo);
}
function doIt(method) {
method();
}
var thing = new Thing();
doIt(thing.logFoo.bind(thing)); //logs bar
你也可以使用apply
或者call
在新的上下文环境中调用方法或者函数。
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
function doIt() {
console.log(this.foo);
}
doIt.apply(this);
}
function doItIndirectly(method) {
method();
}
var thing = new Thing();
doItIndirectly(thing.logFoo.bind(thing)); //logs bar
可以使用bind
替换this
,适用于任何函数或方法,即使没有在实例原型上定义。
function Thing() {
}
Thing.prototype.foo = "bar";
function logFoo(aStr) {
console.log(aStr, this.foo);
}
var thing = new Thing();
logFoo.bind(thing)("using bind"); //logs "using bind bar"
logFoo.apply(thing, ["using apply"]); //logs "using apply bar"
logFoo.call(thing, "using call"); //logs "using call bar"
logFoo("using nothing"); //logs "using nothing undefined"
避免从构造函数返回任何东西,因为它可能会替换所产生的实例。
function Thing() {
return {};
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
console.log(this.foo);
}
var thing = new Thing();
thing.logFoo(); //Uncaught TypeError: undefined is not a function
奇怪的是,假如你返回的是原始值(string或者number),返回语句将会被忽略。最好不要从你打算调用的构造函数中返回任何东西,即使你知道你在做什么。如果你想创建一个工厂模式,使用一个函数来创建实例,不要用new
的。当然,这只是个人观点。
避免使用new
`,使用Object.create
也能创建一个实例
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
console.log(this.foo);
}
var thing = Object.create(Thing.prototype);
thing.logFoo(); //logs "bar"
然而这不会调用构造函数。
function Thing() {
this.foo = "foo";
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
console.log(this.foo);
}
var thing = Object.create(Thing.prototype);
thing.logFoo(); //logs "bar"
因为Object.create
不会调用构造函数,所以这是一个有效的创建继承模式的方法,能够重写原型链上的构造函数。
function Thing1() {
this.foo = "foo";
}
Thing1.prototype.foo = "bar";
function Thing2() {
this.logFoo(); //logs "bar"
Thing1.apply(this);
this.logFoo(); //logs "foo"
}
Thing2.prototype = Object.create(Thing1.prototype);
Thing2.prototype.logFoo = function () {
console.log(this.foo);
}
var thing = new Thing2();
四. 对象中的this
可以在对象的任何函数中使用this
来引用该对象上的其他属性。这与使用new
实例不同。
var obj = {
foo: "bar",
logFoo: function () {
console.log(this.foo);
}
};
obj.logFoo(); //logs "bar"
不使用new
,Object.create
,function
去创建一个对象,也可以像实例化一样绑定到对象上。
var obj = {
foo: "bar"
};
function logFoo() {
console.log(this.foo);
}
logFoo.apply(obj); //logs "bar"
当你像下面使用this
时,没有顺着对象的层次结构。只有直接父对象上的属性才能通过this
进行访问
var obj = {
foo: "bar",
deeper: {
logFoo: function () {
console.log(this.foo);
}
}
};
obj.deeper.logFoo(); //logs undefined
你可以直接使用你想要的属性。
var obj = {
foo: "bar",
deeper: {
logFoo: function () {
console.log(obj.foo);
}
}
};
obj.deeper.logFoo(); //logs "bar"
五. DOM event中的this
在一个HTML DOM event处理程序中,this
通常是指DOM element event绑定的对象
function Listener() {
document.getElementById("foo").addEventListener("click",
this.handleClick);
}
Listener.prototype.handleClick = function (event) {
console.log(this); //logs "<div id="foo"></div>"
}
var listener = new Listener();
document.getElementById("foo").click();
除非你绑定新的上下文
function Listener() {
document.getElementById("foo").addEventListener("click",
this.handleClick.bind(this));
}
Listener.prototype.handleClick = function (event) {
console.log(this); //logs Listener {handleClick: function}
}
var listener = new Listener();
document.getElementById("foo").click();
六. HTML中的this
在HTML属性中可以放js代码,this
指向当前的元素
<div id="foo" onclick="console.log(this);"></div>
<script type="text/javascript">
document.getElementById("foo").click(); //logs <div id="foo"...
</script>
this的覆盖
你不能够复写this
,因为它是一个关键词
function test () {
var this = {}; // Uncaught SyntaxError: Unexpected token this
}
七. eavl中的this
可以使用eavl
访问this
function Thing () {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
eval("console.log(this.foo)"); //logs "bar"
}
var thing = new Thing();
thing.logFoo();
这种做法有安全隐患,可以使用Function
访问this
function Thing () {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = new Function("console.log(this.foo);");
var thing = new Thing();
thing.logFoo(); //logs "bar"
八. with中的this
可以使用with
将this
添加到当前的范围来读取和写入值,而不用显式调用。
function Thing () {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
with (this) {
console.log(foo);
foo = "foo";
}
}
var thing = new Thing();
thing.logFoo(); // logs "bar"
console.log(thing.foo); // logs "foo"
很多人认为这是错的做法,鉴于with
引起的歧义。
九. jQuery中的this
像HTML DOM elements的事件处理程序一样,jQuery在很多地方使用this
指向DOM元素。比如$.each
<div class="foo bar1"></div>
<div class="foo bar2"></div>
<script type="text/javascript">
$(".foo").each(function () {
console.log(this); //logs <div class="foo...
});
$(".foo").on("click", function () {
console.log(this); //logs <div class="foo...
});
$(".foo").each(function () {
this.click();
});
</script>
鉴于笔者翻译水平有限,有什么问题欢迎提出指教。
十.参考资料
原文地址:all this
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。