最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JS淺拷貝深拷貝原理以及常用方法大全

 更新時(shí)間:2026年03月30日 10:12:15   作者:不甜情歌  
在JavaScript中,深拷貝和淺拷貝是兩種常見(jiàn)的對(duì)象復(fù)制方式,它們的主要區(qū)別在于復(fù)制的深度和對(duì)原始對(duì)象數(shù)據(jù)的引用關(guān)系,這篇文章主要介紹了JS淺拷貝深拷貝原理以及常用方法的相關(guān)資料,需要的朋友可以參考下

前言

在JavaScript中,拷貝是我們?nèi)粘i_(kāi)發(fā)中高頻用到的操作,但很多小伙伴會(huì)被“淺拷貝”和“深拷貝”搞暈——為什么有時(shí)候修改原對(duì)象,新對(duì)象也跟著變?有時(shí)候卻完全不受影響?

其實(shí)答案很簡(jiǎn)單:拷貝只針對(duì)引用類(lèi)型(對(duì)象、數(shù)組等),基本類(lèi)型(數(shù)字、字符串、布爾值)不存在“深淺”一說(shuō)(賦值就是直接拷貝值)。今天我們就結(jié)合具體代碼,把淺拷貝和深拷貝講透。

先明確核心概念

無(wú)論是淺拷貝還是深拷貝,核心都是「基于原對(duì)象,創(chuàng)建一個(gè)新對(duì)象」,區(qū)別在于:拷貝的“深度”不同,是否會(huì)拷貝原對(duì)象內(nèi)部的子引用類(lèi)型。

重點(diǎn)牢記:引用類(lèi)型的賦值,賦值的是「內(nèi)存地址」,不是值本身;而拷貝是創(chuàng)建新對(duì)象,只是拷貝的“層數(shù)”有差異。

一、淺拷貝:只抄“表面”,子對(duì)象還是“共用”

淺拷貝的特點(diǎn):只拷貝原對(duì)象的第一層屬性。如果原對(duì)象的屬性值是引用類(lèi)型(比如子對(duì)象、數(shù)組),那么拷貝的不是值,而是這個(gè)子對(duì)象的內(nèi)存地址——也就是說(shuō),原對(duì)象和新對(duì)象的子引用類(lèi)型,共用同一個(gè)“空間”,修改其中一個(gè),另一個(gè)會(huì)跟著變。

1. 數(shù)組淺拷貝:slice() 方法

const arr = ['a', 'b', 'c', 'd', {age: 18}]
const arr2 = arr.slice(0) // 淺拷貝數(shù)組arr(slice不傳參、傳0效果一致,均拷貝整個(gè)數(shù)組)
arr[4].age = 20 // 修改原數(shù)組中的子對(duì)象屬性
console.log(arr2); // 輸出:['a','b','c','d',{age:20}]

?? 解析:slice(0) 會(huì)創(chuàng)建一個(gè)新數(shù)組,拷貝原數(shù)組的第一層元素。但數(shù)組中第4個(gè)元素是對(duì)象(引用類(lèi)型),拷貝的是它的地址,所以修改原數(shù)組中這個(gè)對(duì)象的age,新數(shù)組arr2中的對(duì)應(yīng)對(duì)象也會(huì)同步變化。

2. 數(shù)組淺拷貝:擴(kuò)展運(yùn)算符 [...arr]

const a = [1, 2, {age: 18}]
let c = [...a] // 用擴(kuò)展運(yùn)算符淺拷貝數(shù)組a
a[2].age = 19 // 修改原數(shù)組中的子對(duì)象屬性
console.log(c); // 輸出:[1,2,{age:19}]

?? 解析:擴(kuò)展運(yùn)算符是最簡(jiǎn)潔的數(shù)組淺拷貝方式之一,原理和slice()一致,只拷貝第一層。子對(duì)象依然是共用地址,修改原對(duì)象子屬性,新對(duì)象會(huì)受影響。

3. 數(shù)組淺拷貝:concat() 方法

const a = [1, 2, 3, {age: 18}]
const d = [].concat(a) // 用concat()淺拷貝數(shù)組a
a[3].age = 20 // 修改原數(shù)組中的子對(duì)象屬性
console.log(d); // 輸出:[1,2,3,{age:20}]

