- 浏览: 1163393 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
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中的数学运算主要涵盖基本算术运算和使用Math对象的高级计算。JavaScript支持常见的加法(+), 减法(-), 乘法(*), 除法(/)以及取模(%)运算符。此外,它还提供了处理特殊计算结果的能力,如正负Infinity和NaN...
10. 表达式运算:Javascript中的表达式运算可以是算术运算、比较运算、逻辑运算等,例如`a++==b?a:b`的结果是根据条件判断的结果而定的。 11. Radio按钮默认选中:在Html中,可以使用checked属性来设置Radio按钮的...
JavaScript10源码大全是一个关于JavaScript编程语言的资源集合,主要针对那些想要深入理解JavaScript核心概念、函数以及工作机制的开发者。这个压缩包可能包含了多个示例、教程、代码片段,甚至是完整的项目源码,...
在编写代码时我们有时候会碰到需要自己解析四则运算表达式的情况,本文简单的介绍使用JavaScript实现对简单四则运算表达式的解析。 一、熟悉概念 中缀表示法(或中缀记法)是一个通用的算术或逻辑公式表示方法, ...