炫意html5
最早CSS3和HTML5移动技术网站之一

😶 JS数组/对象的值为什么变了?你需要深入理解对象的值传递

复杂的对象数据处理后,可能发现某个对象值变了,有时却不变。

上述问题往往会困扰 JS初学者,甚至老手也不一定能说出所以然。

本文将对此进行一定深层探究 —— 深入理解 JS中的对象的值传递。

Why

引入概念前,先出四道题(各题互不关联),可先试着写下,最下面公布答案哦~

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 如下:

Example 1 Example 2 Example 3 Example 4

如果你可以全对 💯,那证明对 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。

但是,ab 共享引用(传递)时(即 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)
复制代码

TS Playground

看看是否与你所想一致?为什么就不说了哦

最后,出个可能会作为面试题的 🌰

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); // -> ?
复制代码

TS playground

🎉 如果真面试到,能否回答正确并且解释出所以然? 有兴趣的可以 Play 研究下(相对比较基础),但实际业务代码这么写会死得很惨 (-"-怒)

How

对象的实际应用

JS 中,数组、函数等皆为对象,那我们通常会怎么应用对象呢?举个 🌰

class student { // 构造函数
name: string
constructor(myname){
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 Playground

对于 TS 开发者,Interface 不失为一种好方式,有兴趣的参考 TS 的 Interface 了解一下?

浅拷贝 & 深拷贝

🎉 继续敲黑板,面试问到相关问题的几率 50%+,问答开始:

Q:浅拷贝、深拷贝是什么?有什么区别?

🙌 浅拷贝只复制一层对象的属性,而深拷贝则递归复制了所有层级。

Q:具体有哪些应用场景?什么时候用浅拷贝、什么时候用深拷贝?

有一份 Object 数据,你打算对它进行处理,但又希望拷贝一份副本出来,方便数据对比恢复数据等。

浅拷贝有效性针对的是单一层级对象,比如简单的一维数组等等。

深拷贝有效性针对的是多层级对象,比如后端返回的Json对象等。

Q:如何在 JS 中实现浅拷贝 & 深拷贝?

🙌 本来想自己总结几种常用方式的,深拷贝和浅拷贝 高赞文章已有,太棒了~

个人通常使用扩展运算符 ... 来实现浅拷贝,

使用JSON.parse(JSON.stringify()) 来深拷贝

Summary

  • 原始类型是不可变的(immutable),只有对象是可变的(mutable)
  • 对象赋值 = 为引用地址的拷贝,改变会影响原对象(实际开发中,需慎用)
  • 需要掌握对象的实际应用方式 & 浅拷贝和深拷贝

炫意HTML5 » 😶 JS数组/对象的值为什么变了?你需要深入理解对象的值传递

CSS3教程HTML5教程