我这里有四种情况,执行的结果如注释中所示。我真正不明白的是,在某些情况下,值可以变异,但有些则不然。有没有人可以提供答案,即不变性和可变性如何在 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
却没有?是因为String
和Number
是不可变的?
基于这些实验,我是否可以假设只有当我显式地将其分配给函数中对象的属性时,该值才会更改?我还将假设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中,您将值传递给函数并对其进行某些操作。_y
是fn2(_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
的封闭函数以及let
和const
的大括号。前面的代码等效于以下内容:
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
属性的引用。