Vue⽗⼦组件传值的⼀些坑
在⽤ Vue 的⽗⼦组件传值时遇到⼀个冷门的问题,⼦组件改变值后⽗组件的值也随之改变了,特此记录下原因和
解决⽅式。
再系统梳理下 JavaScript 的深拷贝与浅拷贝相关知识点。
1. 问题描述
⽗组件传值给⼦组件,⼦组件改变传过来的值后,⽗组件的值也会跟着改变。
这个问题⽐较冷门,平时如果对组件通信使⽤得⽐较简单,⼀般不会遇到。
2. 原因剖析
核⼼:双向绑定
⽗⼦组件传值的时候涉及双向绑定,当传值为 object 类型时,传值之后数据源会被改变。
深拷贝与浅拷贝
下⽂详细讲。
3. 解决⽅案
我⽬前采⽤的解决办法是:
传值的时候不要直接传数据源,⽽是通过拷贝或者定义新变量等⽅式传值。
简单处理就 JSON.parse(JSON.stringify(obj)),但是这种简单粗暴的⽅法有其局限性。当值为 undefined、function、symbol 会在转换过程中被忽略。所以,对象值有这三种的话⽤这种⽅法会导致属性丢失。
剩下的就是⾃写深拷贝的⼯具函数,或者直接借助第三⽅的库函数,下⾯展开讲。
4. 深拷贝和浅拷贝
JavaScript中的浅拷贝与深拷贝,只是针对复杂数据类型(Object,Array)的复制问题。浅拷贝与深拷贝都可以实现在已有对象上再⽣出⼀份的作⽤。但是对象的实例是存储在堆内存中然后通过⼀个引⽤值去操作对象,由此拷贝的时候就存在两种情况了:拷贝引⽤和拷贝实例,这也是浅拷贝和深拷贝的区别。
下图为JavaScript复杂数据类型的浅拷贝⽰意图:
浅拷贝
浅拷贝是拷贝引⽤,拷贝后的引⽤都是指向同⼀个对象的实例,彼此之间的操作会互相影响。
值得注意的是:Object.assgin() 是浅拷贝,它只能深拷贝第⼀层,深层的还是浅拷贝。因为 Object.assign() 拷贝的是属性值。假如源对象的属性值是⼀个对象的引⽤,那么它也只指向那个引⽤。(摘选⾃MDN)
MDN讲述 assign 的时候,就有⼀个典型的例⼦,这⾥是。
下⾯列举第⼀类浅拷贝 - 拷贝原对象的引⽤:
/**
* 对象的浅拷贝
*/
var obj1 = {
name:'wenyuan',
age: 22
}
var obj2 = obj1;
obj2['job'] = 'coder';
console.log(obj1); //Object {name: "wenyuan", age: 22, job: "coder"}
console.log(obj2); //Object {name: "wenyuan", age: 0, job: "coder"}
/* ------------------------- 华丽的分割线 ------------------------- */
/**
* 数组的浅拷贝
var arr1 = [1, 2, 3, '4'];
var arr2 = arr1;
arr2[1] = "test";
console.log(arr1); // [1, "test", 3, "4"]
console.log(arr2); // [1, "test", 3, "4"]
接下来看第⼆类浅拷贝 - 源对象拷贝实例,其属性对象拷贝引⽤:
这种情况,外层源对象是拷贝实例,如果其属性元素为复杂数据类型(Object、Array)时,内层元素拷贝引⽤。对源对象直接操作,不影响另外⼀个对象,但是对其属性操作时候,会改变另外⼀个对象的属性的值。
/**
* 对象的浅拷贝
* jQuery的 $.extend(a,b) 或 $.extend({},a,b)
*/
var obj1 = {
name:'wenyuan',
age: 22,
social: {
blog: 'www.wenyuanblog'
},
skills: ['js', 'html', 'css', 'python']
}
var obj2 = $.extend({},obj1);
console.log(obj1 === obj2) // 输出false,说明外层数组拷贝的是实例
console.log(obj1.social === obj2.social) // 输出true,说明对于Object类型的属性是拷贝引⽤
console.log(obj1.skills === obj2.skills) // 输出true,说明对于Array类型的属性是拷贝引⽤
/**
* 对象的浅拷贝
* ES6的 Object.assign() 和对象扩展运算符...
*/
var obj1 = {
name:'wenyuan',
age: 22,
social: {
blog: 'www.wenyuanblog'
},
skills: ['js', 'html', 'css', 'python']
}
var obj2 = Object.assign({},obj1);
console.log(obj1 === obj2) // 输出false,说明外层数组拷贝的是实例
console.log(obj1.social === obj2.social) // 输出true,说明对于Object类型的属性是拷贝引⽤
console.log(obj1.skills === obj2.skills) // 输出true,说明对于Array类型的属性是拷贝引⽤
var obj3 = {...obj1};
console.log(obj1 === obj3) // 输出false,说明外层数组拷贝的是实例
console.log(obj1.skills === obj3.skills) // 输出true,说明对于Array类型的属性是拷贝引⽤
console.log(obj1.skills === obj3.skills) // 输出true,说明对于Array类型的属性是拷贝引⽤
/* ------------------------- 华丽的分割线 ------------------------- */
/
**
* 数组的浅拷贝
* Array.prototype.slice()
*/
var arr1 = [{name: "wenyuan"}, {name: "Evan You"}];
var arr2 = arr1.slice(0);
console.log(arr1 === arr2); // 输出false,说明外层数组拷贝的是实例
console.log(arr1[0] === arr2[0]); // 输出true,说明其元素拷贝的是引⽤
/**
* 数组的浅拷贝
* at()
*/
var arr1 = [{name: "wenyuan"}, {name: "Evan You"}];
var arr2 = at();
console.log(arr1 === arr2); // 输出false,说明外层数组拷贝的是实例
console.log(arr1[0] === arr2[0]); // 输出true,说明其元素拷贝的是引⽤
* 数组的浅拷贝
* ES6的 Object.assign() 和对象扩展运算符...
* 由于数组是特殊的对象,所以ES6中的这种⽅式也可以⽤于数组
*/
var arr1 = [{name: "wenyuan"}, {name: "Evan You"}];
var arr2 = Object.assign([],arr1)
var arr3 = { ...arr1 };
console.log(arr1 === arr2); // 输出false,说明外层数组拷贝的是实例
console.log(arr1 === arr3); // 输出false,说明外层数组拷贝的是实例
console.log(arr1[0] === arr2[0]); // 输出true,说明其元素拷贝的是引⽤
console.log(arr1[0] === arr3[0]); // 输出true,说明其元素拷贝的是引⽤
深拷贝
在堆中重新分配内存,并且把源对象所有属性都进⾏新建拷贝,以保证深拷贝的对象的引⽤图不包含任何原有对象或对象图上的任何对象,拷贝后的对象与原来的对象是完全隔离,互不影响。
下⾯列举⼀些深拷贝的例⼦:
/**
* 对象的深拷贝
* JSON.stringify()和JSON.parse()
* 这种深拷贝最简单,但有其局限性,上⽂已经提到过了
*/
var obj1 = {
name:'wenyuan',
age: 22,
social: {
blog: 'www.wenyuanblog'
},
skills: ['js', 'html', 'css', 'python']
}
var obj2 = JSON.parse(JSON.stringify(obj1));
console.log(obj1 === obj2) // 输出false,说明外层数组拷贝的是实例
console.log(obj1.social === obj2.social) // 输出false,说明对于Object类型的属性也是拷贝实例
console.log(obj1.skills === obj2.skills) // 输出false,说明对于Array类型的属性也是拷贝实例
/**
* 对象的深拷贝
* jQuery的 $.extend(true,a,b)
*/
var obj1 = {
name:'wenyuan',
age: 22,
social: {
blog: 'www.wenyuanblog'
js assign},
skills: ['js', 'html', 'css', 'python']
}
var obj2 = $.extend(true,obj1);
console.log(obj1 === obj2) // 输出false,说明外层数组拷贝的是实例
console.log(obj1.social === obj2.social) // 输出false,说明对于Object类型的属性也是拷贝实例
console.log(obj1.skills === obj2.skills) // 输出false,说明对于Array类型的属性也是拷贝实例
/**
* 对象的深拷贝
* 也可以⾃⼰写⼀个函数实现,⽤递归+判断,注意别进⼊死循环就好
* 这⾥不举例了,以前我整理过⼀篇常⽤⼯具类函数的博客,⾥⾯包含了深拷贝函数
*/
/**
* 对象的深拷贝
* lodash的_.cloneDeep
*/
var obj1 = {
name:'wenyuan',
age: 22,
social: {
blog: 'www.wenyuanblog'
},
skills: ['js', 'html', 'css', 'python']
}
var obj2 = _.cloneDeep(obj1);
console.log(obj1 === obj2) // 输出false,说明外层数组拷贝的是实例
console.log(obj1.social === obj2.social) // 输出false,说明对于Object类型的属性也是拷贝实例
console.log(obj1.skills === obj2.skills) // 输出false,说明对于Array类型的属性也是拷贝实例
以上就是JavaScript中的浅拷贝与深拷贝的知识点,以代码的形式记录下来,⽅便回顾。
到此这篇关于Vue⽗⼦组件传值的⼀些坑的⽂章就介绍到这了,更多相关Vue⽗⼦组件传值内容请搜索以前的⽂章或继续浏览下⾯的相关⽂章希望⼤家以后多多⽀持!