關於js中深拷貝跟淺拷貝的一點總結

深拷貝 vs 淺拷貝

1.先總結

關於深拷貝跟淺拷貝的區別,以前看過各類博客、論壇對於這個問題的分析以及文章,總結出了一句很是 「精闢」 的解釋:「淺拷貝只是拷貝對象或者數組一級屬性上的基本類型,深拷貝是對對象以及對象的全部子對象進行拷貝」。(想了一下,仍是不太準確。。。)javascript

2.基本類型跟引用類型

先了解下基本數據類型跟引用類型的概念java

  • 基本類型:undefined,null,Boolean,String,Number,Symbol(基本類型:基本類型值在內存中佔據固定大小,保存在棧內存中(不包含閉包中的變量))git

  • 引用類型:Object,Array,Date,Function,RegExp等(引用類型的值是對象,保存在堆內存中。而棧內存存儲的是對象的變量標識符以及對象在堆內存中的存儲地址(引用),引用數據類型在棧中存儲了指針,該指針指向堆中該實體的起始地址。當解釋器尋找引用值時,會首先檢索其在棧中的地址,取得地址後從堆中得到實體。)github

3.數組中的拷貝

3.1 slice

let arr = [1,2,3,4,5,6]
let copyArr = arr.slice(0,2)
copyArr[0] = 9
console.log(arr[0]) //1
console.log(copyArr[0])//9
複製代碼

slice能夠實現拷貝,若是是二維數組呢?數組

let arr = [1,[2,3],4,5]
let copyArr = arr.slice(0,2)
copyArr[1][0] = 9
console.log(arr[1][0]) //9
console.log(copyArr[1][0])//9
複製代碼

因此slice是淺拷貝閉包

3.2 concat(方法用於鏈接兩個或多個數組,不會改變原數組)

let arr = [1,[2,3],4,5]
let copyArr = arr.concat()
copyArr[1][0] = 9
console.log(arr[1][0]) //9
console.log(copyArr[1][0])//9
複製代碼

concat 也屬於淺拷貝函數

3.3 擴展運算符(...)

let arr1 = [1,[2,3],4]
let copyArr = [...arr1]
copyArr[1][0] = 9
console.log(arr1[1][0])//9
console.log(copyArr[1][0])//9
複製代碼

擴展運算符也屬於淺拷貝ui

4.對象上的拷貝

關於對象上的拷貝就不依次上代碼了,得出結論是:this

淺拷貝:Object.assign()和擴展運算符spa

深拷貝:JSON.parse()JSON.stringify() 、經過遞歸

5.JSON方法有哪些缺點

5.1 Map、Set、Date、function、undefined等類型會丟失

let obj = {
    a: 1,
    printstr: function() {
        console.log(0)
    }
}
obj.date = new Date()
obj.reg = new RegExp(/^\d{3}\-\d{3,8}$/)
obj.b = undefined
obj.buffer = new ArrayBuffer(8)
let objCopy = JSON.parse(JSON.stringify(obj))
console.log(obj)
console.log(objCopy)
複製代碼

1566465604_1_.png

SetMapRegExpArrayBuffer會被轉爲空對象,Functionundefined會被忽略

補充:Symbol()也會忽略

5.2 不能處理循環引用

const x = {}
const y = {x}
x.y = y // Cycle: x.y.x.y.x.y.x.y.x...
const copy = JSON.parse(JSON.stringify(x)) // throws!
複製代碼

6.如何實現一個簡單的深拷貝(主要是深拷貝對象與數組)

function deepClone(obj) {
    function isObject(o) {
        return (typeof o === 'object' || typeof o === 'function') && o !== null
    }
    if (!isObject(obj)) {
        throw new Error('非對象')
    }
    let isArray = Array.isArray(obj)
    let newObj = isArray ? [...obj] : { ...obj}
    Reflect.ownKeys(newObj).forEach(key => {
        newObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
    })
    return newObj
}
複製代碼

非原創,以前保存的一個方法;這裏主要是用isArray去判斷ArrayObjectReflect.ownKeys方法獲取對象上全部屬性的key

這個方法實現的只是一個簡單的深拷貝,考慮獲得的狀況比較少,若是項目中涉及到比較複雜的狀況,能夠考慮一些比較成熟的庫,好比lodashUnderscore等;這些庫對於Symbols、原型鏈上面的屬性拷貝以及循環引用都有考慮到

參考文章

lodash是如何實現深拷貝的

7.關於數據類型的判斷

由於深拷貝中涉及到類型的判斷,這裏總結了下js中數據類型的類型判斷

7.1 typeofinstanceof

typeof:這個方法能夠看做是一個js的bug,該方法不能區分數組、對象、正則、Date等類型,還有一個比較特殊的:Null,typeof null返回的也是object

instanceof:用於判斷一個變量是否屬於某個對象的實例,會依次從原型鏈上面去查找

let b = new Array()
alert(b instanceof Array)  // true
alert(b instanceof Object)  // true

// instanceof的檢測類型必須是對象
let num = 1
num instanceof Number // false

num = new Number(1)
num instanceof Number // true
複製代碼

ArrayDateRegExpFunction等數據類型的原型鏈上層都是Object

得出結論:上述兩個方法都不能準確的判斷數據類型

7.2 構造函數 consstructor

constructor是原型對象上的一個屬性,默認指向這個原型的構造函數

let arr = [1]
let object = {}
let fun = function() {}
let set = new Set()
let date = new Date()
console.log(arr.constructor)
console.log(object.constructor)
console.log(fun.constructor)
console.log(set.constructor)
console.log(date.constructor)
複製代碼

1566543331_1_.png

從上面的代碼能夠看出constructor能夠判斷引用類型的數據類型,但這個方法也有一些缺點:

(1)undefined和null是不可以判斷出類型的

(2)constructor屬性是能夠被修改的,可能會致使判斷不許確

7.3 Object.prototype.toString

toString是Object原型上面的一個方法,該方法默認返回其調用者的具體類型,更嚴格的講,是toString運行時this指向的對象類型, 返回的類型

console.log(Object.prototype.toString.call("jerry"))//[object String]
console.log(Object.prototype.toString.call(12))//[object Number]
console.log(Object.prototype.toString.call(true))//[object Boolean]
console.log(Object.prototype.toString.call(undefined))//[object Undefined]
console.log(Object.prototype.toString.call(null))//[object Null]
console.log(Object.prototype.toString.call({name: "jerry"}))//[object Object]
console.log(Object.prototype.toString.call(function(){}))//[object Function]
console.log(Object.prototype.toString.call([]))//[object Array]
console.log(Object.prototype.toString.call(new Date))//[object Date]
console.log(Object.prototype.toString.call(/\d/))//[object RegExp]
function Person(){}
console.log(Object.prototype.toString.call(new Person))//[object Object]
複製代碼

這個方法能夠準確的判斷js中任意數據的類型

最後

才疏學淺,寫的比較簡單,喜歡的記得點個哦~~

文中有哪些沒有涉及到的,歡迎補充~~

相關文章
相關標籤/搜索