var a = { n: 1 };
var b = a;
a.x = a = { n: 2 };
console.log(a.x);
console.log(b.x);
这个题目考察了两个知识点
- 不同数据类型在内存中的存储方式
- 运算符优先级问题
知识点解析
1不同数据类型在内存中的存储方式
基本类型和引用类型
JavaScript 数据类型目前是有 8 种,在大的方向可以分为两种,一种是基本类型,另外一种是引用类型。
- 基本类型 基本类型也称为原始数据类型,基本数据类型有 7 种,number、string、boolean、null、undefined,symbol(ES6),bigint(ES10)
- 引用类型 引用类型统称为 object 类型,细分的话有:Object 类型、Array 类型、Date 类型、RegExp 类型、Function 类型 等。
变量的内存分配
-
基本类型
基本数据类型变量保存在栈(
stack)中,它们的值直接存储在变量访问的位置。这是因为这些原始类型占据的空间是固定的,所以可将它们存储在较小的内存区域 – 栈中。这样存储便于迅速查寻变量的值。 -
引用类型
javascript的引用数据类型是同时保存在栈内存和堆内存中的对象。与其它语言的不同是,你不可以直接访问堆内存空间中的位置和操作堆内存空间。只能操作对象在栈内存中的引用地址。准确地说,引用类型的存储需要内存的栈区和堆区(堆区是指内存里的堆内存)共同完成,栈区内存保存变量标识符和指向堆内存中该对象的指针,也可以说是该对象在堆内存的地址。由于引用值的大小会改变,所以不能把它放在栈中,否则会降低变量查寻的速度。
运算符优先级
2运算符优先级问题
| 优先级 | 运算符 | 说明 | 结合性 |
|---|---|---|---|
| 1 | . [] () | 字段访问、数组下标、函数调用以及表达式分组 | 从左往右 |
| 2 | ++ -- - ~ ! delete new typeof void | 一元运算符、返回数据类型、对象创建、未定义值 | 从右向左 |
| 3 | * / % | 乘法、除法、取余 | 从左向右 |
| 4 | + - + | 加法、减法、字符串连接 | 从左向右 |
| 5 | << >> >>> | 左位移、右位移、无符号右移 | 从左向右 |
| 6 | < <= > >= instanceof | 小于、小于或等于、大于、大于或等于、是否为特定类的实例 | 从左向右 |
| 7 | == != === !== | 等于、不等于、全等、不全等 | 从左向右 |
| 8 | & | 按位'与' | 从左向右 |
| 9 | ^ | 按位'异或' | 从左向右 |
| 10 | | | 按位'或' | 从左向右 |
| 11 | && | 逻辑(短路)与 | 从左向右 |
| 12 | || | 逻辑(短路)或 | 从左向右 |
| 13 | ?: | 条件(三元)运算符 | 从右向左 |
| 14 | = += -= *= /= %= &= 等 | 混合赋值运算符 | 从右向左 |
| 15 | , | 多个计算 | 按优先级计算,然后从右向左 |
题目分析
var a = { n: 1 };
由变量的内存分配我们得知:变量 a 存储在栈中具体值指向存在堆中的对象 { n: 1 }
var b = a;
此时变量 b 也指向对象 { n: 1 }
a.x = a = { n: 2 };
.(点)的优先级比=(等于) 高 所以限制性a.x,对堆中的对象进行属性赋值,因为x声明未定义,所以是undefined,存在堆中的对象变成{ n: 1, x: undefined }
- 然后从右向左赋值,执行
a = { n: 2 },a的指向变成新对象{ n: 2 }
-
最后一步执行赋值
a.x = a, 即时a.x = { n: 2 },此时左侧a不会重新解析,而是使用最初解析a.x的时候的a,也就是旧对象。旧对象变成{ n: 1, x: { n: 2 } }
总结
a = { n: 2 }b = { n: 1, x: { n: 2 } }
console.log(a.x);//
undefined
console.log(b.x);//
{ n: 2 }