引入

今天逛园子的时候看到一道javascript面试题,是关于连续赋值的,正好最近读jQuery源码经常看到这种连续赋值的表达式,所以很感兴趣。

废话不多说,来看题:

var a = {n: 1}
var b = a;
a.x = a = {n: 2}
console.log(a.x);
console.log(b.x)

答案:

console.log(a.x); // undefined
console.log(b.x) //{n:2}

看到这个答案,我真是百思不得解。。。。 于是网上搜了搜,整理如下:

以下转自:http://www.iteye.com/topic/785445

1、引用(Reference)与GetValue & PutValue

引用
A Reference  is a reference to a property of an object. A Reference consists of two components, the base object and the property name.

“引用”是引用某个对象的一个属性(可能这个对象并没有这个属性),一个引用含“根对象”与“属性名”两个成员。 
后面以“(根对象,属性名)”来表达一个引用

引用
GetValue (V) 
1. If Type(V) is not Reference, return V. 
2. Call GetBase(V). 
3. If Result(2) is null, throw a ReferenceError exception. 
4. Call the [[Get]] method of Result(2), passing GetPropertyName(V) for the property name. 
5. Return Result(4).

GetValue,即取值操作,返回的是确定的值,而不是引用。(可以理解为变量与变量的值,或指针与指针指向的对象)

引用
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.

PutValue操作只对引用生效,在ECMAScript的描述中,修改对象的属性都是通过Refrence + PutValue进行的 
(ECMAScript是为了便于表达而引入Reference这个类型,实际上JS语言中并无此类型。The internal Reference type is not a language data type. It is defined by this specification purely for expository 
purposes.)

2、成员表达式(MemberExpression)解释过程

引用
The production MemberExpression : MemberExpression [ Expression ] is evaluated as follows: 
1. Evaluate MemberExpression. 
  53 
2. Call GetValue(Result(1)). 
3. Evaluate Expression. 
4. Call GetValue(Result(3)). 
5. Call ToObject(Result(2)). 
6. Call ToString(Result(4)). 
7. Return a value of type Reference whose base object is Result(5) and whose property name is Result(6).

着重看第7步:a value of type Reference

3、赋值表达式解析

引用
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).

这里可以看到左侧得出的是引用,右侧调用GetValue取得的是确定值。

那么开始分析a.b = a = {n:2}这个表达式,先假设{n:1}这个对象为OBJ1,{n:2}为OBJ2,全局为GLOBAL。

它的解析如下: 
a.b = Expression1 
Expression1为另一个赋值表达式: 
a = {}

