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 對象和原始類型的值。