JavaScript的数据类型分为基本数据类型(String, Number, Boolean, Undefined, Null, Symbol)和引用数据类型。 引用类型在栈中存储了指针,该指针指向的是堆中的该实体的起始地址,当解释器寻找引用值的时候,会先检索它在栈中的地址,而后根据地址获取到堆中的实体。javascript
深拷贝和浅拷贝主要是针对于Object和Array这样的引用类型的。浅拷贝只赋值某一个对象的指针,而不复制对象自己,新旧对象会共享同一块内存,可是深拷贝会建立一个一摸同样的对象,新建立的对象不会和原来的对象共享同一块内存,修改新对象的时候,原来对象的内容不会被修改。java
赋值:当把一个值赋给一个新变量的时候,其实是把这个对象在栈中的地址赋给变量,而不是栈中的数据。两个变量会指向同一个存储空间。 浅拷贝:浅拷贝是按位拷贝对象,它会建立一个新对象,这个对象有着原始对象属性值的一份精确拷贝。若是属性是基本类型,拷贝的就是基本类型的值;若是属性是内存地址(引用类型),拷贝的就是内存地址 ,所以若是其中一个对象改变了这个地址,就会影响到另外一个对象。即默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝),即只复制对象空间而不复制资源。数组
和原数据是否指向同一个对象 | 第一层数据为基本数据类型 | 原数据中包含子对象 | |
---|---|---|---|
赋值 | 是 | 改变会使原数据一块儿改变 | 改变会使原数据一块儿改变 |
浅拷贝 | 否 | 改变不会使原数据一块儿改变 | 改变会使原数据一块儿改变 |
深拷贝 | 否 | 改变不会使原数据一块儿改变 | 改变不会使原数据一块儿改变 |
一、Object.assign()异步
Object.assign()方法能够把任意多个源对象自身的可枚举的属性拷贝给目标对象,而后返回目标对象。Object.assign()进行的是浅拷贝拷贝的是对象属性的引用,并非对象自己。可是当obj只有一层的时候,Object.assign()进行的是深拷贝。async
console.log("当对象只有一层的时候:");
let obj1 = {
a: 10
}
let obj2 = Object.assign({}, obj1);
console.log("改变以前:");
console.log(obj1.a); // 10
console.log(obj2.a); // 10
obj2.a = 20;
console.log("改变以后:");
console.log(obj1.a); // 10
console.log(obj2.a); // 20
console.log("当对象不止一层的时候:");
let obj3 = {
a: {
b: 10
}
};
let obj4 = Object.assign({}, obj3);
console.log("改变以前:");
console.log(obj3.a.b); // 10
console.log(obj4.a.b); // 10
obj4.a.b = 20;
console.log("改变以后:");
console.log(obj3.a.b); // 20
console.log(obj4.a.b); // 20
复制代码
二、Array.prototype.concat()函数
let arr = [1, 3, {
username: 'kobe'
}];
let arr2=arr.concat();
arr2[2].username = 'wade';
console.log(arr); // wade
复制代码
三、Array.prototype.slice()ui
let arr = [1, 3, {
username: ' kobe'
}];
let arr3 = arr.slice();
arr3[2].username = 'wade'
console.log(arr); // wade
复制代码
concat()和slice()方法不会修改原数组,只会返回一个浅复制了原数组元素的一个新数组。spa
一、JSON.parse(JSON.stringify)prototype
原理: 用JSON.stringify将对象转换成JSON字符串,再使用JSON.parse()方法将字符串转换成JSON对象,这个过程是一个深拷贝的过程。指针
let arr = [1, 3, {
username: ' kobe'
}];
let arr4 = JSON.parse(JSON.stringify(arr));
arr4[2].username = 'duncan';
console.log(arr, arr4)
复制代码
这样的方法虽然能够实现数组或者对象的深拷贝,可是不能够实现函数的深拷贝。
二、手写的递归方法
//检测类型
function checkedType(target){
return Object.prototype.toString.call(target).slice(8, -1);
}
//实现深度拷贝
function clone(target){
let result;
let targetType = checkedType(target);
if(targetType === 'Object'){
result = {};
}else if(targetType === 'Array'){
result = [];
}else{
result = target;
}
//遍历目标对象
for(let i in target){
let value = target[i];
//判断目标结构的每个值里面是否存在对象或者数组
if(checkedTypevalue === 'Object' || checkedType(value) === 'Array'){
result[i] = clone(value);
}else{
result[i] = value;
}
}
return result;
}
复制代码
三、函数库lodash
lodash函数库也提供用于深拷贝的方法,程序示例:
var _ = require('lodash');
var obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);// false
复制代码
async函数就是Generator函数的语法糖,async函数就是将Generator函数的星号(*)替换成async将yield替换成await.
(1)内置执行器 Generator()函数的执行必需要靠执行器,async函数自带函数执行器。因此async函数的执行与普通函数同样,都只要一行。
(2)更好的语义化 async 和 await,比起星号和 yield,语义更清楚了。async 表示函数里有异步操做,await 表示紧跟在后面的表达式须要等待结果。
(3)更广的实用性 co 函数库约定,yield 命令后面只能是 Thunk 函数或 Promise 对象,而 async 函数的 await 命令后面,能够跟 Promise 对象和原始类型的值。