無標題文檔

What is "this"? - Part.2

好了,我们 继续上次留下的问题

var x = 10;
var foo = {
    x: 20,
    bar: function () {
        var x = 30;
        return this.x;
    }
};

console.log(
    foo.bar(),             // 1.
    (foo.bar)(),           // 2.
    (foo.bar = foo.bar)(), // 3.
    (foo.bar, foo.bar)()   // 4.
);

我们考虑语句 3. 和上面的两个语句有什么不同

(foo.bar = foo.bar)(), // 3.

相比语句 2.,语句 3. 中的 Grouping Operator 中有赋值(「=」)语句。那么,我们首先得明白赋值语句干了啥,继续 参考对应的 ECMA 文档

11.13.1 Simple Assignment (= )

The production 
    AssignmentExpression : LeftHandSideExpression = AssignmentExpression 

is evaluated as follows:

1. Evaluate LeftHandSideExpression.
2. Evaluate AssignmentExpression.
3.Call GetValue(Result(2)).
4.Call PutValue(Result(1), Result(3)).
5.Return Result(3).

其中,最重要的步骤就是 PutValue,我们 继续刨根问底

8.7.2 PutValue(V, W)

1. If Type(V) is not Reference, throw a 
   ReferenceError exception.
2. Call GetBase(V).
3. If Result(2) is null, go to step 6.
4. Call the [[Put]] method of Result(2), passing GetPropertyName(V) 
   for the property name and W for the value.
5. Return.
6. Call the [[Put]] method for the global object, passing 
   GetPropertyName(V) for the property name and W for the value.
7. Return.

所以,我们根据上面的定义可以得知,语句返回的是 foo.bar 的函数值。因此,赋值操作符返回的是「值(Value)」而不是「引用(Reference)」。

因为函数体需要 this 值获取 x 属性的值,那么接下来我们考虑改函数时调用时的上下文作用域以及背后的具体流程。 尤其注意第七条规则

...
6. If Type(Result(1)) is Reference, Result(6) is GetBase( Result(1)). 
    Otherwise, Result(6) is null.
7. If Result(6) is an activation object, Result(7) is null. Otherwise,
    Result(7) is the same as Result(6).
8. Call the [[Call]] method on Result(3), providing Result(7) as 
    the this value and providing the list Result(2) as the 
    argument values.
…

那么在这种情况下,GetBase 操作实际上返回的是 null,因此此条语句函数执行的作用域为 global ,在浏览器中也就是 window 。

(foo.bar = foo.bar)()

那么,上面的语句中我们可以得知

  1. Grouping Operator 中的赋值语句返回的是 foot.bar 的函数值(「Value」)
  2. 该函数执行的上下文作用域为 window

那么,在该函数中执行获取 this.x 也就是获取 window.x 的值。因此,这条语句返回的就是 10 。如果还不理解,考虑下面的代码段

var x = 10;
(function() {
    return this.x; // 这里会返回什么?
})();

如果理解了上面的语句的前因后果,那么题目中的语句 4. 就能举一反三给推导出来。首先我们来了解逗号运算符(「,」)的定义,我们就可以得之语句

(foo.bar, foo.bar)

返回的也是 foo.bar 的值「Value」而非引用「Reference」,那么接下来的事情其实就是和语句 3. 一样的了。因此,语句 4. 返回的液是 window.x 的值,也就是 10 。

总结下,那么上面的输出总的来说是

20 20 10 10

-- Split --

似乎目前为止,我们已经完全回答出了当初设定的问题。但恐怕会留下疑虑,就是传值「Value」和引用「Reference」之间到低有何不同、函数的作用域以及 this 的指向是否已经真正了解?

是的,这个题目已经完了,而我们的问题似乎还是没有怎麽搞清楚。OK,下次我们来详细讨论下这个问题…

-- To be continued --

What is "this"? - Part.1

呃,这次的标题有些「装逼」,首先我们从道题目开始。

这是道有趣的题目:考虑下面的代码,考虑 console.log 输出的值:

var x = 10;
var foo = {
    x: 20,
    bar: function () {
        var x = 30;
        return this.x;
    }
};

console.log(
    foo.bar(),             // 1.
    (foo.bar)(),           // 2.
    (foo.bar = foo.bar)(), // 3.
    (foo.bar, foo.bar)()   // 4.
);

-- Split --

实际上,题目真正意思求 console.log 中依次四条语句的值。为了方便讲述,上面的语句分别标注为 1-4。

首先,是「1.」这条语句相对来说比较明朗(其实我们也经常这样写)。调用 foo 的 bar 方法,因此 bar 的 this 指向(作用域)为 foo,因此就等于是取 foo 上面的 x 属性(也就是 foo.x)的值,那么语句返回的值就是 20 。

