原始值/ 对象引用;浅比较, immutable 及在react的运用
Opened this issue · 0 comments
原始值(Primitive Values, 红宝书译为基本类型)
Undefined、Null、Boolean、Number、String、Symbol
原始值是不可更改的:任何方法都无法更改(或 突变mutable )原始值,都是返回新的原始值。
在很多语言中, 字符串以对象的形式来表示, 因此被认为是引用类型的。 ECMAScript 放弃了这一传统。
对象引用(Object References,红宝书译为引用类型)
Object 类型、Array 类型、Function 类型、Date 类型、RegExp 类型
与其他语言不同,JavaScript 不允许直接访问内存中的位置, 也就是说不能直接操作对象的内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。 为此,对象引用的值是按引用访问的。
从上图可看到,因为array和copyArray变量都是等于同一个引用地址(在栈内存),访问同一个对象引用的值(在堆内存),所以除非array变量等于其他的对象引用,否则改了array的对象引用的值,copyArray也会被改变。
浅比较及在react中的运用(比较是否相等):
当比较值(原始值),浅比较去比较他们的值是否相等
当比较对象(对象引用),浅比较不去比较他们的属性值,只比较是否来自同一个引用。
user = {
name: "John",
surname: "Doe"
}
例1:
const user = this.state.user;
user.name = "Jane";
console.log(user === this.state.user); // true
改了名字实际改的还是同一个引用地址的对象,引用相同
const user = clone(this.state.user);
console.log(user === this.state.user); // false
(这边的clone方法可以为 (ES6 语法)
const clone = obj => Object.assign({}, ...obj);
)
现在,对对象属性没有任何改变,它们完全不同。通过克隆原始对象,可以创建具有不同引用的新对象。
浅比较是检测有没有改变的高效方法。它希望你不要mutate数据。
所以react要求你是immutable的操作,
react redux 的connect在每次redux state变化时都会去浅比较mapDispatchToProps 和mapStateToProps,所以可以用reselect让一些需要计算的对象引用如果没有变化就不去计算,避免不必要的计算开销。reselect内部也是去浅比较传入的参数,如果结果为新旧相等,那就返回旧的结果,不重新计算。
React.PureComponent(class component) 或者memo(function component) 则是在component update的时候进行浅比较 如果浅比较结果为true 则不rerender
浅拷贝(Object.assign)
Object.assign()是浅拷贝,拷贝的是属性值。假如源对象的属性值是一个对象的引用,那么它也只指向那个引用。
let obj1 = { a: 0 , b: { c: 0}};
let obj2 = Object.assign({}, obj1);
console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}
obj1.a = 1;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}}
console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}
obj2.a = 2;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}}
console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 0}}
obj2.b.c = 3;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 3}}
console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 3}}
b的属性值是一个对象的引用,浅拷贝后,obj1这个属性值b里面的c被改了,浅拷贝obj1的obj2的c由于指向相同引用,所以值也被改了。
浅复制只复制一层对象的属性和值(属性值为对象的只拷贝了对象在堆内存的地址),而深复制则递归复制了所有层级,而 JavaScript 存储对象都是存地址的,所以浅复制会导致 obj.arr 和 shallowObj.arr 指向同一块内存地址