首先计算a.b = Expression1,按(3)中赋值表达式运行步骤 
step1先得到引用(OBJ1, "b") 
step2解析Expression1{ 
   Expression1解析 
   step1得到引用(GLOBAL, "a") 
   step2得到一个对象OBJ2 
   step3取值,仍是OBJ2 
   step4将引用(GLOBAL, "a")赋值为step3结果 
   step5返回OBJ2 

step3取值,结果同样为OBJ2 
step4将(OBJ1, "b")赋值为OBJ2 
step5返回OBJ2

最终结果: 
OBJ1: {n:1, b:OBJ2} 
OBJ2: {n:2} 
a : OBJ2

PS: 
我们常说赋值运算是从右至左,是指右边先结合 
所以a.b = a = {n:2}解析为了a.b = ( a = {n:2}),而不会解析为(a.b = a) = {n:2} 
如果理解为右边先运算就会有误解了,虽然右边先赋值成功。

----------------------------分割线---------------------------

附上ECMA262文档:ECMA262

(转)深入理解javascript连续赋值表达式的更多相关文章

  1. 深入理解javascript系列(4):立即调用的函数表达式

    本文来自汤姆大叔 前言 大家学JavaScript的时候,经常遇到自执行匿名函数的代码,今天我们主要就来想想说一下自执行. 在详细了解这个之前,我们来谈了解一下“自执行”这个叫法,本文对这个功能的叫法 ...

  2. [JS]深入理解JavaScript系列(4):立即调用的函数表达式

    转自:汤姆大叔的博客 前言 大家学JavaScript的时候,经常遇到自执行匿名函数的代码,今天我们主要就来想想说一下自执行.在详细了解这个之前,我们来谈了解一下"自执行"这个叫法 ...

  3. 深入理解javascript:揭秘命名函数表达式

    这是一篇转自汤姆大叔的文章:http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html 前言 网上还没用发现有人对命名函数表达式进去重复深 ...

  4. <深入理解JavaScript>学习笔记(2)_揭秘命名函数表达式

    写在前面的话 注:本文是拜读了 深入理解JavaScript 之后深有感悟,故做次笔记方便之后查看. 感觉这章的内容有点深奥....略难懂啊. 先坐下笔记,加深一下印象吧. 我主要记一下自己感觉有用的 ...

  5. 深入理解JavaScript系列(2):揭秘命名函数表达式

    前言 网上还没用发现有人对命名函数表达式进去重复深入的讨论,正因为如此,网上出现了各种各样的误解,本文将从原理和实践两个方面来探讨JavaScript关于命名函数表达式的优缺点. 简单的说,命名函数表 ...

  6. 深入理解JavaScript系列(2):揭秘命名函数表达式(转)

    前言 网上还没用发现有人对命名函数表达式进去重复深入的讨论,正因为如此,网上出现了各种各样的误解,本文将从原理和实践两个方面来探讨JavaScript关于命名函数表达式的优缺点. 简 单的说,命名函数 ...

  7. 深入理解JavaScript的闭包特性如何给循环中的对象添加事件

    初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件.在事件响应函数中(event handler)获取对应的索引.但每次获取的都是最后一次循环的索引.原因是初学者并未理解JavaScript ...

  8. 深入理解javascript原型和闭包(8)——简述【执行上下文】上

    什么是“执行上下文”(也叫做“执行上下文环境”)?暂且不下定义,先看一段代码: 第一句报错,a未定义,很正常.第二句.第三句输出都是undefined,说明浏览器在执行console.log(a)时, ...

  9. 深入理解javascript原型和闭包(9)——简述【执行上下文】下

    继续上一篇文章(http://www.cnblogs.com/wangfupeng1988/p/3986420.html)的内容. 上一篇我们讲到在全局环境下的代码段中,执行上下文环境中有如何数据: ...

随机推荐

  1. Memcached 数据缓存系统

    Memcached 数据缓存系统 常用命令及使用:http://www.cnblogs.com/wayne173/p/5652034.html Memcached是一个自由开源的,高性能,分布式内存对 ...

  2. C#实现任意大数的计算和简单逻辑命题的证明——前言

    介绍 这是本人毕业设计的项目,一直想将其整理成文,可一不小心4年就过去了(这个时间又可以读个大学了).现在给自己定一个目标,一个月时间里将项目的所有关键点都整理出来.不然真怕一眨眼又一个4年过去了,而 ...

  3. 使用图灵机器人API实现聊天机器人

    使用图灵机器人的API需要先注册,获取key才行,这我就不说了,自己到http://www.tuling123.com/注册一个账号即可. 下面就是一个简单的python调用API实现聊天机器人的简易 ...

  4. double精度问题

    一个Double值由2个Double相加,比如明明是91.2 + 2.4,结果应为93.6,为什么结果是93.6000000000001 为什么会无端地在小数点后面加很多个0,最后还是一个1 ? 计算 ...

  5. ios启动载入启动图片

    版本判断: 1.首先你要知道这个键值对的key:id key =   (id)kCFBundleVersionKey; 2.同过本地的NSBundle取得当前的版本号. 3.在沙盒中取得对应的版本号. ...

  6. C#中属性与字段的用法

    //People.cs public class People { //字段 private string _name; //属性 作用:保护字段,对字段的取值和赋值进行限定,限制非法字段的摄入 pu ...

  7. jsoncpp初使用

    一 前言 由于最近一个c++项目需要使用json这种数据格式来传输数据, so上网去寻找合适的类库,毕竟对于这种不是很新的技术, 自己造轮子有点得不偿失. 从百度上翻了翻, 基本上就boost跟jso ...

  8. LeetCode 350. Intersection of Two Arrays II

    Given two arrays, write a function to compute their intersection. Example:Given nums1 = [1, 2, 2, 1] ...

  9. Hive On Spark hiveserver2方式使用

    启动hiveserver2: hiveserver2 --hiveconf hive.execution.engine=spark spark.master=yarn 使用beeline连接hives ...

  10. 细说IIS异常日志 — 你必须知道的功能

    最近在跟QAD用Webservice搞接口做数据维护,搞的哥那个叫头大,遇到很多问题,系统的log4net根本就无法记录.话说QAD调我某一个接口,可能包含几百个字段,而且QAD是个产品,所以我这边提 ...