Mutable vs. Immutable
immutable
immutable 变量是不可变变量/原始变量,它一旦创建,在整个生命周期内都是不可以改变的。在JavaScript中只有以下几种变量是不可变变量:字符串, 数字,boolean,其它所有变量都是可变变量。
let a = "this is a string";
let b = a.slice(0,4);
//a没有变化,就是不可变变量,string 有concat函数,并非改变原来的string的生命周期。
要注意的是,仅当直接赋值时才是immutable状态,如果使用new的方式创建字符串或数字,则创建的是对象而不是原始变量,它是可变的。
let a = 3; // immutable
typeof a; // number
let b = new Number(4); // mutable new String("s") new Boolean
typeof b; // object
mutable
在JavaScript中,除了原始变量之外的所有变量都是可变变量:对象、数组。
const people = {
title: "student",
age: 14
}
people.age = 20;
console.log(people);
//跟前面的不可变变量相反,它是可以直接修改对象的属性
两者区别
Mutable是对象,如果引用另一个对象,当前对象属性变化,也会影响到被引用的对象。Imutable 则是原始变量。
对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。
! The JSON.parse() method parses a JSON string, constructing the JavaScript value or object described by the string.
deep copy, shadow copy
赋值相当于给一个变量传一个新的指针,可以指向一个字符串,也可以指向一个对象。
无论深浅拷贝,说的都是可变变量,对于不可变变量,没有拷贝一说。浅拷贝就是把对象的第一层属性拷贝到另外一个变量中去,常用的方法是利用Object.assign或Array.slice函数来拷贝,也可以用对象省略语法 […arr] 来拷贝。深拷贝就是把对象完完整整地拷贝到另外一个变量中去,可以使用JSON.parse和JSON.stringify转换生成一个新的对象(只能拷贝enumerable的属性),另外可以利用Object.assign配合递归的方法来拷贝,这种方法就比JSON转换法复杂,但拷贝最彻底,可以把任何属性都拷贝过去。
const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };
const returnedTarget = Object.assign(target, source);
console.log(target); //根据source 拷贝到target
// expected output: Object { a: 1, b: 4, c: 5 }
//shadow copy sortedArr is new array to arr.
let sortedArr = [...arr].sort((a, b) => a - b);
赋值 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
可变变量和不可变变量指的是字符串,数字,boolean,object,而不是let, const。
Pass-by-Value vs. Pass-by-Reference
对原始变量primitive(String, Number, boolean, null, undefine)都是进行传值/复制(两值地址分开):
let a = 3;
let b;
b = a; //copy,但是a and b 地址分开。
a = 2;
console.log("a is ", a);
console.log('b is ', b);
对可变变量(array, object, function)都是进行引用(共用一个地址)。
let age = 29;
function calculateYear(age) {
return age + 1;
}
age = calculateYear(age);
console.log(age) //30 modify parameter
const c = { greeting: "hello" };
const d = c;
console.log("c is ", c); //hello
console.log("d is ", d); //hello
c.greeting = "Hola";
console.log("c is ", c); //Hola
console.log("d is ", d); //Hola
function changeAttribute(obj) {
obj.greeting = "Hi";
}
changeAttribute(d);
console.log("c is ", c); //Hi
console.log("d is ", d); //Hi
Summary
- Immutable vs Mutable: 原始变量(生命周期不变)和object变量(改变属性)。
- 深浅拷贝是针对对象。
- 赋值let const针对其地址(指针)是否有所变化。
- 参数传递:传值(地址分开,copy值不影响原值)和引用(地址共用,copy值和原值互相影响)。