App下載

完美解決JavaScript的深淺拷貝

猿友 2020-08-27 10:01:13 瀏覽數(shù) (3284)
反饋

前言

"拷貝"一直都是面試的熱門考題??此坪?jiǎn)單,實(shí)則難住不少面試者,回答的馬馬虎虎,模棱兩可。抽出時(shí)間好好分析總結(jié)一下"拷貝",讓這個(gè)難題徹底消失。

正文

從一則故事講起,昨天因?yàn)獒t(yī)院開不出藥,我拿上藥取小區(qū)藥店去買藥,進(jìn)門之后我問老板有沒有這個(gè)藥,老板轉(zhuǎn)身進(jìn)去一個(gè)小屋子拿了一盒藥,果不其然確實(shí)有,藥的名字和毫克一摸一樣,但是盒子的樣子和廠商不一樣,我問老板:“這兩個(gè)藥是一種藥嗎,盒子不一樣啊,藥的成分是一樣的嗎?”老板說當(dāng)然一樣啊,這個(gè)就和你去買豬肉一樣,同樣是豬身上的肉,只不過是你去這個(gè)超市和去其他超市買場(chǎng)地一樣而已。最后為了安全起見,我還是沒有買那個(gè)藥。

"拷貝"分為淺拷貝和深拷貝。它是針對(duì)對(duì)象來說的,如果不是對(duì)象一切免談。這里的對(duì)象可以理解為我拿的那盒藥,淺拷貝可以理解為老板拿出來的那盒藥,雖然藥的名字和毫克一樣,然后里面的我們不知道是否真的一樣,可能一樣可能不一樣。深拷貝可以理解為我買到了一摸一樣的藥,一層一層的藥名,毫克,廠商,成分都一樣。

總結(jié):

  • 淺拷貝就是針對(duì)對(duì)象的屬性依次進(jìn)行復(fù)制,只復(fù)制一層,不會(huì)遞歸到個(gè)屬性復(fù)制,會(huì)產(chǎn)生引用問題即內(nèi)存地址是指的同一地址。簡(jiǎn)單來說就是拷貝之后和原對(duì)象有關(guān)。
  • 深拷貝就是針對(duì)對(duì)象的各屬性遞歸復(fù)制到新的對(duì)象上,內(nèi)存地址不會(huì)指向同一地址。簡(jiǎn)單來說就是拷貝之后和元對(duì)象無關(guān)。

下面看一個(gè)淺拷貝的例子:

let school={'name':"W3Cschool"};
let my = {age:{count:18},name:"W3Cschool編程獅"};
let all = {...school,...my};
my.age.count=100;
console.log(all);
console.log(my);

結(jié)果:

{ age: { count: 100 }, name: 'W3Cschool編程獅' }
{ age: { count: 100 }, name: 'W3Cschool編程獅' }

結(jié)論是:淺拷貝修改拷貝之后的對(duì)象上的屬性會(huì)把原對(duì)象身上的屬性同時(shí)修改掉。

下面再看一個(gè)深拷貝的例子:

const _ = require("loadsh")
let my = {age:{count:18},name:"W3Cschool編程獅"};
let all = _.cloneDeep(my);
all.age.count =100;
console.log(my);
console.log(all);

結(jié)果:

{ age: { count: 18 }, name: 'W3Cschool編程獅' }
{ age: { count: 100 }, name: 'W3Cschool編程獅' }

結(jié)論是:深拷貝修改拷貝之后的對(duì)象上的屬性不會(huì)把原對(duì)象身上的屬性同時(shí)修改掉。

拷貝的方法

1.數(shù)組方法:slice和concat

  • slice

let arr = [1,2,3,4];
let arr2 = arr.slice(0)
arr2[2]=5;


console.log(arr);  //[ 1, 2, 3, 4 ]
console.log(arr2); //[ 1, 2, 5, 4 ]

當(dāng)數(shù)組里是不是對(duì)象的時(shí)候從結(jié)果上看是深拷貝,在看下面例子

let arr = [{1:1,2:2}];
let arr2 = arr.slice(0)
arr2[2]=5;


console.log(arr);  //[ { '1': 1 }, { '2': 5 } ]
console.log(arr2); //[ { '1': 1 }, { '2': 5 } ]

當(dāng)數(shù)組里是對(duì)象的時(shí)候就變成了淺拷貝

  • concat

let arr = [1,2,3,4];
let arr2 = [].concat(arr);
arr2[2]=5;
console.log(arr); //[ 1, 2, 3, 4 ]  ?
console.log(arr2); //[ 1, 2, 5, 4 ]

當(dāng)數(shù)組里不是對(duì)象的時(shí)候從結(jié)果上看是深拷貝,在看下面例子

let arr = [{1:1},{2:2}];
let arr2 = arr.cancat(0)
arr2[1][2]=5;