?? 解析:concat() 原本用于拼接數(shù)組,當(dāng)只傳入一個(gè)原數(shù)組時(shí),會(huì)返回一個(gè)新數(shù)組,本質(zhì)也是淺拷貝,子引用類(lèi)型依然共用地址。

4. 數(shù)組淺拷貝:toReversed().reverse() (特殊小技巧)

const arr = [1, 2, 3]
const arr2 = arr.toReversed().reverse() // 先反轉(zhuǎn)再反轉(zhuǎn),得到原數(shù)組的淺拷貝
console.log(arr2); // 輸出:[1,2,3]

?? 解析:toReversed() 會(huì)返回一個(gè)反轉(zhuǎn)后的新數(shù)組(不修改原數(shù)組),再調(diào)用reverse() 反轉(zhuǎn)回來(lái),相當(dāng)于創(chuàng)建了一個(gè)和原數(shù)組完全一樣的新數(shù)組,屬于淺拷貝。

5. 對(duì)象淺拷貝:Object.assign() 方法

const obj = {
  name: '跳跳虎',
  like: ['偵探'] // 子屬性是數(shù)組(引用類(lèi)型)
}
const newObj = Object.assign({}, obj) // 淺拷貝obj到新對(duì)象
obj.like[0] = '蹦蹦跳跳' // 修改原對(duì)象的子數(shù)組
console.log(newObj); // 輸出:{name: '跳跳虎', like: ['蹦蹦跳跳']}

?? 解析:Object.assign(target, source) 會(huì)將source對(duì)象的屬性拷貝到target對(duì)象,返回target。這里target是空對(duì)象,相當(dāng)于創(chuàng)建了一個(gè)新對(duì)象,拷貝原對(duì)象的第一層屬性。子數(shù)組like是引用類(lèi)型,拷貝的是地址,所以修改原對(duì)象的like,newObj的like也會(huì)變。

6. 手動(dòng)實(shí)現(xiàn)淺拷貝

const obj = {
  name: '跳跳虎',
  age: 18,
  like: {
    n: '偵探',
    m: '蹦蹦跳跳'
  }
}
// 自定義淺拷貝函數(shù)
function shallowCopy(obj) {
  let o = {} // 創(chuàng)建新對(duì)象
  // 遍歷原對(duì)象,拷貝第一層屬性(只拷貝自身可枚舉屬性)
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) { // 排除原型鏈上的屬性
      o[key] = obj[key] // 引用類(lèi)型拷貝地址,基本類(lèi)型拷貝值
    }
  }
  return o
}
const newObj = shallowCopy(obj)
obj.like.m = '麥芽糖' // 修改原對(duì)象的子對(duì)象屬性
console.log(newObj); // 輸出:like.m 也變成了'麥芽糖'

?? 解析:手動(dòng)實(shí)現(xiàn)的淺拷貝邏輯很簡(jiǎn)單——創(chuàng)建新對(duì)象,遍歷原對(duì)象的自身屬性,將屬性值賦值給新對(duì)象。和內(nèi)置方法一樣,子引用類(lèi)型拷貝的是地址,會(huì)受原對(duì)象影響。

二、深拷貝:層層拷貝,完全“獨(dú)立”

深拷貝的特點(diǎn):層層拷貝,直到所有層級(jí)的屬性都是基本類(lèi)型。新對(duì)象和原對(duì)象完全獨(dú)立,修改原對(duì)象的任何屬性(包括子對(duì)象、子數(shù)組),新對(duì)象都不會(huì)有任何變化——相當(dāng)于“復(fù)制粘貼”了一整個(gè)對(duì)象,連里面的“小零件”都復(fù)制了一份。

1. 深拷貝:structuredClone() 方法

const obj = {
  name: '跳跳虎',
  age: 18,
  like: {
    n: '偵探',
    m: '蹦蹦跳跳'
  },
  a: 123n, // BigInt類(lèi)型
  date: new Date(), // Date類(lèi)型
  map: new Map([['key1', 'value1']]), // Map類(lèi)型
  say() {
    console.log('hello');
  }
}
const newObj = structuredClone(obj) // 深拷貝obj
obj.like.m = '麥芽糖' // 修改原對(duì)象的子對(duì)象屬性
obj.date.setFullYear(2025) // 修改原對(duì)象的Date屬性
console.log(newObj); 
// 輸出:like.m 依然是'蹦蹦跳跳',date依然是原日期(不受影響)
// 注意:say()函數(shù)被忽略,map被正??截?/pre>

