- 浏览: 1163374 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
zhizhen23:
LZ 提供的链接地址失效了
重写的isPlainObject方法 -
LovingBaby:
LovingBaby 写道function fun() {}f ...
读jq之二(两种扩展) -
LovingBaby:
说的很清楚!jQuery作者为了实现简洁调用的苦心!高超的编程 ...
读jq之一(jq对象的组成) -
hard_ly:
...
将伪数组转换成数组 -
zlxzlxzlxzlxzlx:
这不能算是任意进制之间的转换,例如二十六进制、十二进制又该如何 ...
用递归实现十进制数转换N进制
很喜欢蔡蔡 的这个标题,实际蔡蔡已经分析过了,这里借用了。或许有点标题党的意思。看完就知了。
一、引子
var a = {n:1}; a.x = a = {n:2}; alert(a.x); // --> undefined
这是蔡蔡在看jQuery源码 时发现这种写法的。以上第二句 a.x = a = {n:2} 是一个连续赋值表达式。这个连续赋值表达式在引擎内部究竟发生了什么?是如何解释的?
二、猜想
猜想1:从左到右赋值,a.x 先赋值为{n:2},但随后 a 赋值为 {n:2},即 a 被重写了,值为 {n:2},新的 a 没有 x属性,因此为undefined。步骤如下
1, a.x = {n:2};
2, a = {n:2};
这种解释得出的结果与实际运行结果一致,貌似是对的。注意猜想1中 a.x 被赋值过。
猜想2:从右到左赋值,a 先赋值为{n:2},a.x 发现 a 被重写后(之前a是{a:1}),a.x = {n:2} 引擎限制a.x赋值,忽略了。步骤如下:
1, a = {n:2};
2, a.x 未被赋值{n:2}
等价于 a.x = (a = {n:2}),即执行了第一步,这样也能解释a.x为undefined了。注意猜想2中a.x压根没被赋值过。
三、证明
上面两种猜想相信多数人都有,群里讨论呆呆认为是猜想1, 我认为是猜想2。其实都错了。我忽略了引用的关系。如下,加一个变量b,指向a。
var a = {n:1}; var b = a; // 持有a,以回查 a.x = a = {n:2}; alert(a.x);// --> undefined alert(b.x);// --> [object Object]
发现a.x仍然是undefined,神奇的是 b.x 并未被赋值过(比如:b.x={n:2}),却变成了[object Object]。b 是指向 a({n:1})的,只有a.x = {n:2}执行了才说明b是有x属性的。实际执行过程:从右到左,a 先被赋值为{n:2},随后a.x被赋值{n:2}。
1, a = {n:2};
2, a.x = {n:2};
等价于
a.x = (a = {n:2});
与猜想2的区别在于a.x 被赋值了,猜想2中并未赋值。最重要的区别,第一步 a = {n:2} 的 a 指向的是新的对象{n:2} , 第二步 a.x = {n:2} 中的 a 是 {a:1}。即在这个连等语句
a.x = a = {n:2};
a.x 中的a指向的是 {n:1},a 指向的是 {n:2}。如下图
四:解惑
这篇写完,或许部分人看完还是晕晕的。因为里面的文字描述实在是绕口。最初我在理解这个连等赋值语句时
var a = {n:1}; a.x = a = {n:2};
认为引擎会限制a.x的重写(a被重写后),实际却不是这样的。指向的对象已经不同了。引擎也没有限制a.x={n:2}的重写。
谢谢所有参与讨论的人:蔡蔡、猪大肠
、呆呆、雅儒。这个问题最早是蔡蔡提出的。雅儒在 菜鸟灰呀灰 群里每次的讨论都那么投入,认真,哪怕是别人提出的话题。
五:结束
呵,以另一个连续赋值题结束。fun执行后,这里的 变量 b 溢出到fun外成为了全局变量。想到了吗?
function fun(){ var a = b = 5; } fun(); alert(typeof a); // --> undefined alert(typeof b); // --> number
评论
a只是一个指针,只能指向一个地址
var a = {n:1}; a.x = a = {n:2}相当于是先 {n:1}.x = {n:2},再a = {n:2},不然如果先把a指向了{n:2},那样a.x就已经不是{n:1}.x,而是{n:2}.x,于是根本不会把x赋值到{n:1}了。
a只是一个指针,不是对象,对a.x赋值改的是{n:1}.x的值,而对a赋值是改的a指针的值。
var a,b,c,d; a=b=c=d={a:1}; a.x=a=b.y=b=c.z=c={} console.log(a,b,c,d)
和结果:
我觉得是这样的,执行这句话的时候,发现有a.x b.y c.z,虚拟机会先把地址准备好,也就是虚拟机要做这件事:
先分别找a.x、a、b.y、b、c.z、c的地址,这时,a.x,b.y,c.z 分别指向旧对象内部的地址,而a,b,c分别为存放对象指针的地址,当赋值时,会将所有这些地址的值都赋值成赋值语句最右面的值。但是由于a.x,b.y,c.z分别指向旧对象的x、y、z域,故这几个值是赋给了就对象,即d,而a、b、c分别赋予了新值,即空对象,所以可以看到如图的结果
纯属推测,也许不是地址,是一个临时的setter也不一定,总之是虚拟机内部实现的。
这帖子里的问题和这个是一样的问题吧。
function fun(){ var a = b = 5; } fun(); alert(typeof a); // --> undefined alert(typeof b); // --> number
其中
var a=b=5;
只是声明了局部变量a,而b并未声明,对于未声明的变量直接在js中赋值就会当做全局变量。
所有b本来就可以在外部读到。
也就是说最后赋值的那个a,没有地方引用,所以alert(a.x);才会是未定义
LZ写了这么多也没明白。看这一句就明白了。。
1. a={n:2};
2. a.x={n:2}; //此时的a为{n:1},即{n:1}.x={n:2};
所以alert(a.x)为undefined, 因为此时的a已经指向{n:2}, 没有为它的x属性赋值。
var a=b=1;
运行时会报错,怎么都有点想不明白,试着尝试运行了一下,发现并没有出错提示,有点不甘心,看到
var a = {n:1};
a.x = a = {n:2};
alert(a.x); // --> undefined
感觉原理有点类似, 遂改写了一下
var a;
a.x = a = {n:2};
alert(a.x);
居然真的报错了!看了上面各位的解释,这里在做一点小小补充,以便新手能够理解:
1.赋值运行算的顺序是从右到左的;
2.JS垃圾回收不会在语句执行时进行.
a.x = a = {n:2}; 这条语句执行过程猜得没错的话应该是
1.查找a并得到a的引用
2.查找a.x发现未定义就重新分配内存并得他的引用
3.再次查找a并得到a的引用
4.给{n:2}分配内存并得到他的引用
5.将新a的引用指向{n:2}的引用
//注意:这里本来应该回收旧a及旧a的x,但因为执行完这条语句后才能GC,所以尚未销毁a,即第2步中的旧a.x的引用在内存中仍然存在
6.将旧a的x指向新的a的引用
7.垃圾回收旧a及旧a的x
个人见解,难免有误,欢迎指正.
int i = 0; i = i++; System.out.println("i = " + i);//i = 0;
这段代码分如下步骤执行
(1)i = 0 ,读取变量i的地址,把0的值赋给i;
(2)i = i ++ ,因为=的优先级高于++,所以先执行赋值运算符=,编译器读取右边i的地址
(3)读取左边的i的地址(编译器并不认为右边i的地址等于左边的i的地址)
(4)右边i++
(5)打印左边的i值(因为编译器拿到的是左边的i的值)
可以看出编译器按下面处理
int i = 0;
int j = 0;//
j = i ++ ;
System.out.println("j = " + j);
其实,我认为上面的代码也是一样的
var a = {n:1}; a.x = a = {n:2}; alert(a.x);
a.x = a = {n:2};左边a.x中的a和右边的a代表两个不同的地址,解释器如下处理;
var a = {n:1}; var b = {n:1}; b.x = a = {n :2}; alert(b.n); alert(a.n); alert(a.x);
var a = {n:1}; a.x = a = {n:2};//a.x中的a指向的是 {n:1}, alert(a.n);//2 ,证明解释器拿到的是 a = {n:2}; alert(a.x);//
var a=[1,2,3]
alert(a[2]) //3 因为数组有这个值a[2]所以当然要返回了
a[2]=a=[] // ?
alert(a[2]) //undefined
//常规思维
//如果要给变量赋值哪么首先是确定变量本身的属性,比如要给a[2]赋值
//首先计算a[2]之前是什么,如果没有定义a是一个数组哪么此时脚本就会直接报错
//因为 var a=[1,2,3]
//既然此时确定了a[2]的值是3
//a[2]=3
//哪么往下走:从右到左,先执行a=[]此时的a是一个空数组[],也就是改变了
//原始定义的var a=[1,2,3]值的变量定义值a=[]
//接着往右赋值a[2]=[],也就是说此时的a[2]不管值是多少,是继承自var a=[1,2,3]
//而不是a=[]
//因为a=[]没有a[2]这个元素, 此时a[]既然没有a[2]这个元素哪么
//a[2]==undefined相当于:
var a=[]
a[2]=? //没有值,当然返回的是undefined
a.x = a = {n:2}; //把a的值赋成 {b:2};
//然后要把a.x的值 赋成a也就是({b:2}); 但a中没有x这个 所以undefined
//第二句改成a.x = a = {n:2,x{}};
//便会得出 a.x= object a.x.n=2;
//{n:1}; 被覆盖了...
alert(a.x); // --> undefined
测试了好几便终于搞明白了;
用下面的代码验证一下,就明白由了...
function init(){ var a = {n:1,x:{}}; var b alert("a.x="+ a.x); alert(b); a.x = b = {n:2}; alert (a.x) alert("a.x.n=" +a.x.n); alert("b.n=" +b.n) }
这种写法在新发布的jQuery1.4.3中1833行
elemData[ eventKey ] = elemData = function(){};
至于巧妙或价值可联系jQuery作者:http://ejohn.org/blog/ 。
b在函数内就不是一个局部变量。。默认就是全局变量。 执行后变量也就有值了。。
结合性和求值顺序是没有必然关系的。
JavaScript的表达式的求值顺序都是从左向右的。赋值运算的结合性虽然是右结合,但同样是从左向右求值的。看ECMAScript 5的附录D,
这里的描述说明ECMAScript是使用从左向右的求值顺序
AssignmentExpression : ConditionalExpression LeftHandSideExpression AssignmentOperator AssignmentExpression
这条语法规则定义了赋值运算符是右结合的。怎么看出来呢?
首先要能读懂ECMAScript规范里语法的记法。在冒号左边的是语法规则的名字,右边的是规则的推导内容。推导内容中,在同一行上的属于同一条子规则,在不同行上的属于不同子规则;不同子规则之间是“或”的关系。
上面的语法规则的意思是:
一个“赋值表达式”,
可以由一个“条件表达式”构成;
或者可以由一个“左手边表达式”加上一个“赋值运算符”加上一个“赋值表达式”构成。
如果在一条语法规则里,推导内容中出现了该规则自身,则这条规则是“直接递归”的。如果自身出现的位置在推导内容某条子规则的最左边,则为“左递归”,出现在最右边则为“右递归”。运算符结合性也正好在此体现:左递归的规则意味着左结合,右递归的规则意味着右结合。
可以看到,ECMAScript的赋值表达式的语法是右递归的,因而是右结合的。
这两者对程序执行有什么影响呢?前面clue的解答已经对路了。有兴趣看结合性、优先级和求值顺序的关系的例子的请参考我之前的一帖,虚拟机随谈(一):解释器,树遍历解释器,基于栈与基于寄存器,大杂烩,中间讲到抽象语法树的时候有举例。Java与C#同JavaScript一样,是用从左向右的求值顺序,而赋值运算是右结合的,所以那帖的例子也可以用来帮助理解JavaScript的状况。
这个与结合性是没有关系的。别乱想了。
只与解释器的进栈地址有关。
发表评论
-
JavaScript获取图片的原始尺寸
2016-04-20 10:30 1595页面里的img元素,想要 ... -
JavaScript中奇葩的假值
2016-03-14 17:43 1159通常在以下语句结构中需要判断真假 if分支语句 whi ... -
世界上最短的数字判断代码
2016-03-14 16:21 1581我们知道JavaScript提供了typeof运算符,因此最 ... -
getBoundingClientRect在IE9/10里的bug
2015-01-12 08:30 1651getBoundingClientRect可以获得页面中某个 ... -
JavaScript中的直接量与初始器的区别
2014-11-08 06:09 1629很多代码优化及公司规范都会提到 写对象不应该 var ... -
JavaScript中的不可见数据类型
2014-11-02 11:08 1291JS提供了一些内置对象、函数和构造器供我们编程,如Math ... -
ES5严格模式
2014-10-08 18:20 1253严格模式(Strict mode)是由ECMA-262规范 ... -
JavaScript生成GUID的算法
2014-07-16 14:25 3014全局唯一标识符(GUID,Globally Unique ... -
JavaScript中点号“.”的多义性
2014-06-07 19:42 1657点号「.」在JavaScript中 ... -
冗余换性能-从Backbone的triggerEvents说开了去
2014-02-19 11:03 952Backbone是一个优秀的前端MVC库,它的代码质量必定 ... -
JavaScript中delete操作符不能删除的对象
2013-11-27 13:21 1307ES3 中,delete在8.6.2.5及11.4.1有介 ... -
JavaScript中instanceof对于不同的构造器可能都返回true
2013-11-19 11:13 1213我们知道 instanceof 运算符用来检查对象是否为某 ... -
JavaScript里模拟sleep
2013-10-16 07:52 1545有几种方式,但都不完美 一、不断循环,直到达到指定时间 ... -
参数有中包含空格且使用Post提交时须将空格转换成加号
2013-10-14 08:07 5207jQuery的serialize模块中有个r20正则 ... -
JavaScript中“基本类型”之争
2013-10-04 20:58 1124前端面试中常被问到的问题之一就是“JavaScript的基本 ... -
ES3和ES5关于计算顺序的问题(ES5先计算函数ES3则是参数)
2013-09-13 23:35 1313从cmc那看到的,分享给园友。以下是一个怪异的代码,谁 ... -
一道关于"/g"笔试题
2013-07-26 07:13 1055正则里“g”表示全局(global)的意思,比如当替换字符串 ... -
JavaScript原型继承的陷阱
2013-05-27 20:51 1396JavaScript默认采用原型 ... -
JavaScript中__proto__与prototype的关系
2013-05-21 10:38 1458这里讨论下对象的内 ... -
JavaScript中__proto__与prototype的关系
2013-05-21 10:01 3这里讨论下对象的内 ...
相关推荐
这说明了JavaScript中的赋值运算的右结合性如何影响连续赋值。在编写连续赋值代码时,开发者需要考虑到这一点,以避免因理解错误而导致的bug。 此外,当涉及到对象和属性的赋值时,还应特别注意JavaScript中对象...
在本项目中,我们将探讨如何使用HTML5、CSS3和JavaScript技术来创建一个简单的四则运算计算器。这个计算器能够执行基本的加法、减法、乘法和除法操作,对于初学者来说是一个很好的实践项目,它能帮助理解前端开发的...
在"javascript input赋值"这个压缩包文件中,可能包含了一些示例代码或者教程,用于演示如何在实际项目中应用这些技术。通过学习和理解这些示例,开发者能够更好地掌握JavaScript中对input元素的操作,提高前端开发...
在JavaScript中,连续赋值是一种常见的操作,它允许开发者将一个值赋给多个变量。通常情况下,连续赋值不会引起混淆,但是当涉及对象和引用时,就可能出现一些不易察觉的陷阱。了解这些陷阱和连续赋值的内部机制对于...
加法(+)、减法(-)、乘法(*)和除法(/)是基础,但为了支持括号和更复杂的运算,我们需要了解优先级和运算顺序。在JavaScript中,括号用于改变运算顺序,表达式`a * (b + c)`会先计算括号内的`b + c`,然后将...
此外,JavaScript还支持复合赋值运算符,如`+=`, `-=`等,它们可以简化代码并进行连续运算: ```javascript a += b; // 等价于 a = a + b a -= b; // 等价于 a = a - b ``` 在HTML5中,我们可以利用`<canvas>`元素...
此函数是我自己写的,虽然在网上可以搜到很多,不过我找到的都是在算法中存在基本的浮点数的运算,导致结果仍然是错误的。由于刚刚学写JS,所以可能考虑不够周全,望大家批评指正。 代码中加了四舍五入函数,是网上...
JavaScript是一种广泛应用于网页和网络应用开发的脚本语言,它主要负责处理客户端的交互逻辑,为用户提供...通过这个项目,你可以深入了解浏览器环境下的JavaScript运行机制,以及如何利用JavaScript提高网页的互动性。
在构建Web应用时,PHP、JavaScript和HTML之间的变量赋值和传递是至关重要的。这篇文章主要讲解了如何在这些技术之间进行数据交互。 首先,我们来看HTML超链接如何传递值。在HTML中,我们可以使用`<a>`标签创建链接...
JavaScript是一种广泛应用于Web开发的脚本语言,它在处理四则运算时提供了多种方式,包括基本的算术运算符和位运算符。本篇将详细探讨如何利用位移操作实现四则运算,以及这些方法在实际编程中的应用。 在...
JavaScript语法基于ECMAScript规范,包括变量声明(var、let、const)、数据类型(如字符串、数字、布尔值、对象、数组等)、运算符(算术、比较、逻辑、位运算等)、流程控制(条件语句、循环结构)以及函数等。...
**JavaScript全方位解析** JavaScript,简称JS,是一种广泛应用于网页和网络应用的脚本语言,尤其在前端开发领域占据着核心地位。它与HTML和CSS一起,构成了网页开发的三驾马车,使得网页具备交互性、动态性和实时...
JavaScript是一种广泛应用于Web开发的脚本语言,由Netscape公司的Brendan Eich在1995年发明。它最初被命名为LiveScript,后来为了与Sun Microsystems的Java语言搭上关系,更名为JavaScript,但实际上两者并无直接...
本文主要探讨了JavaScript中几个易出错的操作符类别,包括算术运算符、比较运算符、逻辑运算符以及赋值和一元运算符的运算顺序和优先级。 首先,我们来看算术运算符。在JavaScript中,有些特殊的字面量值,如`NaN`...
javascript float运算会有误差,可以用此脚本中的函数进行修补。
JavaScript中的数学运算主要涵盖基本算术运算和使用Math对象的高级计算。JavaScript支持常见的加法(+), 减法(-), 乘法(*), 除法(/)以及取模(%)运算符。此外,它还提供了处理特殊计算结果的能力,如正负Infinity和NaN...
10. 表达式运算:Javascript中的表达式运算可以是算术运算、比较运算、逻辑运算等,例如`a++==b?a:b`的结果是根据条件判断的结果而定的。 11. Radio按钮默认选中:在Html中,可以使用checked属性来设置Radio按钮的...
JavaScript10源码大全是一个关于JavaScript编程语言的资源集合,主要针对那些想要深入理解JavaScript核心概念、函数以及工作机制的开发者。这个压缩包可能包含了多个示例、教程、代码片段,甚至是完整的项目源码,...