目录
深拷贝和浅拷贝的区别
实现浅拷贝的几种方法
Object.assign();扩展运算符;for 循环;Array.prototype.slice();Array.prototype.
concat()。。。
ngOnInit() {
// Object.assign() 方法
const x = { name: "hi" };
const y = Object.assign({}, x);
y.name = "go";
console.log(x.name, y.name); // hi go
// 扩展运算符方法
const x1 = { name: "hi" };
const y1 = { ...x1 };
y1.name = "go1";
console.log(x1.name, y1.name); // hi go1
// 封装函数 for in 方法
const x2 = { name: "hi" };
const y2 = this.test2(x2);
y2.name = "go2";
console.log(x2.name, y2.name); // hi go2
// 数组的slice() 方法
const x4 = [1, 2, 3, [5, 6]];
const y4 = x4.slice(1);
y4[2][0] = 99;
console.log(y4, x4); // y4: [2,3,[99,6]]; x4: [1,2,3,[99,6]]
}
test2(obj: Object): any {
const y2 = {};
for (var attr in obj) {
y2[attr] = obj[attr];
}
return y2;
}
实现深拷贝的3种方法
当后台返回了一堆数据,你需要对这堆数据做操作,但多人开发情况下,你是没办法明确这堆数据是否有其它功能也需要使用,直接修改可能会造成隐性问题,深拷贝能帮你更安全安心的去操作数据
深拷贝是对对象以及对象的所有子对象进行拷贝。完全改变变量 a 之后对 b 没有任何影响,这就是深拷贝的魔力。
1. 实现深拷贝的一种思路就是递归调用浅拷贝,把所有属于对象的属性类型都遍历赋给另一个对象即可。
ngOnInit() {
// const a = [1, 2, 3, 4];
const a = { name: "lake", detail: { age: 18, gender: "female" } };
const b = this.deepClone(a);
a.detail.age = 99;
console.log(a, b);
}
deepClone(obj: any) {
let objClone = Array.isArray(obj) ? [] : {};
if (obj && typeof obj === "object") {
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
//判断ojb子元素是否为对象,如果是,递归复制
if (obj[key] && typeof obj[key] === "object") {
objClone[key] = this.deepClone(obj[key]);
} else {
//如果不是,简单复制
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
以上打印:
2. 引入lodash库实现
import * as L from 'lodash';
let targetOj =L.cloneDeep(sourceObj)
3. JSON.parse(JSON.stringify(object))方法
对数组使用JSON.parse(JSON.stringify(object))方法深
拷贝:
const x = [1, 2, 3, [4,5]];
const y = JSON.parse(JSON.stringify(x));
x[3][0] = 77,
console.log(x, y);
// x: [1,2,3,[77,5]]
// y: [1,2,3,[4,5]]
对对象使用JSON.parse(JSON.stringify(object))方法深
拷贝:
const x = {
name: "lake",
detail: {
gender: "male",
age: 40
}
};
const y = JSON.parse(JSON.stringify(x));
x.detail.age = 99;
console.log(x, y);
打印 :
需要注意的是,JSON.parse(JSON.stringify())
会有一些不适用的情况:因为这里面的x只能是Number
, String
, Boolean
, Array
, 扁平对象,即那些能够被 JSON 直接表示的数据结构。所以会忽略掉undefined
、symbol
和函数。。。
undefined
、任意的函数以及 symbol 值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成null
(出现在数组中时)。函数、undefined 被单独转换时,会返回 undefined,如JSON.stringify(function(){})
orJSON.stringify(undefined)
. ---mdn【undefined、函数 通过这种方式不会被深拷贝, null会被深拷贝】
另外,这种方法的clone不会clone 对象内部的函数指针,假如a{ b:1 ,c:function(e){} }
,其中函数c是不会被复制的,clone之后就是 a{ b:1 }
大部分时候 deep clone 的用例都是在数据结构的持久化上,换句话说应该是可以被序列化/反序列化的数据。大部分情况下是不需要deepClone的,而且deepClone是应该被避免的,因为通常这会在你的程序状态中创建多个source of truth而影响reasoning,对performance影响也很大。如果对象比较大,层级也比较多,深复制会带来性能上的问题。在遇到需要采用深复制的场景时,可以考虑有没有其他替代的方案。在实际的应用场景中,也是浅复制更为常用。
之所以会出现了深拷贝和浅拷贝,究其根本是因为JS种的变量包含了不同类型的数据值:基本类型和引用类型。
基本数据类型
A primitive value is a member of one of the following built‑in types: Undefined,Null, Boolean, Number, String, and Symbol.
基本数据类型值不可变。数字,布尔值不可变,而字符串中所有的方法看上去返回了一个修改后的字符串,实际上返回的是一个新的字符串值。
基本数据类型的比较:
true == 1; // true
true === 1; // false
==会进行类型转换再比较;===是严格等,最好用这个进行值的比较。
引用类型
var person1 = {name:'jozo'};
var person2 = {name:'xiaom'};
var person3 = {name:'xiaoq'};
引用类型的比较:
所以每次我们对 js 中的引用类型进行操作的时候,都是操作其对象的引用(保存在栈内存中的指针),所以比较两个引用类型,是看其的引用是否指向同一个对象。例如:
var a = [1,2,3];
var b = [1,2,3];
console.log(a === b); // false
// 虽然变量 a 和变量 b 都是表示一个内容为 1,2,3 的数组,但是其在内存中的位置不一样,
// 也就是说变量 a 和变量 b 指向的不是同一个对象,所以他们是不相等的。
传值与传址
在我们进行赋值操作的时候,基本数据类型的赋值(=)是在内存中新开辟一段栈内存,然后再把再将值赋值到新的栈中。基本类型的赋值的两个变量是两个独立相互不影响的变量。
但是引用类型的赋值是传址。只是改变指针的指向,也就是说引用类型的赋值是对象保存在栈中的地址的赋值,这样的话两个变量就指向同一个对象,因此两者之间操作互相有影响。
|
Symbol
/* 原写法 :
const shapeType = {
triangle: 'Triangle'
}; */
const shapeType = {
triangle: Symbol()
};
function getArea(shape, options) {
let area = 0;
switch (shape) {
case shapeType.triangle:
area = .5 * options.width * options.height;
break;
}
return area;
}
getArea(shapeType.triangle, { width: 100, height: 100 });
字符串Triangle
就是一个魔术字符串。它多次出现,与代码形成“强耦合”,不利于将来的修改和维护。
常用的消除魔术字符串的方法,就是把它写成一个变量。即:const shapeType = {triangle: 'Triangle'} 。我们把Triangle
写成shapeType
对象的triangle
属性,这样就消除了强耦合。
如果仔细分析,可以发现shapeType.triangle
等于哪个值并不重要,只要确保不会跟其他shapeType
属性的值冲突即可。因此,这里就很适合改用 Symbol 值。
不需要对外操作和访问的属性使用Symbol来定义。