?? 解析:structuredClone() 是ES2022新增的深拷貝方法,用法簡(jiǎn)單,能處理大部分引用類(lèi)型(對(duì)象、數(shù)組、Map、Set、Date、RegExp、ArrayBuffer等),支持BigInt類(lèi)型,且能正確拷貝循環(huán)引用(比如obj.self = obj,不會(huì)報(bào)錯(cuò))。

注意:structuredClone() 有以下局限:

  • 無(wú)法拷貝函數(shù)(普通函數(shù)、箭頭函數(shù)、類(lèi)方法均會(huì)被忽略);
  • 無(wú)法拷貝原型鏈上的屬性(只拷貝對(duì)象自身屬性);
  • 無(wú)法拷貝Error、Function、GeneratorFunction、AsyncFunction等類(lèi)型;
  • 無(wú)法拷貝Symbol類(lèi)型(會(huì)被忽略);
  • 無(wú)法拷貝DOM節(jié)點(diǎn)(會(huì)報(bào)錯(cuò))。

2. 深拷貝:JSON.parse(JSON.stringify(obj))

const obj = {
  name: '跳跳虎',
  age: 18,
  like: {
    n: '偵探',
    m: '蹦蹦跳跳'
  },
  say() {
    console.log('hello');
  },
  a: undefined, // undefined類(lèi)型
  b: null,
  c: NaN, // NaN類(lèi)型
  d: Infinity // 無(wú)窮大類(lèi)型
}
const oo = JSON.parse(JSON.stringify(obj)) // 深拷貝
obj.like.m = '麥芽糖' // 修改原對(duì)象
console.log(oo); 
// 輸出:{name: '跳跳虎', age:18, like:{n:'偵探',m:'蹦蹦跳跳'}, b:null, c:null, d:null}

?? 解析:這是最常用的“民間深拷貝方法”,原理是:先將對(duì)象轉(zhuǎn)為JSON字符串(JSON.stringify),再將字符串轉(zhuǎn)回對(duì)象(JSON.parse),相當(dāng)于重新創(chuàng)建了一個(gè)完全獨(dú)立的對(duì)象。

注意:這個(gè)方法有明顯局限,無(wú)法處理以下類(lèi)型,會(huì)自動(dòng)忽略或轉(zhuǎn)換:

  • 函數(shù)(say()方法被忽略)

  • BigInt(會(huì)報(bào)錯(cuò),無(wú)法轉(zhuǎn)換)

  • undefined(被忽略)

  • NaN、Infinity(會(huì)轉(zhuǎn)為null)

3. 手動(dòng)實(shí)現(xiàn)深拷貝

const obj = {
  name: '跳跳虎',
  age: 18,
  like: {
    n: '偵探',
    m: '蹦蹦跳跳'
  }
}
// 自定義遞歸深拷貝函數(shù)
function deepClone(obj) {
  let o = {} // 創(chuàng)建新對(duì)象
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) { // 只拷貝自身屬性
      // 判斷當(dāng)前屬性值是否是引用類(lèi)型(且不是null,因?yàn)閚ull的typeof也是object)
      if (typeof(obj[key]) == 'object' && obj[key] !== null) {
        // 遞歸調(diào)用深拷貝,對(duì)子對(duì)象也進(jìn)行深拷貝
        const childObj = deepClone(obj[key])
        o[key] = childObj
      } else {
        // 基本類(lèi)型,直接拷貝值
        o[key] = obj[key]
      }
    }
  }
  return o
}
const newObj = deepClone(obj)
obj.like.m = '麥芽糖' // 修改原對(duì)象
console.log(newObj); // 輸出:like.m 依然是'蹦蹦跳跳'(完全獨(dú)立)

?? 解析:手動(dòng)深拷貝的核心是「遞歸」——遍歷對(duì)象時(shí),如果遇到引用類(lèi)型(且不是null),就遞歸調(diào)用自身,對(duì)這個(gè)子對(duì)象也進(jìn)行深拷貝;如果是基本類(lèi)型,就直接賦值。這樣就能實(shí)現(xiàn)“層層拷貝”,新對(duì)象和原對(duì)象完全獨(dú)立。

