复杂的对象数据处理后,可能发现某个对象值变了,有时却不变。
上述问题往往会困扰 JS初学者,甚至老手也不一定能说出所以然。
本文将对此进行一定深层探究 —— 深入理解 JS中的对象的值传递。
Why
在引入概念之前,先出下面四道题,可以先试着写下,最下面公布答案哦~
P.S 各题互不关联
let a = [1,2]
let b = a
a = [3, 4]
console.log(b)
let a = [1,2]
let b = a
a[0] = 3
console.log(b)
let a = [1,2]
let b = a
a = [3,4]
a[0] = 3
console.log(b)
let a = [1,2]
let b = a
a.pop()
console.log(b)
附上 TS Playground,其实一个改改就够了,顺手都 share 如下:
如果你可以全对 💯,那证明对 JS 对象值的传递理解还算扎实。
如果其中有错误,那我觉得你有必要继续看下去了 (づ。◕‿‿◕。)づ
What
在解释上述 🌰 分析之前,我们需要复习一下 JS 的基础数据类型:
🙌 8 种(7 种原始类型 以及 对象)更多参考 MDN JavaScript 数据类型
- 原始类型:Number、BigInt、String、Boolean、Null、Undefined 和 ES6中的 Symbol
- 复杂类型:Object对象(数组是其中一种内置对象,函数等都是特殊的对象类型)
!!!注意:原始类型是不可变的(immutable),只有对象是可变的(mutable)。
我们再来具体理清下 =
赋值操作具体执行了什么?
- 原始类型:值的拷贝/传递
- 复杂类型:引用地址的拷贝;或称共享传递(call by sharing)名称不重要,领会 ↓ 即可
有了以上理论理解的基础,我们再来依次分析下之前的 🌰
直接上图,应该不难理解了吧,当执行了 a = [3,4]
,之后,a 和 b 变量的引用根本变了,此时不论 a如何改变其值,都不再影响 b。
但是,a
和 b
共享引用(传递)时(即 Example2、4),a
的变化会直接影响 b
.pop()
或者.push
等方法会直接更改数组,这里就不多加引申了。
继续举个简单的 🌰 ,看看你的掌握程度,以下会输出什么? (快速回答哦 (๑•̀ㅂ•́)و✧ )
let a = [1,2]
let b = a
b = []
console.log(a.length == 0)
let c = [] // TS类型强定义,TS Playground 中为 let c:number[] = []
console.log(b, c)
console.log(b == c)
let x = {}
let y = {}
console.log(x == y)
看看是否与你所想一致?为什么就不说了哦
最后,出个可能会作为面试题的 🌰
function changeAgeAndReference(person) {
person.age = 18;
person = {
name: "Anna",
age: 16
};
return person;
}
let personObj1 = {
name: "Olaf",
age: 1000
};
console.log(personObj1); // -> ?
let personObj2 = changeAgeAndReference(personObj1);
console.log(personObj1); // -> ?
console.log(personObj2); // -> ?
🎉 如果真面试到,能否回答正确并且解释出所以然? 有兴趣的可以 Play 研究下(相对比较基础),但实际业务代码这么写会死得很惨 (-"-怒)
How
对象的实际应用
JS 中,数组、函数等皆为对象,那我们通常会怎么应用对象呢?举个 🌰
class student { // 构造函数
name: string
constructor(myname:string){
this.name = myname;
}
sayHi(){
console.log("Hi, I'm " + this.name)
}
}
let s1 = new student("Elsa")
let s2 = s1 // ❌ 这样引用,s2的更改会直接影响s1
s2.name = "Anna"
s1.sayHi()
let s3 = new student("Olaf") // ✅ 通常会用 new 来创建一个新的对象
s3.sayHi()
对于 TS 开发者,Interface 不失为一种好方式,有兴趣的参考 TS 的 Interface 了解一下?
浅拷贝 & 深拷贝
🎉 继续敲黑板,面试问到相关问题的几率 50%+,问答开始:
Q:浅拷贝、深拷贝是什么?有什么区别?
🙌 浅拷贝只复制一层对象的属性,而深拷贝则递归复制了所有层级。
Q:具体有哪些应用场景?什么时候用浅拷贝、什么时候用深拷贝?
有一份 Object 数据,你打算对它进行处理,但又希望拷贝一份副本出来,方便数据对比和恢复数据等。
浅拷贝有效性针对的是单一层级对象,比如简单的一维数组等等。
深拷贝有效性针对的是多层级对象,比如后端返回的Json对象等。
Q:如何在 JS 中实现浅拷贝 & 深拷贝?
🙌 本来想自己总结几种常用方式的,浅拷贝和深拷贝 高赞文章已有,太棒了~
个人通常使用扩展运算符 ...
来实现浅拷贝
使用JSON.parse(JSON.stringify())
来深拷贝
Summary
- 原始类型是不可变的(immutable),只有对象是可变的(mutable)
- 对象赋值
=
为引用地址的拷贝,改变会影响原对象(实际开发中,需慎用) - 需要掌握对象的实际应用方式 & 浅拷贝和深拷贝