Reflections on js deep copy and shallow copy of Reflections on js deep copy and shallow copy of props induced neutron component vue vue modify modifications induced in the neutron component props

Reflections on js deep copy and shallow copy of the modified props vue neutron component caused

 

Either react or communicate vue, parent component and subassembly are achieved by props, the props in the parent component vue follow a one-way data flow, the official's words, props parent of updates It flows down to the subassembly, but not vice versa. In other words, the children will not be to amend the props. However, the actual development process, there may be some cases attempt to modify data props:

1, only the props pass an initial value, it subassembly as a local variable is used, this is generally define a local data property, the value assigned to it props. as follows:

1
2
3
4
5
6
props: [ 'initialCounter' ],
data: function () {
   return  {
     counter:  this .initialCounter
   }
}

2, the value of props to incoming raw data, but its sub-components need to be converted. This case, it is preferable to use a calculation to define the attributes computed as follows:

props: ['size'],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}

In both cases, the value of the basic data types are passed, but in most cases, we need to pass a reference type data to the sub-components, then the problem came.

In JavaScript objects and arrays are passed by reference, so for an array or object type prop it, change the subject or the array itself will affect the parent component of the state in the sub-assembly.

For example, there is a list of the parent component, a double-click editing elements, the data is transmitted to the element as a sub-assembly The props, the need to edit the data in the subassembly, you will find As mentioned above, after editing the parent component values ​​have also changed. In fact, we want to influence the parent component sub-assemblies, subassemblies modify but do not affect the parent component. vue official line seemingly did not explain how this situation should be handled.

Here the situation is relatively simple point, a copy of the data (this data is a single-level object) used when transmitting Object.assign The props, then edit inside the subassembly. Object.assign to achieve the object of merging, but it is shallow copy, if that is familiar objects are objects can not.

So access to relevant information, to consolidate in the next JS deep copy and shallow copy of the relevant knowledge again.

1, the basic data types and the reference data of a storage location

 The basic data types are stored in the stack memory , such var a = 1;

When the copy operation b = a, will then open a stack memory in the memory, as follows

Effect of complementary memory variables a and b, if the value of b is modified at this time does not affect the value of a.

Reference types of data stored in the heap memory, reference type name is stored in the stack memory, the value is in the heap memory, stack memory but will provide a reference point to the value stored in the address heap memory.

When b = a copy operation, the address reference is copied, rather than the value in the memory heap.

When we a [0] = 1 when array to be modified, since the points a and b are the same address, so naturally also influenced by b, which is called a shallow copy.

In fact we desired effect should look like this:

Well, here, in the end what is the depth of copies:

For reference only copied (address), in other words, after copying, the original variables and a new variable pointing to the same thing, the operations between each other influence each other as a  shallow copy .

And if it is to re-allocate memory in the heap, with a different address, but the value is the same, the object with the original object after copying is completely isolated, independently of each other, is  a deep copy .

 

JS method under review copy of which was achieved:

针对数组有这些方法:

Array.slice()

1
2
var  a=[1,2,3];
var  b=a.slice();<br>b[0]=4;<br>console.log(b); //[4,2,3]<br>console.log(a);//[1,2,3]

Array.concat

1
2
3
4
5
var  a=[1,2,3];
var  b=a.concat();
b[0]=4;
console.log(b); //[4,2,3]
console.log(a); //[1,2,3]

 当然,也可以遍历数组赋值。

但是以上两种只对单级结构的数组有效,如果数组的元素是一个引用类型,就不行了,比如:

1
2
3
4
5
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()

1
2
3
4
5
6
7
var  a={
   "name" : "张三" 
};
b=Object.assign({},a);
b.name= "李四"
console.log(b.name); //李四
console.log(a.name); //张三

同样该方法也是浅拷贝,如果对象属性值是引用类型也不行;

那么到底有哪些办法可以实现深拷贝呢

1、递归

复制代码
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);
复制代码

2、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);
复制代码

3、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);
复制代码

4、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

