对操作符优先级和数据存储方式考察题
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 }
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END