前几天,听同事讨论起javascript的this指向问题,趁此机会复习一下这个知识点。
翻了下犀牛书,在8.2.2节(第6版p171)提到了this的指向问题:
需要注意的是,this是一个关键字,不是变量,也不是属性名。JavaScript的语法不允许给this赋值。
和变量不同,关键字this没有作用域的限制,嵌套的函数不会从调用它的函数中继承this。如果嵌套函数作为方法调用,其this的值指向调用它的对象。如果嵌套函数作为函数调用,其this值不是全局对象(非严格模式下)就是undefined(严格模式下)。很多人误以为调用嵌套函数时this会指向调用外层函数的上下文。如果你想访问这个外部函数的this值,需要将this的值保存在一个变量里,这个变量和内部函数都同在一个作用域内。
(以上是LD亲手敲的奥,赞一个- -)
对着上面这段话提到的场景解释一下:
嵌套的函数不会从调用它的函数中继承this
12345678 var Outer = function () {this.foo = 'outer value';function fnInner(){console.log(this.foo)//如果继承了外层,那么会打印outer value}fnInner();};new Outer();
答案是undefined,也就是说不会继承外层this,这个应该是最好理解的。那么如何访问外层this下的属性呢?很简单,且看:
说起that,关于这个局部变量的命名有三大党派:
that
派self
派me
派
你更喜欢哪一派呢?
再看下一个:
如果嵌套函数作为方法调用,其this的值指向调用它的对象。
12345678 var Outer = function () {this.foo = 'outer value';this.fnInner = function (){console.log('nested func: ' + this.foo);//this即为刚刚创建的Outer实例对象};this.fnInner();//方法调用};new Outer();
理解这个应该也不难。最后一个场景:
如果嵌套函数作为函数调用,其this值不是全局对象(非严格模式下)就是undefined(严格模式下)。
12345678 var Outer = function () {//非严格模式this.foo = 'outer value';function fnInner(){console.log(this);//此this为全局对象,在浏览器中就是window,在node中是一个global全局对象}fnInner();//函数调用};new Outer();
浏览器:
node:
再看:
浏览器:
node:
嗯,大致有数了。
PS:严格模式是ES5推出的标准,开启这个模式(函数首行添加’use strict’;),可以防止很多由于javascript本身的设计缺陷导致的隐形bug(比如这里的this指向全局对象,可能导致全局变量污染),帮助我们及早发现问题。
上述只是犀牛书中提到的关于this的一部分相关内容,其实还远远不够。
在我理解来看,大致上分为以下四大场景:
一、函数仅作为函数被调用
即直接在函数名后加圆括号调用,或在高阶函数里调用函数类型的形参,甚至使用匿名立即执行函数,它们的this都指向全局对象。
好吧,好像又多出了几个小场景囧。不过无论全局函数还是局部函数,这都成立。上demo:
二、传给setTimeout、setInterval等全局函数的回调
这些回调内的this也是全局对象,只不过浏览器和node有些区别(我也刚发现- -)。
三、函数作为对象方法被调用
这种场景,就属于面向对象编程了,this的指向很好分辨,当前方法的调用者即为this的指向。在这个例子里,this指向刚刚创建的Foo的实例对象,因为是通过这个实例对象“点”someMethod()的,也就是说谁“点”了someMethod(),谁就是this。顺便提一下,这样调用是等价的:
四、函数与call,apply,bind结合使用
这个场景是最复杂的,也是几乎所有框架都会使用的技巧。
这三者都属于函数的原型方法,都可以改变函数(方法)的this指向(或者叫做改变执行上下文)。
call与apply更接近,他们会立即执行目标函数;而bind是ES5种新增的标准,用于预处理函数,事先绑定this指向。一图胜千言:
另外,我自己也好奇,如果调用boundAdd
时,也用call会怎么样呢?如果你也一样好奇的话,就自己动手敲一敲代码吧,我也是希望加深你的印象~
另外提一下,call与apply的适用场景。个人认为,apply是比call强大一些的,举个复杂一点的栗子:
如果你能完全理解这个栗子的话,说明你已经对call和apply理解透彻了(其实还包含了闭包的知识点)。
至于bind的话,可以在场景二中这样使用:
总结
关于this的话题,其实还有不少,尤其在面向对象编程的领域,更加重要,需要更加深刻的理解,想要在javascript上有所造诣,这块一定要啃下。
PPS:这是换了Pro之后写的第一篇博客,这屏幕太赞了,回去再看看Air,瞬间变“渣屏”(不过比起一般的windows兼容机还是好得多的)。我用了两年零八个月Air退居二线了,希望新Pro能让我告别卡机死机的日子~科科。