同一个坑Python掉了一次JS还掉一次,很有生活了
值传递vs引用传递
以下为ChatGPT解答
在 JavaScript 中,函数参数传递遵循“按值传递”(pass by value),但对象和数组等复杂数据类型是按引用访问的(pass by reference-like behavior)。
1. 按值传递(原始类型)
以下类型是按值传递,即传递的是拷贝,函数内部修改参数不会影响原始变量:
- number(数值)
- string(字符串)
- boolean(布尔值)
- undefined
- null
- bigint
- symbol
示例
function changeValue(x) {
x = 99; // 修改的是参数 x 的副本
}
let num = 42;
changeValue(num);
console.log(num); // 42 ✅ 原值未变
解释:
x 在 changeValue() 里是 num 的拷贝,函数内部的修改不会影响原始变量。
2. 按引用访问(对象类型)
以下类型是按“引用”访问(实际上仍是按值传递),即传递的是对象的引用地址,函数内部修改对象的内容会影响原始对象:
- Object
- Array
- Function
- Date
- RegExp
- Map
- Set
- 其他引用类型(如 WeakMap、WeakSet)
示例
function modifyObject(obj) {
obj.name = "Bob"; // 修改的是 obj 的引用指向的对象
}
let person = { name: "Alice" };
modifyObject(person);
console.log(person.name); // "Bob" ✅ 原对象被修改
解释:
- obj 传递的是 person 的引用,指向同一个内存地址。
- 修改 obj.name,person.name 也会变,因为它们指向同一个对象。
3. 重新赋值 vs. 修改内容
虽然对象是按引用访问的,但如果在函数内部重新赋值,不会影响外部对象:
function reassignObject(obj) {
obj = { name: "Charlie" }; // 重新指向新对象
}
let person = { name: "Alice" };
reassignObject(person);
console.log(person.name); // "Alice" ❌ 原对象未变
解释:
- obj = { name: “Charlie” } 只是改变了 obj 的本地引用,person 仍然指向原对象,不受影响。
如果想要让函数重新赋值影响外部对象,必须返回新对象:
function reassignObject(obj) {
return { name: "Charlie" }; // 返回新对象
}
let person = { name: "Alice" };
person = reassignObject(person);
console.log(person.name); // "Charlie" ✅ 重新赋值生效
4. 复杂情况:数组 & 深拷贝
数组的行为类似对象,传递的是引用:
function modifyArray(arr) {
arr[0] = 99; // 修改的是数组内部内容
}
let numbers = [1, 2, 3];
modifyArray(numbers);
console.log(numbers); // [99, 2, 3] ✅ 原数组被修改
但如果重新赋值数组,不影响原数组:
function reassignArray(arr) {
arr = [99, 100, 101]; // 重新指向新数组
}
let numbers = [1, 2, 3];
reassignArray(numbers);
console.log(numbers); // [1, 2, 3] ❌ 原数组未变
如何避免修改原对象/数组
- 浅拷贝(slice()、spread operator)
function modifyCopy(arr) {
let copy = [...arr]; // 创建新数组
copy[0] = 99;
}
let numbers = [1, 2, 3];
modifyCopy(numbers);
console.log(numbers); // [1, 2, 3] ✅ 原数组未变
- 深拷贝(JSON.parse(JSON.stringify(obj)))
function deepCopy(obj) {
return JSON.parse(JSON.stringify(obj));
}
let original = { a: { b: 1 } };
let copy = deepCopy(original);
copy.a.b = 99;
console.log(original.a.b); // 1 ✅ 原对象未变
总结
数据类型 | 传递方式 | 影响原值? | 例子 |
---|---|---|---|
number,string,boolean,null,undefined,symbol,bigint | 按值传递 | ❌ 不影响 | function(x) { x = 99; } |
Object,Array,Function,Map,Set等 | 按引用访问 | ✅ 影响 | function(obj) { obj.name = “Bob”; } |
重新赋值对象/数组 | 按值传递(新引用) | ❌ 不影响 | function(obj) { obj = { new: “value” }; } |
JSON.parse(JSON.stringify(obj)) | 深拷贝 | ❌ 不影响 | function deepCopy(obj) { return JSON.parse(JSON.stringify(obj)); } |
结论
- 原始类型(基本类型)总是按值传递,不会影响原值。
- 对象和数组是按“引用”访问,修改属性或元素会影响原值。
- 重新赋值对象或数组不会影响外部变量,因为函数参数始终是按值传递的(只是这个“值”是引用地址)。
- 如果要避免修改原值,可以使用 slice()、… 或 JSON.parse(JSON.stringify(obj)) 进行拷贝。
这就是 JavaScript 传参的引用与值的区别! 🚀
狗尾续貂:eg
a = [[6],[7,8]];function s(b){b[0]=6;}s(a[1]);
最终a的值为[[6],[6,8]]
引用类型的值比较
传引用的类型值也不可直接比较!
eg.
let arr1 = [1, 2, 3];
let arr2 = [1, 2, 3];
console.log(arr1 === arr2); // false ❌ (不同的引用)
console.log(arr1 == arr2); // false ❌ (不同的引用)
正确操作(ChatGPT)
方法 | 适用场景 | 代码简洁性 | 性能 | 局限性 |
---|---|---|---|---|
=== | 仅适用于同一引用的数组 | ✅ 简单 | ✅ 快速 | ❌ 不能比较不同引用但内容相同的数组 |
JSON.stringify() | 简单一维数组 | ✅ 简单 | ❌ 性能较差(字符串转换开销) | ❌ 不能比较复杂对象或 undefined 、NaN |
递归 every() | 适用于多维数组 | ❌ 需要额外代码 | ✅ 比 JSON.stringify 快 | ❌ 需要额外实现 |
_.isEqual() (Lodash) | 适用于复杂嵌套结构 | ✅ 简单 | ✅ 高效 | ❌ 需要引入 lodash |
最佳选择:
- 简单数组:使用
JSON.stringify()
- 多维数组或对象数组:使用递归
every()
- 项目已有
lodash
:直接用_.isEqual()
发表回复