console.log(arr);  //[ { '1': 1 }, { '2': 5 } ]  ?變成了引用
console.log(arr2); //[ { '1': 1 }, { '2': 5 } ]

當(dāng)數(shù)組里是對(duì)象的時(shí)候就變成了淺拷貝

總結(jié):只有當(dāng)數(shù)組是一維數(shù)組而且不包含對(duì)象的時(shí)候才是深拷貝

(推薦教程:JavaScript教程

2.Object.assgin()

let a= {a:1,b:2};
let b= Object.assign({},a);
a.a=3;
console.log(a)  //{a: 3, b: 2}
console.log(b)  //{a: 1, b: 2}  ?
let a= {a:1,b:{c:2}};
let b= Object.assign({},a);
a.b.c=3;
console.log(a)  //{a: 1, b: {c:3}}
console.log(b)  //{a: 1, b: {c:3}}   ?變成了引用

總結(jié):Object.assgin如果涉及到嵌套多個(gè)對(duì)象的話就變成了引用 解決方法:使用JSON.stringify()先轉(zhuǎn)化成字符串,再通過JSON.parse()轉(zhuǎn)化成對(duì)象

  • 3.JSON.parse(JSON.stringify())

let a= {a:1,b:{c:2}};
let b= JSON.parse(JSON.stringify(a))
a.b.c=3;
console.log(a)  //{a: 1, b: {c:3}}
console.log(b)  //{a: 1, b: {c:2}}   ?
let school={'name':"W3Cschool編程獅",fn:function(){}};
let my = {age:{count:18},name:"W3Cschool編程獅"};
let all=JSON.parse(JSON.stringify({...school,...my}))
console.log(all);  //{'name':"W3Cschool編程獅",age:{count:18}}; //?把fn給丟了

總結(jié): JSON.parse(JSON.stringify()) 這個(gè)方法有一定的局限性,會(huì)丟失 fn 。

  • 4.手寫深拷貝

let deepClone=(obj)=>{
    if(obj==undefined) return obj;  //undefined == null
    if(obj instanceof RegExp) return new RegExp(obj);
    if(obj instanceof Date) return new Date(obj);
    if(typeof obj!=="object") return obj;
    let newObj = new obj.constructor;
    for(let key in obj){
        if(obj.hasOwnProperty(key)){
            newObj[key] = deepClone(obj[key])
        }
    }
    return newObj;
}
let obj1 = {name:{age:"10"}}
let n = deepClone(obj1)
obj1.name.age = "231"
console.log(n);  //{name:{age:"10"}}  ?
let obj = { name:"W3Cschool編程獅" }
obj.aaa=obj
let n = deepClone(obj1)
console.log(n);  //死循環(huán)了  ?

解決這個(gè)問題可以使用WeakMap

let deepClone=(obj,hash=new WeakMap())=>{
    if(obj==undefined) return obj;  //undefined == null
    if(obj instanceof RegExp) return new RegExp(obj);
    if(obj instanceof Date) return new Date(obj);
    if(typeof obj!=="object") return obj;
    if(hash.has(obj)) return hash.get(obj);
    let newObj = new obj.constructor;
    hash.set(obj,newObj);


    for(let key in obj){
        if(obj.hasOwnProperty(key)){
            newObj[key] = deepClone(obj[key],hash)
        }
    }
    return newObj;
}

  • 5.lodash的cloneDeep


<br> 源碼地址:https://github.com/lodash/lodash/blob/86a852fe763935bb64c12589df5391fd7d3bb14d/.internal/baseClone.js
<br> ```

  • 6.vue-router源碼中的克隆方法

function clone (value) {
  if (Array.isArray(value)) {
    return value.map(clone)
  } else if (value && typeof value === 'object') {
    const res = {}
    for (const key in value) {
      res[key] = clone(value[key])
    }
    return res
  } else {
    return value
  }
}
let arr = [{1:1},{2:2},function(){}];
let arr2 = clone(arr)
arr2[1][2]=5;
console.log(arr)  //[ { '1': 1 }, { '2': 2 }, [Function (anonymous)] ]   ? 深拷貝
console.log(arr2); //[ { '1': 1 }, { '2': 5 }, [Function (anonymous)] ]
function extend (a, b) {
  for (const key in b) {
    a[key] = b[key]
  }
  return a
}
let b={a:1,b:{c:2}};
let a= extend({},b);
a.b.c=5;
console.log(a);  //{ a: 1, b: { c: 5 } }
console.log(b);  //{ a: 1, b: { c: 5 } }   淺拷貝

(推薦微課:JavaScript微課

文章來源:公眾號(hào)--小丑的小屋 作者:小丑

以上就是W3Cschool編程獅關(guān)于 完美解決JavaScript的深淺拷貝 的相關(guān)介紹了,希望對(duì)大家有所幫助。

0 人點(diǎn)贊