不管是react还是vue,父级组件与子组件的通信都是通过props来实现的,在vue中父组件的props遵循的是单向数据流,用官方的话说就是,父级的props的更新会向下流动到子组件中,反之则不行。也就是说,子组件不应该去修改props。但实际开发过程中,可能会有一些情况试图去修改props数据:

1、这个props只是传递一个初始值,子组件把它当做一个局部变量来使用,这种情况一般定义一个本地的data属性,将props的值赋值给它。如下:

1
2
3
4
5
6
props: [ 'initialCounter' ],
data: function () {
   return  {
     counter:  this .initialCounter
   }
}

2、这个props的值以原始数据传入,但是子组件对其需要转换。这种情况,最好使用computed来定义一个计算属性,如下:

props: ['size'],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}

以上两种情况,传递的值都是基本数据类型,但是大多数情况下,我们需要向子组件传递一个引用类型数据,那么问题就来了。

JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变这个对象或数组本身将会影响到父组件的状态。

比如,在父组件中有一个列表,双击其中一个元素进行编辑,该元素的数据作为props传递给一个子组件,在子组件中需要对该数据进行编辑,你会发现如上所说,编辑后父组件的值也发生了变化。实际上我们想父组件影响子组件,但是子组件修改不要影响父组件。vue官网上貌似没说明这种情况应该如何处理。

这里情况相对简单点,在传递props时用Object.assign拷贝一份数据(这里数据是一个单层级对象),然后在子组件里面对其进行编辑。Object.assign能实现对象的合并,但是它是浅拷贝,也就是说如果对象的熟悉也是对象就不行。

于是查阅了相关资料,再次巩固下JS中深拷贝与浅拷贝的相关知识。

1、基本数据类型和引用数据类型的存储位置

 基本数据类型是存储在栈内存中,比如 var a=1;

当进行复制操作b=a时,会在栈内存中再开一个内存,如下

变量a和变量b的存储互补影响,如果此时修改b的值不会影响a的值。

引用类型数据存储在堆内存中,引用类型的名是存储在栈内存中,值是存储在堆内存中,但是栈内存会提供引用地址指向堆内存中的值。

当进行b=a的复制操作时,复制的是引用地址,而不是堆内存中的值。

而当我们a[0]=1时进行数组修改时,由于a与b指向的是同一个地址,所以自然b也受了影响,这就是所谓的浅拷贝了。

而实际上我们希望的效果应该是这样:

好,到这里,到底什么是深浅拷贝:

对于仅仅是复制了引用(地址),换句话说,复制了之后,原来的变量和新的变量指向同一个东西,彼此之间的操作会互相影响,为 浅拷贝

而如果是在堆中重新分配内存,拥有不同的地址,但是值是一样的,复制后的对象与原来的对象是完全隔离,互不影响,为 深拷贝

 

回顾下JS里实现拷贝的方法有哪些:

针对数组有这些方法:

Array.slice()

1
2
var  a=[1,2,3];
var  b=a.slice();<br>b[0]=4;<br>console.log(b); //[4,2,3]<br>console.log(a);//[1,2,3]

Array.concat

1
2
3
4
5
var  a=[1,2,3];
var  b=a.concat();
b[0]=4;
console.log(b); //[4,2,3]
console.log(a); //[1,2,3]

 当然,也可以遍历数组赋值。

但是以上两种只对单级结构的数组有效,如果数组的元素是一个引用类型,就不行了,比如:

1
2
3
4
5
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()

1
2
3
4
5
6
7
var  a={
   "name" : "张三" 
};
b=Object.assign({},a);
b.name= "李四"
console.log(b.name); //李四
console.log(a.name); //张三

同样该方法也是浅拷贝,如果对象属性值是引用类型也不行;

那么到底有哪些办法可以实现深拷贝呢

1、递归

复制代码
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);
复制代码

2、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);
复制代码

3、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);
复制代码

4、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

Guess you like

Origin www.cnblogs.com/mouseleo/p/11115174.html