然后是「2.」这条语句。我们可能对 Grouping Operator (也就是「()」)犹豫不决,那 么我们找找 ECMA 中相关定义

11.1.6 The Grouping Operator

The production PrimaryExpression : ( Expression ) is evaluated as follows:

    1. Evaluate Expression. This may be of type Reference.
    2. Return Result(1).

NOTE 

This algorithm does not apply GetValue to Result(1). The principal 
motivation for this is so that operators such as delete and typeof 
may be applied to parenthesised expressions.

因此,由于 foo.bar 是个引用「Reference」,所以使用组操作符 (foo.bar) 返回 的引用是和 foo.bar 是一样的。

如果还不理解,那么考虑下面的表达式:

console.log(foo.bar === (foo.bar))

因此,

(foo.bar)();

返回的也是 foo.x 的值,也就是 20 。

目前为止上面的前两个语句的解释,想必已经了解。细心的读者会发现,其实题目的重点是在 3. 和 4. 两条语句。

要解释后面的两个语句恐怕这篇文章会塞不下,请留给我点喝水的时间,我们下篇继续。

-- To be continued --

PS, Part 2. 在这里

生活、读书、新知

记得中学的时候从书店的角落中不经意翻到本「野史」,自此就对教科书外介绍的另一面历史感到情有独钟。书的名字自然已经不得不_被_遗忘,但书的出版社却已经记得清清楚楚。

由此,喜欢上「野史」的同时,也爱屋及乌得喜欢上这家名叫 三联书店 的图书。同时,它的「生活、读书、新知」这三个词语,也深深得烙进了我的灵魂中。

读书能获得新知,从而充实自己的生活,但现世的浮躁,已经很难让人静下心来慢慢得咀嚼完一本好书。想起在某本书中碰到的段话:

「我们生活在信息爆炸的社会,我们所遇到的问题不
是信息汲取的困难,而大脑无法辨认真正我们想要的
信息。

我们习惯每天上网拿着鼠标面对冰冷的显示器查找我
们所需要的信息,却忽略了我们身边随时可以‘请教’
的老师。」

每天面对 Google Reader 的信息爆炸,甚至是 1000+ 的未读信息,是不是有强迫自己继续读下去生怕自己会遗漏某些重要的信息?

相信我,如果真的有那么多信息,你也不可能全部消化,更不用说找到有用处的信息。那么,你还不如直接 Shift + A 让这些「多余」的信息直接在面前消失,然后去做更加有意义的事情。

是的,我承认曾经我也碰到了这样两难的境地,甚至我怀疑我有强迫症的倾向。

http://pic.yupoo.com/feelinglucky/5260790a5175/medium.jpg

via

A:在我的新闻阅读器中,大概有 300 个种子。
B:什么是新闻阅读器?
A:你不知道 RSS、ATOM 这些玩意?
A:你到现在还不会直接访问他们的站点每个挨个去阅读吧?
B:当然不会,我会把它们打印出来然后吃饭的时候慢慢阅读。
A:好吧,这是你的信仰。

其实上面的可能不仅仅是个笑话。所以,如果你明白能下面的些道理,那么我相信你了解自己需要些什么

  1. 看过的信息不代表就是你的,好记性不如烂笔头
  2. 时间和精力都是有限的,但它们都是资源
  3. 永远不要强迫自己做什么,如果真的需要如此,那么至少努力让自己不会厌恶它
  4. 如果觉得自己没时间,那么尝试在厕所扔本书,即便是杂志也好
  5. 睡觉前半小时不要碰任何电子设备

还是那句话,大道理其实谁都明白,做与不做是另外回事情。

说到书店,顺便说下杭州的书店。其实让人担忧的是, 杭州的书店在逐渐得减少

现在的人们,不是在咖啡吧谈论生意、就是在酒吧讨论风月,因此让人对这些场所已颇感「敬畏从而远之」。而那些「纯粹」的书店 -- 这对于宅男们仅存的宿营地,也正在不断被商业社会淹没。

-- EOF --

我的照片

嗨!我叫「明城」,八零后、码农、宁波佬,现居杭州。除了这里,同时也欢迎您关注我的 GitHubTwitterInstagram 等。

这个 Blog 原先的名字叫 Gracecode.com 、现在叫 「無標題文檔」 。 要知道作为码农取名是件很难的事情,所以不想在取名这事情上太费心思。

作为八零后,自认为还仅存点点可能不怎么被理解的幽默感,以及对平淡生活的追求和向往。 为了避免不必要的麻烦,声明本站所输出的内容以及观点仅代表个人,不代表自己所服务公司或组织的任何立场。

如果您想联系我,可以发我邮件 `echo bWluZ2NoZW5nQG91dGxvb2suY29tCg== | base64 -d`

分类

搜索

文章