一、问题描述
// 示例 import React from "react"; import ReactDOM from "react-dom"; import _ from "lodash"; class App extends React.Component { constructor(props) { super(props); this.state = { arr: [] }; } render() { let { arr } = this.state; return <div>arr: {arr.join()}</div>; } componentDidMount() { let arr = _.cloneDeep(this.state.arr); arr.push(1); this.setState({ arr: arr }); } } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);
第1段代码,arr和this.state.arr,值不同,arr变了,在setState前,this.state.arr没变。
第2段代码,arr和this.state.arr,值相同,arr变了,在setState前,this.state.arr也变了,但是setState必须要写。
// 第1段代码,深拷贝 this.state = { arr: [] }; let arr = _.cloneDeep(this.state.arr); arr.push(1); this.setState({ arr: arr });
// 第2段代码,引用类型的赋值 this.state = { arr: [] }; let arr = this.state.arr; arr.push(1); this.setState({ arr: this.state.arr });
二、问题分析
1、赋值
A、基本数据类型传值
基本数据类型的赋值(=)是在内存中新开辟一段栈内存,然后再把再将值赋值到新的栈中。
var a = 10; var b = a; a++ ; console.log(a); // 11 console.log(b); // 10
B、引用数据类型传址
引用类型的赋值是传址,只是改变指针的指向。
var a = {}; // a保存了一个空对象的实例 var b = a; // a和b都指向了这个空对象 a.name = 'camille'; console.log(a.name); // 'camille' console.log(b.name); // 'camille' b.age = 28; console.log(b.age);// 28 console.log(a.age);// 28 console.log(a == b);// true
2、浅拷贝,将A对象拷贝到B对象中,但不包括A里面的子对象。
3、深拷贝,将A对象拷贝到B对象中,包括A里面的子对象。
// 内部方法:用户合并一个或多个对象到第一个对象 // 参数: // target 目标对象 对象都合并到target里 // source 合并对象 // deep 是否执行深度合并 function extend(target, source, deep) { for (key in source) if (deep && (isPlainObject(source[key]) || isArray(source[key]))) { // source[key] 是对象,而 target[key] 不是对象, 则 target[key] = {} 初始化一下,否则递归会出错的 if (isPlainObject(source[key]) && !isPlainObject(target[key])) target[key] = {} // source[key] 是数组,而 target[key] 不是数组,则 target[key] = [] 初始化一下,否则递归会出错的 if (isArray(source[key]) && !isArray(target[key])) target[key] = [] // 执行递归 extend(target[key], source[key], deep) } // 不满足以上条件,说明 source[key] 是一般的值类型,直接赋值给 target 就是了 else if (source[key] !== undefined) target[key] = source[key] } // Copy all but undefined properties from one or more // objects to the `target` object. $.extend = function(target){ var deep, args = slice.call(arguments, 1); //第一个参数为boolean值时,表示是否深度合并 if (typeof target == 'boolean') { deep = target; //target取第二个参数 target = args.shift() } // 遍历后面的参数,都合并到target上 args.forEach(function(arg){ extend(target, arg, deep) }) return target }
三、归纳总结
操作类型 | a是基本类型 | a是引用类型 |
赋值 适用于拷贝基本数据类型数据 | 改变a,b不变;改变b,a不变; (互不影响) | 1、单个修改,ab互相影响,cd互相影响; 改变a的属性,b会变;改变b的属性,a会变; //对象,ab都会变成tom var a = {'name': 'lily'}; var b = a; a.name = 'marry'; b.name = 'tom';
//数组, cd都会变成[1, 2] var c = []; var d = c; c.push(1); d.push(2); 赋值,改变对象指针,仍引用同一对象。 2、整体赋值,ab互不影响,cd互不影响; var a = {'name': 'lily'}; var b = a; a = {'name': 'marry'}; b = {'name': 'tom'};
var c = []; var d = c; c = [1, 3, 5]; d = [2, 4, 6]; |
浅拷贝 只复制一层对象的属性,并不包括对象里面的为引用类型的数据。 适用于拷贝单层引用类型数据。 | 改变a,b不变;改变b,a不变; (互不影响) | 1、单个修改,表层,ab互不影响;深层,ab互相影响; 改变a的二层属性,b会变;改变b的二层属性,a会变; //对象
var a = {
'name' : 'zhangsan',
'age' : '18',
'language' : [1, [2,3], [4,5]],
};
var b = _.clone(a);
a.language[2] = 34;
b.age = "20";
b.language[2] = ["四","五"];
浅拷贝,创建了新对象。
2、整体赋值,ab互不影响,cd互不影响; var a = {'name': 'lily'}; var b = _.clone(a); a = {'name': 'marry'}; b = {'name': 'tom'};
var c = []; var d = _.clone(c); c = [1, 3, 5]; d = [2, 4, 6]; |
深拷贝 适用于拷贝深层引用类型的数据。 | 改变a,b不变;改变b,a不变; (互不影响) | 1、单个修改,ab互不影响; 改变a的属性,b不会变;改变b的属性,a不会变; //对象
var a = {
'name' : 'zhangsan',
'age' : '18',
'language' : [1, [2,3], [4,5]],
};
var b = _.cloneDeep(a);
a.language[2] = 34;
b.age = "20";
b.language[2] = ["四","五"];
2、整体赋值,ab互不影响,cd互不影响; var a = {'name': 'lily'}; var b = _.cloneDeep(a); a = {'name': 'marry'}; b = {'name': 'tom'};
var c = []; var d = _.cloneDeep(c); c = [1, 3, 5]; d = [2, 4, 6]; |