不论是react仍是vue,父级组件与子组件的通讯都是经过props来实现的,在vue中父组件的props遵循的是单向数据流,用官方的话说就是,父级的props的更新会向下流动到子组件中,反之则不行。也就是说,子组件不该该去修改props。但实际开发过程当中,可能会有一些状况试图去修改props数据:vue
一、这个props只是传递一个初始值,子组件把它当作一个局部变量来使用,这种状况通常定义一个本地的data属性,将props的值赋值给它。以下:react
props: ['initialCounter'], data: function () { return { counter: this.initialCounter } }
二、这个props的值以原始数据传入,可是子组件对其须要转换。这种状况,最好使用computed来定义一个计算属性,以下:jquery
props: ['size'], computed: { normalizedSize: function () { return this.size.trim().toLowerCase() } }
以上两种状况,传递的值都是基本数据类型,可是大多数状况下,咱们须要向子组件传递一个引用类型数据,那么问题就来了。json
JavaScript 中对象和数组是经过引用传入的,因此对于一个数组或对象类型的 prop 来讲,在子组件中改变这个对象或数组自己将会影响到父组件的状态。数组
好比,在父组件中有一个列表,双击其中一个元素进行编辑,该元素的数据做为props传递给一个子组件,在子组件中须要对该数据进行编辑,你会发现如上所说,编辑后父组件的值也发生了变化。实际上咱们想父组件影响子组件,可是子组件修改不要影响父组件。vue官网上貌似没说明这种状况应该如何处理。this
这里状况相对简单点,在传递props时用Object.assign拷贝一份数据(这里数据是一个单层级对象),而后在子组件里面对其进行编辑。Object.assign能实现对象的合并,可是它是浅拷贝,也就是说若是对象的熟悉也是对象就不行。spa
因而查阅了相关资料,再次巩固下JS中深拷贝与浅拷贝的相关知识。3d
一、基本数据类型和引用数据类型的存储位置code
基本数据类型是存储在栈内存中,好比 var a=1;orm
当进行复制操做b=a时,会在栈内存中再开一个内存,以下
变量a和变量b的存储互补影响,若是此时修改b的值不会影响a的值。
引用类型数据存储在堆内存中,引用类型的名是存储在栈内存中,值是存储在堆内存中,可是栈内存会提供引用地址指向堆内存中的值。
当进行b=a的复制操做时,复制的是引用地址,而不是堆内存中的值。
而当咱们a[0]=1时进行数组修改时,因为a与b指向的是同一个地址,因此天然b也受了影响,这就是所谓的浅拷贝了。
而实际上咱们但愿的效果应该是这样:
好,到这里,到底什么是深浅拷贝:
对于仅仅是复制了引用(地址),换句话说,复制了以后,原来的变量和新的变量指向同一个东西,彼此之间的操做会互相影响,为 浅拷贝。
而若是是在堆中从新分配内存,拥有不一样的地址,可是值是同样的,复制后的对象与原来的对象是彻底隔离,互不影响,为 深拷贝。
回顾下JS里实现拷贝的方法有哪些:
针对数组有这些方法:
Array.slice()
var a=[1,2,3]; var b=a.slice();
b[0]=4;
console.log(b);//[4,2,3]
console.log(a);//[1,2,3]
Array.concat
var a=[1,2,3]; var b=a.concat(); b[0]=4; console.log(b);//[4,2,3] console.log(a);//[1,2,3]
固然,也能够遍历数组赋值。
可是以上两种只对单级结构的数组有效,若是数组的元素是一个引用类型,就不行了,好比:
let a=[0,1,[2,3],4], b=a.slice(); a[0]=1; a[2][0]=1; console.log(a,b);
修改二维数组的元素仍是会影响原数组,也就是说slice和concat其实是浅拷贝。
针对对象:
Object.assign()
var a={ "name":"张三" }; b=Object.assign({},a); b.name="李四"; console.log(b.name);//李四 console.log(a.name);//张三
一样该方法也是浅拷贝,若是对象属性值是引用类型也不行;
那么到底有哪些办法能够实现深拷贝呢
一、递归
function deepClone(obj){ let objClone = Array.isArray(obj)?[]:{}; if(obj && typeof obj==="object"){ for(key in obj){ if(obj.hasOwnProperty(key)){ //判断ojb子元素是否为对象,若是是,递归复制 if(obj[key]&&typeof obj[key] ==="object"){ objClone[key] = deepClone(obj[key]); }else{ //若是不是,简单复制 objClone[key] = obj[key]; } } } } return objClone; } let a=[1,2,3,4], b=deepClone(a); a[0]=2; console.log(a,b);
二、jquery中的$.extend();
var obj = {name:'xixi',age:20,company : { name : '腾讯', address : '深圳'} }; var obj_extend = $.extend(true,{}, obj); //extend方法,第一个参数为true,为深拷贝,为false,或者没有为浅拷贝。 console.log(obj === obj_extend); obj.company.name = "ali"; obj.name = "hei"; console.log(obj); console.log(obj_extend);
三、JSON对象的JSON.parse()和JSON.stringify();
var obj = {name:'xixi',age:20,company : { name : '腾讯', address : '深圳'} }; var obj_json = JSON.parse(JSON.stringify(obj)); console.log(obj === obj_json); obj.company.name = "ali"; obj.name = "hei"; console.log(obj); console.log(obj_json);
四、Lodash中的_.cloneDeep()
var objects = [{ 'a': 1 }, { 'b': 2 }]; var deep = _.cloneDeep(objects); console.log(deep[0] === objects[0]); // => false
虽然经过拷贝props数据解决了问题,可是拷贝后修改新数据的属性并不会触发vue的更新机制,须要强制更新$forceUpdate(),总以为很奇怪,不知道你们有什么更好的办法没有,欢迎你们留言讨论。
参考文章:
https://zhuanlan.zhihu.com/p/26282765
https://zhuanlan.zhihu.com/p/26282765