You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
functionThing(){}Thing.prototype.foo="bar";Thing.prototype.logFoo=function(){varinfo="attempting to log this.foo:";functiondoIt(){console.log(info,this.foo);}doIt();}varthing=newThing();thing.logFoo();//logs "attempting to log this.foo: undefined"
functionThing(){}Thing.prototype.foo="bar";Thing.prototype.logFoo=function(){varself=this;varinfo="attempting to log this.foo:";functiondoIt(){console.log(info,self.foo);}doIt();}varthing=newThing();thing.logFoo();//logs "attempting to log this.foo: bar"
functionThing(){}Thing.prototype.foo="bar";Thing.prototype.logFoo=function(){console.log(this.foo);}functiondoIt(method){method();}varthing=newThing();doIt(thing.logFoo.bind(thing));//logs bar
functionThing(){}Thing.prototype.foo="bar";Thing.prototype.logFoo=function(){functiondoIt(){console.log(this.foo);}doIt.apply(this);}functiondoItIndirectly(method){method();}varthing=newThing();doItIndirectly(thing.logFoo.bind(thing));//logs bar
functionThing(){return{};}Thing.prototype.foo="bar";Thing.prototype.logFoo=function(){console.log(this.foo);}varthing=newThing();thing.logFoo();//Uncaught TypeError: undefined is not a function
而JavaScript不是这样的。手册难读。非常多缺陷在里面,以至于人们抽离出了它好的部分(The Good Parts)。最好的文档可能是MDN上的了。所以我建议你看看他上面关于this的介绍,并且始终在搜索JS相关问题时加上"mdn" 来获得最好的文档资料。静态代码检查也是个不错的工具,比如jshint。
The text was updated successfully, but these errors were encountered:
原文:all this
习惯了高级语言的你或许觉得JavaScript中的
this
跟Java这些面向对象语言相似,保存了实体属性的一些值。其实不然。将它视作幻影魔神比较恰当,手提一个装满未知符文的灵龛。以下内容我希望广大同行们能够了解。下面都是满满的干货,其中很多花费了我很多时间才掌握。
全局
this
浏览器宿主的全局环境中,
this
指的是window
对象。Demo
浏览器中在全局环境下,使用
var
声明变量其实就是赋值给this
或window
。Demo
任何情况下,创建变量时没有使用
var
或者let
(ECMAScript 6),也是在操作全局this
。Demo
Node命令行(REPL)中,
this
是全局命名空间。可以通过global
来访问。在Node环境里执行的JS脚本中,
this
其实是个空对象,有别于global
。当尝试在Node中执行JS脚本时,脚本中全局作用域中的
var
并不会将变量赋值给全局this
,这与在浏览器中是不一样的。...但在命令行里进行求值却会赋值到
this
身上。在Node里执行的脚本中,创建变量时没带
var
或let
关键字,会赋值给全局的global
但不是this
(译注:上面已经提到this
和global
不是同一个对象,所以这里就不奇怪了)。但在Node命令行里,就会赋值给两者了。
注意:* Node脚本中
global
和this
是区别对待的,而Node命令行中,两者可等效为同一对象。*函数或方法里的
this
除了DOM的事件回调或者提供了执行上下文(后面会提到)的情况,函数正常被调用(不带
new
)时,里面的this
指向的是全局作用域。Demo
还有个例外,就是使用了
"use strict";
。此时this
是undefined
。Demo
当用调用函数时使用了
new
关键字,此刻this
指代一个新的上下文,不再指向全局this
。Demo
通常我将这个新的上下文称作实例。
原型中的
this
函数创建后其实以一个函数对象的形式存在着。既然是对象,则自动获得了一个叫做
prototype
的属性,可以自由地对这个属性进行赋值。当配合new
关键字来调用一个函数创建实例后,此刻便能直接访问到原型身上的值。Demo
当通过
new
的方式创建了多个实例后,他们会共用一个原型。比如,每个实例的this.foo
都返回相同的值,直到this.foo
被重写。Demo
在实例中,
this
是个特殊的对象,而this
自身其实只是个关键字。你可以把this
想象成在实例中获取原型值的一种途径,同时对this
赋值又会覆盖原型上的值。完全可以将新增的值从原型中删除从而将原型还原为初始状态。Demo
...或者不通过实例,直接操作函数的原型。
Demo
同一函数创建的所有实例均共享一个原型。如果你给原型赋值了一个数组,那么所有实例都能获取到这个数组。除非你在某个实例中对其进行了重写,事实上是进行了覆盖。
Demo
通常上面的做法是不正确的(译注:改变
thing1
的同时也影响了thing2
)。如果你想每个实例互不影响,那么请在函数里创建这些值,而不是在原型上。Demo
多个函数可以形成原型链,这样
this
便会在原型链上逐步往上找直到找到你想引用的值。Demo
很多人便是利用这个特性在JS中模拟经典的对象继承。
注意原型链底层函数中对
this
的操作会覆盖上层的值。Demo
我习惯将赋值到原型上的函数称作方法。上面某些地方便使用了方法这样的字眼,比如
logFoo
方法。这些方法中的this
同样具有在原型链上查找引用的魔力。通常将最初用来创建实例的函数称作构造函数。原型链方法中的
this
是从实例中的this
开始住上查找整个原型链的。也就是说,如果原型链中某个地方直接对this
进行赋值覆盖了某个变量,那么我们拿到 的是覆盖后的值。Demo
在JavaScript中,函数可以嵌套函数,也就是你可以在函数里面继续定义函数。但内层函数是通过闭包获取外层函数里定义的变量值的,而不是直接继承
this
。Demo
上面Demo中,
doIt
函数中的this
指代是全局作用域或者是undefined
如果使用了"use strict";
声明的话。对于很多新手来说,理解这点是非常头疼的。还有更奇葩的。把实例的方法作为参数传递时,实例是不会跟着过去的。也就是说,此时方法中的
this
在调用时指向的是全局this
或者是undefined
在声明了"use strict";
时。Demo
所以很多人习惯将
this
缓存起来,用个叫self
或者其他什么的变量来保存,以将外层与内层的this
区分开来。Demo
...但上面的方式不是万能的,在将方法做为参数传递时,就不起作用了。
Demo
解决方法就是传递的时候使用
bind
方法显示指明上下文,bind
方法是所有函数或方法都具有的。Demo
同时也可以使用
apply
或call
来调用该方法或函数,让它在一个新的上下文中执行。Demo
使用
bind
可以任意改变函数或方法的执行上下文,即使它没有被绑定到一个实例的原型上。Demo
避免在构造函数中返回作何东西,因为返回的东西可能覆盖本来该返回的实例。
Demo
但,如果你在构造函数里返回的是个原始值比如字符串或者数字什么的,上面的错误就不会发生了,返回语句将被忽略。所以最好别在一个将要通过
new
来调用的构造函数中返回作何东西,即使你是清醒的。如果你想实现工厂模式,那么请用一个函数来创建实例,并且不通过new
来调用。当然这只是个人建议。当然,你也可以使用
Object.create
从而避免使用new
。这样也能创建一个实例。Demo
这种方式不会调用该构造函数。
Demo
正因为
Object.create
没有调用构造函数,这在当你想实现一个继承时是非常有用的,随后你可能想要重写构造函数。Demo
对象中的
this
可以在对象的任何方法中使用
this
来访问该对象的属性。这与用new
得到的实例是不一样的。Demo
注意这里并没有使用
new
,也没有用Object.create
,更没有函数的调用来创建对象。也可以将函数绑定到对象,就好像这个对象是一个实例一样。Demo
此时使用
this
没有向上查找原型链的复杂工序。通过this
所拿到的只是该对象身上的属性而以。Demo
也可以不通过
this
,直接访问对象的属性。Demo
DOM 事件回调中的
this
在DOM事件的处理函数中,
this
指代的是被绑定该事件的DOM元素。Demo
...除非你通过
bind
人为改变了事件处理器的执行上下文。Demo
HTML中的
this
HTML标签的属性中是可能写JS的,这种情况下
this
指代该HTML元素。Demo
重写
this
无法重写
this
,因为它是一个关键字。Demo
eval
中的this
eval
中也可以正确获取当前的this
。Demo
这里存在安全隐患。最好的办法就是避免使用
eval
。使用
Function
关键字创建的函数也可以获取this
:Demo
使用
with
时的this
使用
with
可以将this
人为添加到当前执行环境中而不需要显示地引用this
。Demo
正如很多人认为的那样,使用
with
是不好的,因为会产生歧义。jQuery中的
this
一如HTML DOM元素的事件回调,jQuery库中大多地方的
this
也是指代的DOM元素。页面上的事件回调和一些便利的静态方法比如$.each
都是这样的。Demo
传递
this
如果你用过underscore.js或者lo-dash你便知道,这两个库中很多方法你可以传递一个参数来显示指定执行的上下文。比如
_.each
。自ECMAScript 5 标准后,一些原生的JS方法也允许传递上下文,比如forEach
。事实上,上文提到的bind
,apply
还有call
已经给我们手动指定函数执行上下文的能力了。Demo
这样可以使得代码简洁些,不用层层嵌套
bind
,也不用不断地缓存this
。一些编程语言上手很简单,比如Go语言手册可以被快速读完。然后你差不多就掌握这门语言了,只是在实战时会有些小的问题或陷阱在等着你。
而JavaScript不是这样的。手册难读。非常多缺陷在里面,以至于人们抽离出了
它好的部分
(The Good Parts)。最好的文档可能是MDN上的了。所以我建议你看看他上面关于this
的介绍,并且始终在搜索JS相关问题时加上"mdn" 来获得最好的文档资料。静态代码检查也是个不错的工具,比如jshint。The text was updated successfully, but these errors were encountered: