JS中的數(shù)據(jù)類型
基礎數(shù)據(jù)類型
number,string,boolean,undefined,null,symbol這種办绝,基本數(shù)據(jù)類型都是直接存儲在內存中的內存棧上的赃梧,數(shù)據(jù)本身的值就是存儲在椗宸空間里面
引用數(shù)據(jù)類型
object。存儲在內存棧上的是指針,指向內存堆中的對象本身瓦糟。
基本數(shù)據(jù)類型和引用類型的區(qū)別主要在于基本數(shù)據(jù)類型是分配在棧上的,而引用類型是分配在堆上的
實現(xiàn)方法
序列反序列
JSON.parse(JSON.stringify())的原理就是將源數(shù)據(jù)序列化稱JSON字符串赴蝇,然后再解析JSON字符串菩浙,構造由字符串描述的JavaScript值或對象。當拷貝JSON字符串的時候會開辟一個新的內存地址,從而完全切斷和源數(shù)據(jù)的聯(lián)系劲蜻。
優(yōu)勢:在開發(fā)中只考慮JSON對象(string, number, 對象, 數(shù)組, boolean或 null)的話用這個就可以了陆淀。
存在的問題:
- undefined、Symbol和function字段會丟失;
- RegExp和Error會變成空對象{};
- Date對象會變成字符串;
- NaN先嬉、Infinite和-Infinite都會變轉成null;
- 如果是由構造函數(shù)實例化出來的函數(shù)轧苫,原型上的constructor也會消失
- 如果對包含循環(huán)引用的對象(對象之間相互引用位衩,形成無限循環(huán))執(zhí)行此方法秩仆,會拋出錯誤
lodash的cloneDeep
- 優(yōu)勢:
- 可以拷貝循環(huán)引用(閉環(huán))的對象;
- 可以拷貝ES6 引入的大量新的標準對象
/** `Object#toString` result references. */
var argsTag = '[object Arguments]',
arrayTag = '[object Array]',
boolTag = '[object Boolean]',
dateTag = '[object Date]',
errorTag = '[object Error]',
funcTag = '[object Function]',
mapTag = '[object Map]',
numberTag = '[object Number]',
objectTag = '[object Object]',
regexpTag = '[object RegExp]',
setTag = '[object Set]',
stringTag = '[object String]',
weakMapTag = '[object WeakMap]';
var arrayBufferTag = '[object ArrayBuffer]',
float32Tag = '[object Float32Array]',
float64Tag = '[object Float64Array]',
int8Tag = '[object Int8Array]',
int16Tag = '[object Int16Array]',
int32Tag = '[object Int32Array]',
uint8Tag = '[object Uint8Array]',
uint8ClampedTag = '[object Uint8ClampedArray]',
uint16Tag = '[object Uint16Array]',
uint32Tag = '[object Uint32Array]';
- 不足:
- 不能拷貝自身不可枚舉類型的屬性(enumerable);
- 不能拷貝普通數(shù)組的屬性象浑,除了RegExp.exec()返回的數(shù)組(只能復制出input和index屬性威沫,groups也不能復制);
- 不能拷貝BOM對象以及DOM對象(用cloneDeepWith)
迭代遞歸
for...in循環(huán)是 遍歷對象的每一個可枚舉屬性,包括原型鏈上面的可枚舉屬性,
Object.keys()只是遍歷自身的可枚舉屬性,不可以遍歷原型鏈上的可枚舉屬性.
而Object.getOwnPropertyNames()則是遍歷自身所有屬性(不論是否是可枚舉的),不包括原型鏈上面的顶岸。
- 拷貝原型:Object.create(proto, [propertiesObject])
- 拷貝不可枚舉屬性:Object.getOwnPropertyDescriptors(obj)
- 拷貝symbol屬性:Object.getOwnPropertySymbols(obj)
- 拷貝循環(huán)引用屬性: 借助hash表
function isObject(obj) {
if ((typeof (obj) == 'object' || typeof (obj) == 'function') && obj !== null) {
return true;
}
}
function deepClone(obj, hash = new WeakMap()) {
if (!isObject(obj)) {
return obj
}
let tempObj;
switch (obj.constructor) {
case RegExp:
tempObj = new RegExp(obj);
break;
case Date:
tempObj = new Date(obj);
break;
case Function:
case Error:
tempObj = obj;
break;
default:
// 查詢哈希表展鸡,防止循環(huán)屬性(環(huán))
if (hash.has(obj)) {
return hash.get(obj);
}
// 初始化拷貝對象
tempObj = Array.isArray(obj) ? [] : {};
// 獲取源對象所有屬性描述符,獲取到的value屬于淺拷貝來的
let allDesc = Object.getOwnPropertyDescriptors(obj);
// 創(chuàng)建對象诵盼,拷貝原型蓄拣,拷貝不可枚舉屬性
tempObj = Object.create(Object.getPrototypeOf(obj), allDesc);
// 獲取原對象全部symbol屬性
let symKeys = Object.getOwnPropertySymbols(obj);
// 拷貝symbol屬性
if (symKeys.length > 0) {
symKeys.forEach(symKey => {
if (isObject(obj[symKey])) {
hash.set(obj, tempObj);
tempObj[symKey] = deepClone(obj[symKey], hash);
} else {
tempObj[symKey] = obj[symKey];
}
})
}
// 拷貝可枚舉屬性
Object.getOwnPropertyNames(obj).map(key => {
if (isObject(obj[key])) {
hash.set(obj, tempObj);
tempObj[key] = deepClone(obj[key], hash);
} else {
tempObj[key] = obj[key];
}
})
}
return tempObj;
}
let person2 = deepClone(person1);
console.log("person1:", person1);
console.log("person2:", person2);
function Person(name, age) {
this.name = name;
}
let sym = Symbol('我是一個Symbol');
Person.prototype = {
constructor: Person,
shape: 'circle', //設置原型上可枚舉
[sym]: 'symbol' //設置原型上的 Symol 類型鍵
}
//設置原型上不可枚舉
Object.defineProperty(Person.prototype, 'year', {
value: 55,
enumerable: false
});
var person1 = new Person("Lily");
// 設置自身子對象
person1.obj = {
name: '我是一個對象',
id: 1,
object: {
name: '子對象',
id: '20181215',
subObject: {
name: '子對象的子對象',
id: '18:37'
}
}
};
//設置自身不可枚舉
Object.defineProperty(person1, 'year2', {
value: {
name: 'year2',
id: '2117'
},
enumerable: false,
writable: true,
configurable: true,
});
//設置自身 Symol 類型鍵
let sym2 = Symbol('我是一個Symbol');
let sym3 = Symbol('我是一個Symbol');
person1[sym2] = {
name: 'symbol2',
id: 'id123',
[sym3]: '內嵌symbol'
};
// 設置自身循環(huán)引用
person1.loopObj = person1;
let person2 = deepClone(person1);
// 修改person1的symbole屬性
Object.getOwnPropertySymbols(person1).forEach(symKey => {
person1[symKey] = '新的symbol值'
})
// ----------------------------------------------
// 修改person1的不可枚舉屬性
Object.defineProperty(person1, 'year2', {
value: '新的不可枚舉值',
});
// 或者
// person1.year2 = '新的不可枚舉值'
console.log("person1.year2:", person1.year2);
// ----------------------------------------------
console.log("person1:", person1);
console.log("person2:", person2);
console.log("Person.prototype.isPrototypeOf(person2):", Person.prototype.isPrototypeOf(person2));
console.log("person1.year2===person2.year2:", person1.year2 === person2.year2)
console.log("person1[sym2]===person2[sym2]:", person1[sym2] === person2[sym2])
console.log("person1.obj.object.subObject===person2.obj.object.subObject:", person1.obj.object.subObject ===
person2.obj.object.subObject)
效率比較:
var x = {};
for (var i = 0; i < 1000; i++) {
x[i] = {};
for (var j = 0; j < 1000; j++) {
x[i][j] = Math.random();
}
}
var start = Date.now();
var y = clone(x);
console.log(Date.now() - start);
深復制方法 | JSON.parse | _.cloneDeep | deepClone |
---|---|---|---|
耗時 | 654 | 213 | 893 |
總結:總的來說沒有一個方法是放諸四海而皆準的拗小,建議使用 lodash的cloneDeep重罪,但是對于小程序來說還是大了點,所以我平常開發(fā)小程序就是用JSON.parse(JSON.stringify())哀九。