JavaScript 的 mutable 和 immutable 变量

发布:elantion 日期:2018-12-12 阅读:960 评论:0

在开始说mutableimmutable变量之前,我们先来大概理解一下变量是怎么存放在内存里的。我们把内存想象成书柜,每本书相当于一个变量,我们要找一本书的时候,我们会通过书的编号去寻找,例如:7-8-12,意思就是在第七个柜子,第八层,左边数来第12本。而在内存里,我们把这本书的编号称为指针。实际上内存的管理要复杂得多,这只是个大概的原理。回到JavaScript,immutable就是放了一本不可编辑的书(例如受版权限制),mutable就是放了一本可以修改的书(例如用铅笔写的书)。

什么是immutable变量

immutable变量,中文称之为不可变变量,顾名思义,它一旦创建,在整个生命周期内都是不可以改变的。在JavaScript中只有以下几种变量是不可变变量:字符串, 数字,其它所有变量都是可变变量。这几种不可变变量我们也称之为原始变量。

let a = 'this is a string';
let b = a.slice(10, 16);

我们把a打印出来,仍然是'this is a string',它并没有变化,这就是不可变变量。

要注意的是,仅当直接赋值时才是immutable状态,如果使用new的方式创建字符串或数字,则创建的是对象而不是原始变量,它是可变的。

let a = 3; // immutable
typeof a; // number

let b = new Number(4); // mutable
typeof b; // object

什么是mutable变量

在JavaScript中,除了原始变量之外的所有变量都是可变变量:对象、数组。请看下面这个对象:

let a = {
    b: 3
}; // mutable
a.b = 4;
a; // {b: 4}

跟前面的不可变变量相反,它是可以直接修改对象的属性。

对React.js的影响

当我们对React的state进行修改时,如果变量是一个mutable变量,那么就不能直接修改,需要把它转换成另外一个变量才能触发render更新。例如:

this.state; // {a: {b: 'test'}}
const a = this.state.a;
a.b = 'test2';
this.setState({a});

这是不会触发render的更新,要改成:

const a = JSON.parse(JSON.stringfy(this.state.a));
a.b = 'test2';
this.setState({a});

这是因为当setState赋值给a时,如果a仍然是之前state里的a(指针相同),那么react就认为state没有更新,于是就不会去更新view,但如果通过JSON转换之后,这个a指向的内存地址发生变化,不再是以前的a,react就能发现state的变化,于是就会去更新view。

赋值、浅拷贝、深拷贝

赋值相当于给一个变量传一个新的指针,可以指向一个字符串,也可以指向一个对象。
无论深浅拷贝,说的都是可变变量,对于不可变变量,没有拷贝一说。浅拷贝就是把对象的第一层属性拷贝到另外一个变量中去,常用的方法是利用Object.assign或Array.slice函数来拷贝,也可以用对象省略语法 {...any} 来拷贝。深拷贝就是把对象完完整整地拷贝到另外一个变量中去,可以使用JSON.parse和JSON.stringify转换生成一个新的对象(只能拷贝enumerable的属性),另外可以利用Object.assign配合递归的方法来拷贝,这种方法就比JSON转换法复杂,但拷贝最彻底,可以把任何属性都拷贝过去。

let 和 const

修饰符const定义的变量是不可以修改的,let定义的变量却可以,是不是表示用const定义的变量是不可变变量而let 定义的变量是可变变量?不是的。const 定义的是常量,在内存上是一个不可改变指针地址的变量,也就是说你可以指向一个对象(可变变量),也可以修改对象的属性,但不能赋值给另外一个对象、字符串或任何东西,也就是不能修改指针的地址。

const a = {b:2};
a.b = 3; // no error and totally ok
a = {b:2}; // throw error, no matter what value you assigned

修饰符let定义的变量可以重新赋值,但不表示这个变量是可变变量,只是变量指向的内存地址改变了,原来的值并未改变。

let a = 'this is a string';
let b = a;
a = 'this is another string'; // ok
b; // 'this is a string', no change

我们说的可变变量和不可变变量指的是对应的值(字符串、对象、布尔值 ...),而不是变量本身,要注意这个区分哦。