javascript中數(shù)據(jù)存儲分為兩種類型:
1喊崖、基本類型
2、引用類型
基本類型保存在棧內(nèi)存中雇逞,引用類型的數(shù)據(jù)則保存在堆內(nèi)存中荤懂,引用數(shù)據(jù)類型的變量是一個指向 堆內(nèi)存中 實際對象的引用,存在棧中
淺拷貝 是指在創(chuàng)建新一組數(shù)據(jù)中塘砸,如果屬性是基本類型节仿,拷貝的就是基本類型的值。如果屬性是引用類型掉蔬,拷貝的就是內(nèi)存地址廊宪;
在JavaScript中存在淺拷貝的現(xiàn)象有:
Object.assign()
假如源對象的屬性值是一個對象的引用矾瘾,那么它也只指向那個引用。也就是說箭启,如果對象的屬性值為簡單類型(如string壕翩, number),通過Object.assign({},srcObj);得到的新對象為深拷貝傅寡;如果屬性值為對象或其它引用類型放妈,那對于這個對象而言其實是淺拷貝的。
var obj = {
age: 18,
nature: ['smart', 'good'],
names: {
name1: 'fx',
name2: 'xka'
},
love: function () {
console.log('fx is a great girl')
}
}
var newObj = Object.assign({}, obj );
Array.prototype.slice() 荐操,
const fxArr = ["One", "Two", "Three"]
const fxArrs = fxArr.slice(0)
fxArrs[1] = "love";
console.log(fxArr) // ["One", "Two", "Three"]
console.log(fxArrs) // ["One", "love", "Three"]
Array.prototype.concat()
const fxArr = ["One", "Two", "Three"]
const fxArrs = fxArr.concat()
fxArrs[1] = "love";
console.log(fxArr) // ["One", "Two", "Three"]
console.log(fxArrs) // ["One", "love", "Three"]
使用拓展運算符實現(xiàn)的復(fù)制
const fxArr = ["One", "Two", "Three"]
const fxArrs = [...fxArr]
fxArrs[1] = "love";
console.log(fxArr) // ["One", "Two", "Three"]
console.log(fxArrs) // ["One", "love", "Three"]
** for ..in **
function shallowClone(obj) {
const newObj = {};
for(let prop in obj) {
if(obj.hasOwnProperty(prop)){
newObj[prop] = obj[prop];
}
}
return newObj;
}
以上都存在著淺拷貝的現(xiàn)象芜抒;
深拷貝
深拷貝開辟一個新的棧,兩個對象屬性完全相同托启,但是對應(yīng)兩個不同的地址宅倒,修改一個對象的屬性,不會改變另一個對象的屬性
常見的深拷貝方式有:
_.cloneDeep() 利用方便的js庫工具 ------ lodash
const _ = require('lodash');
const obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
const obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);// false
jQuery.extend()
const $ = require('jquery');
const obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
const obj2 = $.extend(true, {}, obj1);
console.log(obj1.b.f === obj2.b.f); // false
JSON.stringify() ----但是這種方式存在弊端驾中,會忽略undefined唉堪、symbol和函數(shù)
const obj2=JSON.parse(JSON.stringify(obj1));
const obj = {
name: 'A',
name1: undefined,
name3: function() {},
name4: Symbol('A')
}
const obj2 = JSON.parse(JSON.stringify(obj));
console.log(obj2); // {name: "A"}
手寫循環(huán)遞歸
function deepClone(obj, hash = new WeakMap()) {
if (obj === null) return obj; // 如果是null或者undefined我就不進(jìn)行拷貝操作
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
// 可能是對象或者普通的值 如果是函數(shù)的話是不需要深拷貝
if (typeof obj !== "object") return obj;
// 是對象的話就要進(jìn)行深拷貝
if (hash.get(obj)) return hash.get(obj);
let cloneObj = new obj.constructor();
// 找到的是所屬類原型上的constructor,而原型上的 constructor指向的是當(dāng)前類本身
hash.set(obj, cloneObj);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// 實現(xiàn)一個遞歸拷貝
cloneObj[key] = deepClone(obj[key], hash);
}
}
return cloneObj;
}
遞歸實現(xiàn)的深拷貝里面用到了 WeakMap 用來創(chuàng)建hash結(jié)構(gòu)
WeakMap結(jié)構(gòu)與Map結(jié)構(gòu)類似,也是用于生成鍵值對的集合肩民。
WeakMap與Map的區(qū)別有兩點唠亚。
首先,WeakMap只接受對象作為鍵名(null除外)持痰,不接受其他類型的值作為鍵名灶搜。
其次,WeakMap的鍵名所指向的對象工窍,不計入垃圾回收機(jī)制割卖。
// WeakMap 可以使用 set 方法添加成員
const wm1 = new WeakMap();
const key = {foo: 1};
wm1.set(key, 2);
wm1.get(key) // 2
// WeakMap 也可以接受一個數(shù)組,
// 作為構(gòu)造函數(shù)的參數(shù)
const k1 = [1, 2, 3];
const k2 = [4, 5, 6];
const wm2 = new WeakMap([[k1, 'foo'], [k2, 'bar']]);
wm2.get(k2) // "bar"
WeakMap 的語法
WeakMap 與 Map 在 API 上的區(qū)別主要是兩個患雏,
一是沒有遍歷操作(即沒有keys()鹏溯、values()和entries()方法),也沒有size屬性淹仑。
二是無法清空丙挽,即不支持clear方法。因此匀借,WeakMap只有四個方法可用:get()颜阐、set()、has()吓肋、delete()凳怨。
Map
JavaScript 的對象(Object),本質(zhì)上是鍵值對的集合(Hash 結(jié)構(gòu)),但是傳統(tǒng)上只能用字符串當(dāng)作鍵肤舞。這給它的使用帶來了很大的限制.
ES6 提供了 Map 數(shù)據(jù)結(jié)構(gòu)紫新。它類似于對象,也是鍵值對的集合李剖,但是“鍵”的范圍不限于字符串弊琴,各種類型的值(包括對象)都可以當(dāng)作鍵。也就是說杖爽,Object 結(jié)構(gòu)提供了“字符串—值”的對應(yīng),Map 結(jié)構(gòu)提供了“值—值”的對應(yīng)紫皇,是一種更完善的 Hash 結(jié)構(gòu)實現(xiàn)慰安。如果你需要“鍵值對”的數(shù)據(jù)結(jié)構(gòu),Map 比 Object 更合適聪铺。
const m = new Map();
const o = {p: 'Hello World'};
m.set(o, 'content')
m.get(o) // "content"
m.has(o) // true
m.delete(o) // true
m.has(o) // false
Map 也可以接受一個數(shù)組作為參數(shù)化焕。該數(shù)組的成員是一個個表示鍵值對的數(shù)組。
const map = new Map([
['name', '張三'],
['title', 'Author']
]);
map.size // 2
map.has('name') // true
map.get('name') // "張三"
map.has('title') // true
map.get('title') // "Author"
如果對同一個鍵多次賦值铃剔,后面的值將覆蓋前面的值撒桨。
const map = new Map();
map.set(1, 'aaa').set(1, 'bbb');
map.get(1) // "bbb"
實例的屬性和操作方法:
(1)size 屬性
(2)set(key, value)
(3)get(key)
(4)has(key)
(5)delete(key)
(6)clear()
{
let map = new Map();
map.set('foo', 11);
map.set('bar', 22);
map.size ;// 2
map.get('foo');//t1
map.has('boo'); //true
map.delete('foo'); //true
map.clear();
map.size // 0
}
遍歷方法
Map 結(jié)構(gòu)原生提供三個遍歷器生成函數(shù)和一個遍歷方法。
keys():返回鍵名的遍歷器键兜。
values():返回鍵值的遍歷器凤类。
entries():返回所有成員的遍歷器。
forEach():遍歷 Map 的所有成員普气。
Map 結(jié)構(gòu)的默認(rèn)遍歷器接口(Symbol.iterator屬性)谜疤,就是entries方法。