aermin/blog

原始值/ 对象引用;浅比较, 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 不允许直接访问内存中的位置, 也就是说不能直接操作对象的内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。 为此,对象引用的值是按引用访问的。

image

从上图可看到,因为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 指向同一块内存地址

How does shallow compare work in react