连续赋值与求值顺序var a = {n:1};a.x = a = {n:2}; alert(a.x);

代码如下:

<script>
var a = {n:1};
var b = a;
a.x = a = {n:2};
console.log(a.x);// --> undefined
console.log(b.x);// --> [object Object]
</script>

上面的例子看似简单,但结果并不好了解,很容易把人们给想绕了——“a.x不是指向对象a了么?为啥log(a.x)是undefined?”、“b.x不是应该跟a.x是一样的么?为啥log出来居然有2个对象”

当然各位可以先自行理解一下,若能看出其中的原因和工作机理自然就无须继续往下看啦。

下面来分析下这段简单代码的工作步骤,从而进一步理解js引用类型“赋值”的工作方式。

首先是

var a = {n:1};  
var b = a;

在这里a指向了一个对象{n:1}(我们姑且称它为对象A),b指向了a所指向的对象,也就是说,在这时候a和b都是指向对象A的:

这一步很好理解,接着继续看下一行非常重要的代码:

a.x = a = {n:2};

我们知道js的赋值运算顺序永远都是从右往左的,不过由于“.”是优先级最高的运算符,所以这行代码先“计算”了a.x。

这时候发生了这个事情——a指向的对象{n:1}新增了属性x(虽然这个x是undefined的):

从图上可以看到,由于b跟a一样是指向对象A的,要表示A的x属性除了用a.x,自然也可以使用b.x来表示了。

接着,依循“从右往左”的赋值运算顺序先执行 a={n:2} ,这时候,a指向的对象发生了改变,变成了新对象{n:2}(我们称为对象B):

接着继续执行 a.x=a,很多人会认为这里是“对象B也新增了一个属性x,并指向对象B自己”

但实际上并非如此,由于( .  运算符最先计算)一开始js已经先计算了a.x,便已经解析了这个a.x是对象A的x,所以在同一条公式的情况下再回来给a.x赋值,也不会说重新解析这个a.x为对象B的x。

所以 a.x=a 应理解为对象A的属性x指向了对象B:

那么这时候结果就显而易见了。当console.log(a.x)的时候,a是指向对象B的,但对象B没有属性x。没关系,当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止。但当查找到达原型链的顶部 - 也就是 Object.prototype - 仍然没有找到指定的属性B.prototype.x,自然也就输出undefined;

而在console.log(b.x)的时候,由于b.x表示对象A的x属性,该属性是指向对象B,自然也输出了[object Object]了,注意这里的[object Object]可不是2个对象的意思,对象的字符串形式,是隐式调用了Object对象的toString()方法,形式是:"[object Object]"。所以[object Object]表示的就只是一个对象罢了:)

以上纯粹为个人对js引用类型工作方式的理解,若有不对的地方请指出谢谢 :)

原文:http://www.cnblogs.com/vajoy/p/3703859.html

时间: 2024-08-02 04:50:01

连续赋值与求值顺序var a = {n:1};a.x = a = {n:2}; alert(a.x);的相关文章

JS连续赋值与求值顺序

以下代码输出什么? 为什么? var a = {n:1}; var b = a; a = {n:2}; a.x = a ; console.log(a.x); console.log(b.x); var a = {n: 1} var b = a; a.x = a = {n: 2}; console.log(a.x); console.log(b.x) 第一个问题: a.x ---> {n:2,x:a}; b.x ---> undefined; 解答:a的值很清晰了,a第二次赋值以后变成了{n:

C之旅(一)运算符——优先级,结合性和求值顺序

本节主要讲基本运算符的优先级,结合性和求值顺序.先看一个表达式- (1 + 2) * 3 + (4 + 5 * (6 + 7 ))记住你对它的运算过程,看完下面的内容之后,也许你会用不同的方式来看待这个表达式. 基本运算符 = + - * / C中没有指数运算.运算符操作的是操作数,操作数就是放在运算符两侧的东西. (1) 赋值运算符 = year = 2016; 读作将值2016赋给year,而非year等于2016.=将2016赋给变量year,是从右到左的,即=具有右结合性.2016 =

C语言对表达式的求值顺序不是明确规定的

讨论区看到的 WA来自那些递归下降求解的代码. 第一种情况,使用|| 和 &&: 例如s为所给串 int getval() { switch(s[c_s++]) { case 'p': return (value & (1 << 0))? 1:0; case 'q': return (value & (1 << 1))? 1:0; case 'r': return (value & (1 << 2))? 1:0; case 's'

c++中函数参数的求值顺序

c++中如果函数的参数列表包含多个实参,那么对参数的求值顺序是不确定的. 在谭浩强的<C++程序设计>(第二版)P94中提到,GCC对参数求值是按从右到左的顺序求值的. 但我实测并非如此. #include <iostream> #include <iomanip> using namespace std; void test(int x, int y) { cout << 'x' << x << " y" <

Golang语句中的求值顺序

在Go specs中,有这样三点陈述: 1.变量声明(variable declaration)中的初始化表达式(initialization expressions)的求值顺序(evaluation order)由初始化依赖(initialization dependencies)决定:但对于初始化表达式内部的操作数的求值需要按照2中的顺序:从左到右: 2.在非变量初始化语句中,对表达式.赋值语句或返回语句中的操作数进行求值时,操作数中包含的函数(function)调用.方法(method)调

C++求值顺序

<C++Primer5th>中文版第124页 C++语言没有明确规定大多数二元运算符的求值顺序, 给编译器优化留下了余地. 这种策略实际上是在代码生成效率和程序潜在缺陷之间进行了权衡,这个是否可以接受? 1.首先可以知道优先级规定了运算对象的组合方式,但是没有说明运算对象按照什么顺序求值. 比如: int i=f1()*f2(); 在这里虽然f1和f2在乘法之前被调用,但是f1先调用还是f2先调用却不得而知. 2.再比如结合律: -- int i=0; cout<<i<<

表达式的求值顺序

代码: #include <stdio.h> #include <stdlib.h> #include <stdbool.h> // C语言保证逻辑表达式是从左至右求值 int main(void) { // printf("Left") == 4 // printf("Right") == 5 if (!printf("Left") && printf("Right")) 

二元运算符求值顺序问题

对于没有指定顺序的运算符来说,如果表达式指向并修改了同一对象,将会引发错误并产生未定义行为.如: int i = 0: cout << i << " " << ++i; // 未定义 经验: 拿不准时用括号 如果改变了一个运算对象的值,在表达式的其他地方就不要在使用这个运算对象(当改变运算对象的子表达式本身就是另外一个子表达式的运算对象时,是可以的 如:*++iter

关于C/C++ 表达式求值顺序 未定义

http://blog.csdn.net/zhongjiekangping/article/details/5164132 这篇文章讲的真好! 对于 "expr1 + expr2",(其中expr1,expr2都可能是复合表达式),不同的编译器想先算exp1就先算exp1,想先算exp2就先算exp2.而且,编译器不必保证exp1计算完毕之后才去计算expr2,它可以这么做,也可以不这么做.C语言标准里并不做规定. 一段正常的程序不应该由于加号前后的求解顺序不同而得到不同的结果.因此,