Javascript:为什么有些情况下对象的值会发生变异但其他情况不会在传递给函数之后?

时间:2018-01-31 00:25:49

标签: javascript immutability mutability

我在这里有四个案例,并且在评论中提供了执行结果。我真正理解的是,在某些情况下,价值可能会发生变异,但有些则不是。有没有人可以提供一个答案,说明不变性和可变性如何在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
帕特里克罗伯茨刚刚指出了之前的#39;案例4是不可变的,我以前没有注意到......但是我感兴趣的是通过翻转a.c = 0;a = 0;的顺序,fn4情况5,输出发生变化......但这就是我库存的地方......对于案例4中先前的错误输出感到抱歉。

1 个答案:

答案 0 :(得分:1)

案例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属性。