一、理解
?深拷貝 與 淺拷貝
?針對(duì)于 引用類型(Object,Array,Function) 來說的
淺拷貝:在棧中分配一塊新內(nèi)存,拷貝需要拷貝的值,
? ? ? ? ? 對(duì)簡單數(shù)據(jù)類型,就是拷貝值;對(duì)復(fù)雜數(shù)據(jù)類型,就是拷貝了一份棧內(nèi)存儲(chǔ)的堆內(nèi)存的地址
?深拷貝:在堆中重新分配新內(nèi)存,存儲(chǔ)新的拷貝數(shù)據(jù);
????????????????在棧中分配新內(nèi)存,存儲(chǔ)新數(shù)據(jù)分配的堆的地址。
標(biāo)準(zhǔn)淺拷貝:
// =
let obj1 = obj
obj.a = '88888'
console.log(obj)
console.log( obj1)
結(jié)果:
實(shí)現(xiàn)方式:
?淺拷貝的實(shí)現(xiàn)方式:
? ? ? ? 1. 基于Object.assign()
? ? ? ? 2. 基于for in = 直接賦值
? ? ? ? 3. ... 原地展開
?深拷貝的三種實(shí)現(xiàn)方式:
? ? ? 1. 遞歸實(shí)現(xiàn)(重點(diǎn))
? ? ? 2. JSON.stringify + JSON.parse 方法
? ? ? 3. JQuery的$extend方法(了解)
?其他奇怪的api方法?可能非真正的深拷貝
? ? ?1. slice 一維數(shù)組深拷貝,多維拷貝不徹底(針對(duì)數(shù)組)
? ? ?2. Object.assign
? ? ?3.?Object.create
二、一些奇怪的api的使用及測(cè)試
1.Object.assign()
Object.assign用法:
? 用于將源對(duì)象的所有可枚舉屬性到目標(biāo)對(duì)象,返回目標(biāo)對(duì)象
?至少需要兩個(gè)對(duì)象做參數(shù):Object.assign(目標(biāo)對(duì)象, 源對(duì)象1,...)
?只要有一個(gè)參數(shù)不是對(duì)象,就會(huì)拋出TypeError錯(cuò)誤
?如果有同名屬性,后覆蓋前;只復(fù)制自身屬性(也會(huì)復(fù)制Symbol值的屬性),不復(fù)制不可枚舉/繼承屬性
?可用于: 為對(duì)象添加屬性或方法;克隆對(duì)象;合并多個(gè)對(duì)象;為屬性指定默認(rèn)值
拷貝實(shí)現(xiàn):
// Object.assign
let obj = {
a: 1,
b: 2,
c: [1, 2, 3],
d: {
g:777,
s: {},
f: function() { console.log('f')}
}
}
let obj1 = Object.assign({}, obj)
console.log(obj1)
拷貝結(jié)果:
?拷貝測(cè)試:
對(duì)第一層的測(cè)試:
obj1.a = 666 // 對(duì)第一層的基本數(shù)據(jù)類型修改=>深拷貝
obj1.c = [] // 對(duì)第一層的引用數(shù)據(jù)類型 => 深拷貝
// obj1.c[0] = '000' // 對(duì)第一層的引用數(shù)據(jù)類型 內(nèi)的某一項(xiàng)進(jìn)行修改=>淺拷貝
//obj1.d.g = 8888 //
obj1.d.s.w = '9999999' // 對(duì)內(nèi)層引用數(shù)據(jù)類型進(jìn)行修改(添加)=>淺拷貝
console.log(obj)
console.log( obj1)
測(cè)試結(jié)果:
?其他測(cè)試:
obj1.c[0] = '000' // 對(duì)第一層的引用數(shù)據(jù)類型 內(nèi)的某一項(xiàng)進(jìn)行修改=>淺拷貝
obj1.d.g = 8888 // 對(duì)內(nèi)層基礎(chǔ)數(shù)據(jù)類型修改 => 淺拷貝
obj1.d.s.w = '9999999' // 對(duì)內(nèi)層引用數(shù)據(jù)類型某一項(xiàng) 進(jìn)行修改(添加)=>淺拷貝
obj1.d.f = {} // 對(duì)內(nèi)層引用數(shù)據(jù)類型 整體修改 => 淺拷貝
console.log(obj)
console.log( obj1)
測(cè)試結(jié)果:
小總結(jié):
?對(duì)拷貝的第一層的基礎(chǔ)數(shù)據(jù)類型 實(shí)現(xiàn)了深拷貝(修改拷貝值,不影響新值);
?對(duì)深層數(shù)據(jù),依然是淺拷貝,修改數(shù)據(jù)值會(huì)影響原數(shù)據(jù)
注意:當(dāng)整體修改第一層引用數(shù)據(jù)后,新對(duì)象的整個(gè)引用數(shù)據(jù)的指向應(yīng)該是修改后的,這時(shí)再對(duì)引用數(shù)據(jù)中某一項(xiàng)修改時(shí),不會(huì)影響原來的對(duì)象;但直接對(duì)于第一層引用數(shù)據(jù)類型的某一項(xiàng)進(jìn)行修改時(shí),還是會(huì)影響原來的對(duì)象。
2.Object.create
api用法:
?方法用于 創(chuàng)建一個(gè)新對(duì)象,使用現(xiàn)有的對(duì)象來提供新創(chuàng)建的對(duì)象的proto
?參數(shù): Object.create(proto新建對(duì)象的原型對(duì)象,[添加到新創(chuàng)建對(duì)象的可枚舉屬性])
?返回值:在指定原型對(duì)象上添加新屬性后的對(duì)象
拷貝:
// 3. Object.create 淺拷貝
let obj1 = Object.create(obj)
console.log(obj1) // obj1 是一個(gè)空對(duì)象,其原型為obj;obj1.__proto__是obj的淺拷貝
console.log(obj1.__proto__ === obj) // true
運(yùn)行結(jié)果:
淺拷貝,新對(duì)象的原型為舊對(duì)象
obj1 是一個(gè)空對(duì)象,其原型為obj;obj1.__proto__是obj的淺拷貝
3. ES6 ...擴(kuò)展運(yùn)算符
代碼實(shí)現(xiàn):
// ES6 擴(kuò)展運(yùn)算符
let obj1 = { ...obj }
console.log(obj1)
測(cè)試結(jié)果同Object.assign
4.slice+concat實(shí)現(xiàn)拷貝(針對(duì)數(shù)組)
slice:
實(shí)現(xiàn)及測(cè)試:
// 針對(duì)數(shù)組
let arr = ['a', 'b', 'c', {d:4}]
// slice
let arr1 = arr.slice(0)
// console.log(arr)
// console.log(arr1)
arr1[0] = '1'
arr1[3].d = '777'
console.log(arr)
console.log(arr1)
運(yùn)行結(jié)果:
?可見,slice 對(duì)于數(shù)組中的基礎(chǔ)數(shù)據(jù)類型,能夠?qū)崿F(xiàn)深拷貝,對(duì)于引用數(shù)據(jù)類型,無法實(shí)現(xiàn)深拷貝。
concat:
// concat
let arr1 = arr.concat()
// console.log(arr)
// console.log(arr1)
測(cè)試結(jié)果與slice相同。
三、實(shí)現(xiàn)深拷貝的三種方式
1. JSON.stringify + JSON.parse
拷貝實(shí)現(xiàn):
let obj = {
a: 1,
b: 2,
c: [1, 2, 3],
d: {
g:777,
s: {w:'www'},
f: function() { console.log('f')}
},
e: undefined,
p: NaN,
date:new Date()
}
// JSON.stringify + JSON.parse 方法
let obj1 = JSON.parse(JSON.stringify(obj))
console.log(obj)
console.log(obj1)
結(jié)果截圖:
可見,拷貝對(duì)象無函數(shù)f、無undefined屬性e,即該方法無法實(shí)現(xiàn)對(duì)函數(shù)、對(duì)undefined值的拷貝?;
經(jīng)測(cè)試,為深拷貝。
該方法存在的問題:
1.對(duì)象中有時(shí)間類型時(shí),序列化后會(huì)變成字符串類型;
2.對(duì)象中有function、undefined類型的數(shù)據(jù),會(huì)直接丟失;
3.對(duì)象中有NaN、Infinity、-Infinity時(shí),序列化后會(huì)顯示null;
4.對(duì)象循環(huán)引用時(shí),會(huì)報(bào)錯(cuò)。
2.JQuery中的$.extend方法可以實(shí)現(xiàn)深拷貝(知道)
3.遞歸實(shí)現(xiàn)深拷貝(待升級(jí)完善版)
深拷貝的簡單遞歸實(shí)現(xiàn):
let obj = {
a: 1,
b: 2,
c: [1, 2, 3],
d: {
g:777,
s: {w:'www'},
f: function() { console.log('f')}
},
e: undefined,
p: NaN,
date:new Date()
}
// 遞歸實(shí)現(xiàn)深拷貝--簡單版()
function deepClone(obj) {
let newObj = null
// if (obj === null) return obj//???
if (typeof obj == 'undefined') return undefined
if (typeof obj === 'function') return obj//淺拷貝函數(shù)?
if (obj.constructor == Date) return new Date(obj)
if(obj.constructor == RegExp) return new RegExp(obj)
if (typeof obj == 'object') {
newObj = obj instanceof Array ? [] : {}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = deepClone(obj[key])
}
}
} else {
newObj = obj
}
return newObj
}
let obj1 = deepClone(obj)
console.log(obj)
console.log(obj1)
運(yùn)行截圖:
復(fù)雜版:
// 深拷貝 復(fù)雜版?
function checkType(target) {
console.log(Object.prototype.toString.call(target).slice(8,-1))
return Object.prototype.toString.call(target).slice(8,-1)
}
function deepClone2(data) {
const obj = checkType(data) === 'Array' ? [] : {}
let arr = ['Object','Array']
if (arr.includes(checkType(data))) {
for (let key in data) {
let value = data[key]
//value為簡單類型,直接賦值
if (!arr.includes(checkType(value))) {
obj[key] = value
} else {
// 定義一個(gè)映射,初始化時(shí),將data本身加入映射中
const map = new WeakMap()
// 如果拷貝的是復(fù)雜數(shù)據(jù)類型第一次拷貝后存入map
// 第二次再遇到該值時(shí),直接賦值為null,結(jié)束遞歸
map.set(data, true)
if (map.has(value)) {
obj[key] = null
} else {
map.set(value, true);
obj[key] = deepClone2(value)
}
}
}
} else {
return data
}
return obj
}
let obj1 = deepClone2(obj)
console.log(obj)
console.log(obj1)
結(jié)果:
文章來源:http://www.zghlxwxcb.cn/news/detail-484476.html
?文章來源地址http://www.zghlxwxcb.cn/news/detail-484476.html
到了這里,關(guān)于JS之深拷貝與淺拷貝的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!