補(bǔ)充:這個(gè)基礎(chǔ)版遞歸深拷貝,還可以?xún)?yōu)化(比如處理數(shù)組、Map、Set等),但日常開(kāi)發(fā)中,處理普通對(duì)象和數(shù)組已經(jīng)足夠啦~

三、補(bǔ)充知識(shí)點(diǎn):for...in 遍歷與hasOwnProperty()

在上面的拷貝函數(shù)中,我們都用到了 for (let key in obj)obj.hasOwnProperty(key),很多小伙伴會(huì)疑惑這兩個(gè)的作用,這里簡(jiǎn)單補(bǔ)充:

// 給Object原型添加一個(gè)屬性(模擬原型鏈上的屬性)
Object.prototype.d = 4
const obj = {
  a: 1,
  b: 2,
  c: 3
}
// for...in 會(huì)遍歷對(duì)象自身的屬性 + 原型鏈上的可枚舉屬性
for (let key in obj) {
  // hasOwnProperty(key):判斷key是否是對(duì)象自身的屬性(排除原型鏈上的屬性)
  if (obj.hasOwnProperty(key)) {
    console.log(key); // 輸出:a, b, c(不會(huì)輸出d)
  }
}

?? 解析:拷貝時(shí),我們只需要拷貝對(duì)象「自身的屬性」,不需要拷貝原型鏈上的屬性(比如上面的d屬性),所以用hasOwnProperty() 來(lái)過(guò)濾,避免拷貝多余的屬性。

四、淺拷貝vs深拷貝 總結(jié)表

類(lèi)型核心特點(diǎn)常用方法注意事項(xiàng)
淺拷貝只拷貝第一層,子引用類(lèi)型共用地址slice()、[...arr]、concat()、Object.assign()修改原對(duì)象子引用類(lèi)型,新對(duì)象會(huì)受影響
深拷貝層層拷貝,新對(duì)象與原對(duì)象完全獨(dú)立structuredClone()、JSON.parse(JSON.stringify())各方法有局限(如無(wú)法拷貝函數(shù)、BigInt等)

最后小總結(jié)

1. 基本類(lèi)型不用糾結(jié)深淺拷貝,賦值就是拷貝值;

2. 淺拷貝適合簡(jiǎn)單場(chǎng)景(原對(duì)象只有第一層基本類(lèi)型),高效簡(jiǎn)潔;

3. 深拷貝適合復(fù)雜場(chǎng)景(原對(duì)象有多層引用類(lèi)型),保證數(shù)據(jù)獨(dú)立,避免意外修改;

4. 選擇拷貝方法時(shí),根據(jù)需求選:

  • 需拷貝Date、Map、循環(huán)引用 → 用structuredClone();
  • 簡(jiǎn)單對(duì)象/數(shù)組,無(wú)特殊類(lèi)型 → 用JSON.parse(JSON.stringify())(注意局限);
  • 有特殊類(lèi)型(如函數(shù)、Symbol)、需自定義拷貝規(guī)則 → 用完善版手動(dòng)深拷貝;
  • 簡(jiǎn)單數(shù)組/對(duì)象,無(wú)需深層拷貝 → 用擴(kuò)展運(yùn)算符、Object.assign()等淺拷貝方法

結(jié)合上面的代碼實(shí)例,能輕松區(qū)分淺拷貝和深拷貝,下次遇到拷貝問(wèn)題,再也不用慌啦!

到此這篇關(guān)于JS淺拷貝深拷貝原理以及常用方法大全的文章就介紹到這了,更多相關(guān)JS淺拷貝深拷貝用法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

桃源县| 太白县| 乳山市| 承德县| 前郭尔| 桑植县| 太仓市| 丹江口市| 凤台县| 甘孜县| 普洱| 临清市| 仁化县| 普兰店市| 丽江市| 西贡区| 抚宁县| 祁连县| 扶余县| 绥芬河市| 阿克| 阿城市| 德化县| 南漳县| 罗山县| 五大连池市| 双牌县| 龙陵县| 常熟市| 定边县| 黑龙江省| 时尚| 南安市| 岳西县| 墨脱县| 张家港市| 台湾省| 赣榆县| 珠海市| 隆德县| 湖州市|