首页 > 网站 > WEB开发 > 正文

详解this

2024-04-27 14:09:16
字体:
来源:转载
供稿:网友

详解this

wayou译文javascript

this虐我千百遍,看完此文效立见!不得不说,这篇文章的总结很地道很全面,适合收藏之用。原文:all this

习惯了高级语言的你或许觉得Javascript中的this跟Java这些面向对象语言相似,保存了实体属性的一些值。其实不然。将它视作幻影魔神比较恰当,手提一个装满未知符文的灵龛。

以下内容我希望广大同行们能够了解。全是掏箱底的干货,其中大部分占用了我很多时间才掌握。

全局this

浏览器宿主的全局环境中,this指的是window对象。

123<script type="text/javascript"> console.log(this === window); //true</script>

示例

浏览器中在全局环境下,使用var声明变量其实就是赋值给thiswindow

12345<script type="text/javascript"> var foo = "bar"; console.log(this.foo); //logs "bar" console.log(window.foo); //logs "bar"</script>

示例

任何情况下,创建变量时没有使用var或者let(ECMAScript 6),也是在操作全局this

1234567891011<script type="text/javascript"> foo = "bar"; function testThis() { foo = "foo"; } console.log(this.foo); //logs "bar" testThis(); console.log(this.foo); //logs "foo"</script>

示例

Node命令行(REPL)中,this是全局命名空间。可以通过global来访问。

1234567> this{ ArrayBuffer: [Function: ArrayBuffer], Int8Array: { [Function: Int8Array] BYTES_PER_ELEMENT: 1 }, Uint8Array: { [Function: Uint8Array] BYTES_PER_ELEMENT: 1 }, ...> global === thistrue

在Node环境里执行的JS脚本中,this其实是个空对象,有别于global

test.js

12console.log(this);console.log(this === global);
123$ node test.js{}false

当尝试在Node中执行JS脚本时,脚本中全局作用域中的var并不会将变量赋值给全局this,这与在浏览器中是不一样的。

test.js

12var foo = "bar";console.log(this.foo);
12$ node test.jsundefined

…但在命令行里进行求值却会赋值到this身上。

12345> var foo = "bar";> this.foobar> global.foobar

在Node里执行的脚本中,创建变量时没带varlet关键字,会赋值给全局的global但不是this(译注:上面已经提到thisglobal不是同一个对象,所以这里就不奇怪了)。

test.js

123foo = "bar";console.log(this.foo);console.log(global.foo);
123$ node test.jsundefinedbar

但在Node命令行里,就会赋值给两者了。

译注:简单来说,Node脚本中globalthis是区别对待的,而Node命令行中,两者可等效为同一对象。

函数或方法里的this

除了DOM的事件回调或者提供了执行上下文(后面会提到)的情况,函数正常被调用(不带new)时,里面的this指向的是全局作用域。

1234567891011<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

123456789foo = "bar";function testThis () { this.foo = "foo";}console.log(global.foo);testThis();console.log(global.foo);
123$ node test.jsbarfoo

还有个例外,就是使用了"use strict";。此时thisundefined

1234567891011<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

12345678910111213<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的属性,可以自由地对这个属性进行赋值。当配合new关键字来调用一个函数创建实例后,此刻便能直接访问到原型身上的值。

12345678function Thing() { console.log(this.foo);}Thing.prototype.foo = "bar";var thing = new Thing(); //logs "bar"console.log(thing.foo); //logs "bar"

示例

当通过new的方式创建了多个实例后,他们会共用一个原型。比如,每个实例的this.foo都返回相同的值,直到this.foo被重写。

1234567891011121314151617181920212223function 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想象成在实例中获取原型值的一种途径,同时对this赋值又会覆盖原型上的值。完全可以将新增的值从原型中删除从而将原型还原为初始状态。

12345678910111213141516171819202122function 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";

示例

…或者不通过实例,直接操作函数的原型。

12345678910function 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";

示例

同一函数创建的所有实例均共享一个原型。如果你给原型赋值了一个数组,那么所有实例都能获取到这个数组。除非你在某个实例中对其进行了重写,实事上是进行了覆盖。

123456789function Thing() {}Thing.prototype.things = [];var thing1 = new Thing();var thing2 = new Thing();thing1.things.push("foo");console.log(thing2.things); //logs ["foo"]

示例

通常上面的做法是不正确的(译注:改变thing1的同时也影响了thing2)。如果你想每个实例互不影响,那么请在函数里创建这些值,而不是在原型上。

12345678910function 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便会在原型链上逐步往上找直到找到你想引用的值。

1234567891011function Thing1() {}Thing1.prototype.foo = "bar";function Thing2() {}Thing2.prototype = new Thing1();var thing = new Thing2();console.log(thing.foo); //logs "bar"
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表