资讯专栏INFORMATION COLUMN

【YDKJS读书笔记】关于Js中的this Part1

siberiawolf / 1205人阅读

摘要:贴一句书中的原文就是说在代码中很简单,没有任何修饰的调用,就可以理解为全局的作用域对象。这种链式赋值,指向问题在中叫做。在这种传一个参数作为对象的功能方面,与是等价的。看了以上文章对于解决面试题应该会有不小的帮助。

书读到"this & object prototype"这一卷。 章节到了“this All Makes Sense Now!”

书里面开篇就提到,this并不复杂,只不过被很多程序员加了自己的臆想以讹传讹,说到底,还是基础知识不熟悉。
的确,看过很多技术文章,分析this都有那种管中窥豹的感觉就是着重在举例,论述这个现象,而不求甚解。
闲言少叙,开始总结。

首先说一个重要的技术名词,call-site,我们平时在debug的时候,可能会接触到callstack这个词,感觉上其实有那么一点类似。

我理解:
call-stack:是一连串的方法执行的链式结果
call-site:只是上一个调用当前方法上下文环境,可以理解为context。

比如下面这个片段:

function callfirst(){
    // call-stack:callfirst
    // call-site:全局
    callsecond();
}
function callsecond(){
    // call-stack:callfirst -> callsecond
    // call-site:callfirst
}
callfirst();

为什么说到这个call-site,确定this对象其实就是找到call-site的过程。

说到这里,还要再提一个细节,js中的this,不是面向对象中传统的概念,这个this不是放在function中就是这个function的context,也不是任何场合都代表了整个js运行环境中的context,这个this,你就可以理解为是刚才提到的call-site,必须是有依据的context。

下面就总结一下找到call-site的方法,也就是如何正确找到并使用this。(规则我就直接使用原文的副标题)

1.Default Binding:看看如下代码片段
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。
贴一句书中的原文:

called with a plain, un-decorated function reference.

就是说在代码中很简单,没有任何修饰的调用,this就可以理解为全局的作用域对象。
但是这种规则,不适用于strict mode环境下的js代码,如果用在strict mode中,以上代码需要改写成为

function foo() {
    console.log( this.a );
}

var a = 2;

(function(){
    "use strict";

    foo(); // 2
})();
2.Implicit Binding:还是看代码
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。

3.Explicit 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是等价的。

4 new Binding

这个恐怕也是很多使用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面试题应该会有不小的帮助。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/82805.html

相关文章

  • JavaScript设计模式之发布-订阅模式(观察者模式)-Part1

    摘要:设计模式与开发实践读书笔记。发布订阅模式又叫观察者模式,它定义了对象之间的一种一对多的依赖关系。附设计模式之发布订阅模式观察者模式数据结构和算法系列栈队列优先队列循环队列设计模式系列设计模式之策略模式 《JavaScript设计模式与开发实践》读书笔记。 发布-订阅模式又叫观察者模式,它定义了对象之间的一种一对多的依赖关系。当一个对象的状态发生改变时,所有依赖它的对象都将得到通知。 例...

    muzhuyu 评论0 收藏0
  • JS

    摘要:栈学习数据结构与算法读书笔记。栈又名堆栈,是一种遵循后进先出原则的有序集合。新添加或待删除的元素都保存在栈的末尾,称作栈顶,另一端称作栈底。在栈里,新元素都靠近栈顶,旧元素都接近栈底。 栈 《学习JavaScript数据结构与算法》读书笔记。 栈(stack)又名堆栈,是一种遵循后进先出(LIFO)原则的有序集合。新添加或待删除的元素都保存在栈的末尾,称作栈顶,另一端称作栈底。在栈里,...

    Lin_R 评论0 收藏0
  • JavaScript设计模式之发布-订阅模式(观察者模式)-Part2

    摘要:设计模式与开发实践读书笔记。看此文章前,建议先看设计模式之发布订阅模式观察者模式在中,已经介绍了什么是发布订阅模式,同时,也实现了发布订阅模式。 《JavaScript设计模式与开发实践》读书笔记。 看此文章前,建议先看JavaScript设计模式之发布-订阅模式(观察者模式)-Part1 在Part1中,已经介绍了什么是发布-订阅模式,同时,也实现了发布-订阅模式。但是,就Part1...

    Charlie_Jade 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<