这是一篇目前篇幅不长,可是写起来至关漫长的文章。中途,我翻译了这篇文章,很是有必要一读。javascript
「在函数内部,有两个特殊的对象:arguments和this」java
你可能见过这样一个函数:segmentfault
function addUp(){ return Array.from(arguments).reduce(function (prev, curr) { return prev + curr }) }
并无定义过它,可是arguments
直接就这么冒出来了。this
和它同样,它们自动定义在全部函数的做用域中,即便你并无用它。app
NZ在《JavaScript高级程序设计》里给this
下了个定义:「this引用的是函数据以执行的环境对象」。这就像是不少人强调的,不要看函数在哪里定义,要看函数在哪里被调用。ide
this
机制能够解耦对象和函数:函数
function identify () { return this.name.toUpperCase() } function speak () { var greeting = "Hello,I'm + identify.call(this)" console.log(greeting) } var me = { name: 'cyq' } var you = { name: 'reader' } identify.call(me) // CYQ identify.call(you) // READER speak.call(me) // Hello,I'm CYQ speak.call(you) // Hello,I'm READER
这段代码能够在不一样的上下文对象中重复使用identify
和speak
,不用针对每一个对象编写不一样版本的函数。若是没有this
,就须要给函数显式地传递一个上下文对象。随着你的模式愈来愈复杂,显式传递上下文会让代码愈来愈混乱。当涉及到原型委托的时候,你就会明白函数能够自动引用合适的上下文对象有多重要。this
我在kangax的博客里看到过多是最短的总结:prototype
The keyword "this" refers to whatever is left of the dot at call-time.翻译
If there's nothing to the left of the dot, then "this" is the root scope (e.g. Window).设计
A few functions change the behavior of "this"—bind, call and apply
The keyword "new" binds this to the object just created
若是看过了这个四条法则,那判断this
指向就好办了啊,倒过来看就行了:
函数是否被new修饰(new绑定)?若是是的话,this
绑定的就是新建立的对象
函数是否经过call、apply()或者bind()硬绑定(显式绑定)?若是是的话,this
绑定的就是指定的对象。
函数是否在某个上下文对象调用(隐式绑定)?若是是的话,this
绑定的就是那个对象。
若是都不是的话,使用默认绑定。若是在严格模式下,就绑定undefined
,不然是全局对象。
这多是这篇文章最核心的地方,可是鉴于这四条太常见了,我假设读者已经知道它们了。
把
this
指向函数自身,从语法角度来讲是说得通的
function foo(num) { this.count++ } foo.count = 0 for(let i = 0;i < 10;i++) { if(i > 5) { foo(i) } } console.log(foo.count) // 0
若是this
指向的是函数自身的话,foo.count
就该是4而不是0了。仔细看看就能明白这里是符合第四条的默认绑定的。
不要看它是在哪定义的,也不要看它是在哪被调用的,看它是怎么被调用的
function foo() { let a = 2 bar() } function bar() { console.log(this.a) } foo() // ReferenceError: a is not a defined
对照第四条,默认绑定。
每当你想要把this
和词法做用域的查找混合使用时,记得提醒本身,这是无法实现的,箭头函数例外。
隐式丢失一般包括这几种状况:
赋值运算符 : (f = foo.bar)()
逗号运算符 : (1, foo.bar)()
回调函数
这一点能够说是一个庞大的工程,为此我翻译了这篇博客。翻译这篇博客这是件很是累的事儿,幸运的是,看完这篇博客以后,我明白了不少我没想到的东西。
全部须要了解的东西均可以在这篇博客里找到答案。
咱们知道,当代码进入一个函数时,做用域会把函数的参数、this 对象和 arguments对象包括在内。所以咱们不可能在一个函数里面用this取到其余函数做用域内的this对象。
var object = { name: 'My object', getName: function() { return function() { return this.name } } } console.log(object.getName()())
这段代码会输出的是什么?虽然看上去很像是'My Object',可是结果是undefined
.缘由上面提到了:最内层的匿名函数的做用域内的this
和getName的this
是两回事。那想要输出'My Object'的话,天然要想办法把getName的this绑定到匿名函数下:
var me = { name: 'cyq', getName: function() { var self = this return function() { return self.name } } } console.log(object.getName()())
能够看到,self=this
经常使用的场景就是帮咱们在一个内层的函数里找到外层函数的this对象。咱们须要把外层的this绑定在一个变量上,从而让内层函数完成某种相似于词法做用域的查找。我我的是很不喜欢这个写法的,后来我发现我不是一我的:搞懂JavaScript的Function.prototype.bind。
若是你了解过箭头函数的话,你可能知道箭头函数能够淘汰掉这种写法。
虽然少打好几个字符就已经足够让人开心了,可是箭头函数的能力还不止于此。
var me = { name: 'cyq', getName: function() { return () => { return this.name } } } console.log(object.getName()()) //cyq
因此箭头函数必定对它里面的this
作了从新设计咯?答案是否认的。
箭头函数能实现相似词法做用域的查找是由于,它里面根本就没有this
对象。前面提到,this
自动定义在全部函数的做用域内,内嵌函数是不能查找到外部的this
对象的,由于本身的做用域里就有this
了。而箭头函数这个特殊的函数直接去做用域里找this
。