纠结的连等赋值

  偶的看到一段有意思的代码:

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

  作为一个热衷于“钻牛角尖”的人,楼主对这样的代码很感兴趣,也不禁陷入了思考。so也不要说写这样的代码难维护啥的,纯粹为了思考逻辑。

  首先,这是一个连等赋值,而且赋值的是个对象。如果是基本的JavaScript类型,不存在引用,也就不会有类似的问题,但是逻辑是一样的。

  

  赋值过程是怎么样的呢?以上的翻译是,b先赋值5,然后(b=5)会返回所赋的值(也就是5),然后再赋值给a,也就是:

a = (b = 5);

  所以最初的代码,可以修改成:

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

  如果是这样,那么以下代码有什么区别:

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

  很明显,结果不一样,继续了解下JavaScript的运算顺序。

var a = 10;
function fn() {
    a = 20;
    return 20;
}

var b = a + fn();
console.log(b);

  输出30,我们猜测JavaScript是从左到右运算的(事实也确实如此)。先取出a在内存中的值(10),然后执行fn函数,返回20(同时改变了a的值),10+20=30。

  如果运算过程中有括号啥的,是不是会“智能”地改变运算顺序呢?

var a = 10;
function fn() {
    a = 20;
    return 20;
}

var b = a + (fn() * 2);
console.log(b);

  输出50,还是从左到右,没有智能地先运算括号里的内容,然后改变a的值,再和a相加。这在JavaScript内核中是怎么实现的?这个我也没有研究过,但是我知道C++等语言是没有这么“智能”的,可以通过堆栈来模拟,比如简单计算器

  再回头看这道题,声明一个变量a,指向一个对象{n: 1},然后执行连等。可以肯定的是,a.x和a指向的是同一块内存,我们从左到右运算,a.x实际操作的是a指向的对象,发现没有x这个key,那么就会自动添加,它的值等待右边的运算结果。然后a重新引用了一个对象,并且之前的{n:1}这个对象新的key(x)的value也引用到同一个对象上。大概过程如下图,过程2我把它理解为等待,等待过程3中a的运算结果。

  如果要说的稍微“专业”一点,连等是先确定所有变量的指针,再让指针指向那个赋值

  事实上,解析器在接受到 a.x = a = {n:2} 这样的语句后,会这样做:

  1. 找到 a 和 a.x 的指针。如果已有指针,那么不改变它。如果没有指针,即那个变量还没被申明,那么就创建它,指向 null。
    a 是有指针的,指向 {n:1};a.x 是没有指针的,所以创建它,指向 null。
  2. 然后把上面找到的指针,都指向最右侧赋的那个值,即 {n:2}

  如果理解了,试试这道题吧:

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);

参考:

  1. Operator_Precedence
  2. javascript 连等赋值问题
时间: 2024-11-15 05:47:53

纠结的连等赋值的相关文章

【JavaScript基础】在写冒泡排序时遇到的JavaScript基础问题:JavaScript的数据类型和变量赋值时的原理

写冒泡排序时,遇到一个问题: function bubbleSort(arr){ var temp = 0; console.log("传入的数组:"); console.log(arr); for(var i = 0;i<arr.length;i++){ //循环arr.length-1次 console.log("外层第"+i+"次循环===============start"); for(var j = 0;j<arr.leng

ajax给全局变量赋值问题

今天在做项目时,遇到了一个问题.我用的是ajax,要在$.ajax({里面给一个全局变量赋值,结果死活赋值不上,纠结了好半天,后来上网查了查,才知道,ajax默认是异步请求,(当要赋值时,此时的值没有拿到,所以赋值不成功)如果要在$.ajax({里面给全局变量赋值,需要改为同步操作,即加上async : false,就可以了. $.ajax({    type:"post",    url:"a.action",    data: {},    dataType:

easyui打开dialog后给弹出框内输入框赋值问题

在写一个弹出页面的时候,里面有一些输入框,需要在弹出的时候从数据库取值并且赋值,刚开始在弹出的时候使用$(id).val(value),结果赋值失败,为空当时纠结了一会,然后突然想到在easyui打开dialog后不能设置原来的dom,输入框text被其他空间替换了.所以查询了api后发现应该这样赋值$(id). textbox('setValue',value),测试后赋值正确.

移动构造和移动赋值与std::move

---------------------------------------移动构造-------------------------------------------- 传统的深拷贝深赋值 对于类中,含有指针的情况,要自实现其拷贝构造和拷贝赋值.也就是所谓的深拷贝和深赋值.我想这己经成为一种共识了. 比如如下类: #include <iostream> using namespace std; class HasPtrMem { public: HasPtrMem():_d(new in

javascript中for循环和标签元素赋值问题总结

<!DOCTYPE html><html><body><p>点击下面的按钮,将代码块循环五次:</p><button onclick="myFunction()">点击这里</button><p id="demo"></p><script>function myFunction(){var x="";for (var i=0;i

es6学习 -- 解构赋值

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring). 以前,为变量赋值,只能直接指定值. let a = 1; let b = 2; let c = 3; ES6 允许写成下面这样. let [a, b, c] = [1, 2, 3]; 上面代码表示,可以从数组中提取值,按照对应位置,对变量赋值. 本质上,这种写法属于"模式匹配",只要等号两边的模式相同,左边的变量就会被赋予对应的值.下面是一些使用嵌套数组进行解构的例子. 我认为

关于makefile中变量的多次赋值以及override指令

1 基本原则如下 1.1 原则1 变量的普通赋值是有先后顺序的,后面的赋值会覆盖掉前面的赋值. 1.2 原则2 使用的时候,用的是其前面最后的赋值,就算其后面有使用了override指令的赋值也不会影响这条原则. 1.3 原则3 当使用了override指令定义赋值了变量后,其后对该变量的所有的赋值都是无效的.但是override之前的所有的赋值都是有效的.使用的时候是往前最近原则. 2 override变量.命令行参数和普通变量之间的屏蔽关系 override变量会屏蔽命令行参数,除非用+=:

OpenCV中对Mat的遍历访问与赋值

一.访问 对于Mat的访问有两种方式 第一种,利用Mat::at进行访问 //读取3通道彩色图像 Mat img = imread("图片地址"); int px; //读取图像中第一行第一列,Blue通道数据 int px = img.at<Vec3b>(0, 0)[0]; 第二种,利用Mat的成员ptr指针进行访问 //读取3通道彩色图像 Mat img = imread("图片地址"); //将Mat中的第一行地址赋予pxVec uchar* px

Test_赋值多个class选择器

ZC: 元素属性"class"中,可以赋值 多个 class选择器 ZC: 多个选择器,相同的属性设置,哪个起作用:晚定义的起作用 1.代码 <!--内联 HTML5--> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <style type="text/css"> .bgc01 { width :500px; he