Javascript:为什么在某些情况下,对象的值发生了变化,而另一些情况下则在将其传递给函数后没有?



我这里有四种情况,执行的结果如注释中所示。我真正不明白的是,在某些情况下,值可以变异,但有些则不然。有没有人可以提供答案,即不变性和可变性如何在 javascript 中为变量赋值?我认为这可能是一个典型的面试问题...但我无法清楚地解释它是如何工作的......

案例1

const x = { a: 1, b: 2 };
function fn1(_x) {
_x.a = 0;
}
fn1(x);
console.log(x);
// { a: 0, b: 2 }          mutated

案例2

const y = { a: 1, b: 2 };
function fn2(_y) {
_y = 0;
}
fn2(y.a);
console.log(y);
// { a: 1, b: 2 }          not mutated

我从此示例代码中得到的是,如果未在函数中显式分配对象属性的值,则无法更改该值。


案例3

const z1 = { a: 1, b: 2 };
function fn3({ a }) {
a = 0;
a.c = 0;
}
fn3(z1);
console.log(z1);
// { a: 1, b: 2 }          not mutated

案例4

const z2 = { a: {}, b: 2 };
function fn3({ a }) {
a = 0;
a.c = 0;
}
fn3(z2);
console.log(z2);
// { a: {}, b: 2 }         not mutated

我在案例 4 中期望的是// { a: {}, b: 2 }.为什么z2的值发生了变异,而z1却没有?是因为StringNumber是不可变的?


基于这些实验,我是否可以假设只有当我显式地将其分配给函数中对象的属性时,该值才会更改?我还将假设Array的行为与Object中的行为相同。

谢谢!


更新:

案例5

const z2 = { a: {}, b: 2 };
function fn4({ a }) {
a.c = 0;
a = 0;
}
fn3(z2);
console.log(z2);
// { a: { c: 0 }, b: 2 }        mutated

帕特里克·罗伯茨(Patrick Roberts)刚刚指出,"先前"的情况4是不可变的,我以前没有注意到这一点。但对我来说有趣的是,通过翻转a.c = 0;a = 0;的顺序,这在案例 5 中fn4,输出会发生变化......但那是我库存的地方...很抱歉在案例 4 中以前的错误输出。

在情况 1中,您传递对对象的引用并改变其属性之一,而在情况 2中,您将值传递给函数并对其进行某些操作。_yfn2(_y)作用域上的变量,它不存在于它之外,调用fn2(y.a)与调用fn2(1)是一回事,因此,它不会改变y上的任何内容。

案例3案例 4都使用新的对象解构语法,这些代码是等效的:

function fn3({ a }) {
a = 0;
a.c = 0;
}
function fn3(x) {
var a = x.a;
a = 0;
a.c = 0;
}

因此,在案例 3 中调用fn3是按值传递,就像在案例 2 中一样。

案例 4 不会改变对象。

更新案例 5

const z2 = { a: {}, b: 2 };
function fn4({ a }) {
a.c = 0;
a = 0;
}
fn4(z2);
console.log(z2);

在案例 5 中,您是吊装的"受害者"。该代码不会按编写顺序执行。在 JS 中声明任何变量等效于在封闭范围的顶部声明它。该范围将是var的封闭函数以及letconst的大括号。前面的代码等效于以下内容:

const z2 = { a: {}, b: 2 };
function fn4(x) {
var a = 0;
x.a.c = 0;
}
fn4(z2);
console.log(z2);

原因是当你声明a = 0它被提升到函数的顶部时,你从参数中获得的a是不同的a,即从传递给函数的对象对a属性的引用。

最新更新