书里面开篇就提到,this并不复杂,只不过被不少程序员加了本身的臆想以讹传讹,说到底,仍是基础知识不熟悉。
的确,看过不少技术文章,分析this都有那种管中窥豹的感受就是着重在举例,论述这个现象,而不求甚解。
闲言少叙,开始总结。程序员
首先说一个重要的技术名词,call-site,咱们平时在debug的时候,可能会接触到callstack这个词,感受上其实有那么一点相似。面试
我理解:
call-stack:是一连串的方法执行的链式结果
call-site:只是上一个调用当前方法上下文环境,能够理解为context。app
好比下面这个片断:oop
function callfirst(){ // call-stack:callfirst // call-site:全局 callsecond(); } function callsecond(){ // call-stack:callfirst -> callsecond // call-site:callfirst } callfirst();
为何说到这个call-site,肯定this对象其实就是找到call-site的过程。this
说到这里,还要再提一个细节,js中的this,不是面向对象中传统的概念,这个this不是放在function中就是这个function的context,也不是任何场合都表明了整个js运行环境中的context,这个this,你就能够理解为是刚才提到的call-site,必须是有依据的context。prototype
下面就总结一下找到call-site的方法,也就是如何正确找到并使用this。(规则我就直接使用原文的副标题)debug
function foo() { console.log( this.a ); } var a = 2; foo();
这里出现this的地方,是foo方法里,咱们先肯定call-site,显而易见,foo方法的call-site就是最后一行foo,隶属于全局对象,那么这个this就呼之欲出了,这个this就表明这个代码的做用域,而this.a访问的也就是var a = 2;这条语句赋值的属性,因此控制台会打印出一个2。
贴一句书中的原文:code
“called with a plain, un-decorated function reference. ”对象
就是说在代码中很简单,没有任何修饰的调用,this就能够理解为全局的做用域对象。
可是这种规则,不适用于strict mode环境下的js代码,若是用在strict mode中,以上代码须要改写成为ci
function foo() { console.log( this.a ); } var a = 2; (function(){ "use strict"; foo(); // 2 })();
function foo() { console.log( this.a ); } var obj = { a: 2, foo: foo }; obj.foo();
这种直译为隐式绑定的方法,肯定call-site的方法就是看是由谁调用的方法,在上面这个例子中,再直白以及明显不过了,obj中有一个foo属性,绑定的foo方法,那么此时foo方法中的call-site就是obj,obj中有一个属性是a,因此代码会输出2
变形:
function foo() { console.log( this.a ); } var obj2 = { a: 42, foo: foo }; var obj1 = { a: 2, obj2: obj2 }; obj1.obj2.foo(); // 42
若是遇到这种链式的风格,就本着就近原则,离foo方法最近的obj2就是foo的call-site,方法中this.a的值就是obj2中a的值。
在Implicit Binding的状况下会有一种叫Implicitly Lost的状况发生,简单直白点说就是刚才那种链式调用的方式,被隐藏在了各类其余的状况之下,举例来讲明。
function foo() { console.log( this.a ); } var obj = { a: 2, foo: foo }; var bar = obj.foo; var a = "oops, global"; bar();
上面这种状况将obj.foo赋值给了bar, 按照惯性思惟,看到obj.foo第一反应,我以为this应该等价于obj,然而事实却哐哐打脸,
上面代码的foo的call-site是bar,虽然链式复制了一大堆给了bar,但实际上,bar在这个时点是等价于foo的,因此这个方法的call-site就是bar,那么this.a的值就是全局属性的a,与obj就不相干了。
与上面状况相同的还有以下几种变种状况:
function foo() { console.log( this.a ); } function doFoo(fn) { fn(); } var obj = { a: 2, foo: foo }; var a = "oops, global"; doFoo( obj.foo );
刨除一切感官上的理解,最终调起foo方法的是fn()这句话,fn的值虽然是由obj.foo传过来的,但其实这种状况与上面说到的方式彻底是等价的
解析一下,fn()就是foo的call-site,而根据第一个default binding原则,fn前面是干净没有任何修饰符的,因此foo中的this表明的就是全局对象。
这里须要强调的就是,链式方法不管是赋值仍是做为方法的参数,不能被长长的语句迷惑双眼,照准call-site是理顺思路的一切法门。
这种链式赋值,this指向问题在js中叫作fall back to default binding。
说到这个显示绑定,就得提到两个方法,一个叫作call,另外一个叫apply,在如今这个时点,咱们暂且理解几个点,这两个方法,是全部function对象均可以调用的内建方法(涉及到prototype),他们的第一个参数,咱们就能够理解为this对象,这是一种强制把this注入到方法中的一种手段。举个例子
function foo() { console.log( this.a ); } var obj = { a: 2 }; foo.call( obj );
仍是熟悉的味道,可是咱们却换了配方,咱们不直接调用foo方法,而是使用foo中的call方法,把obj传到call中做为foo中的this对象,控制台会为咱们输出一个2,call能够换成apply。
foo.apply(obj);
在这种传一个参数做为this对象的功能方面,call与apply是等价的。
这个恐怕也是不少使用js的朋友们最容易混淆的地方,new在js中生成的只是一个function,new过是在一个function前面抢了一个new单词,而这样表示会让function有一些新的变化
大致上有4点:
产生一个新的function对象
这个与原型链有关,暂且不说
new出来的function对象调用的方法是使用的this,就是它自己
除对象自己改变本身自己之外,每次new出来的对象都是全新的对象(这话我再润色一下)
上例子:
function foo(a) { this.a = a; } var bar = new foo( 2 ); console.log( bar.a );
这个比之以前的复杂状况就太浅显了,望文生义便可。
看了以上文章对于解决this面试题应该会有不小的帮助。