一、資料型別儲存
-
在JavaScript中存在兩大資料型別:基本型別、引用型別。
-
基本資料型別存放在棧中,是一段簡單的資料段,資料大小確定,記憶體空間大小可以分配,是直接按值存放的,可以按值訪問。
-
引用資料型別存放在堆中,變數在棧中儲存的是指向堆記憶體的地址值,這個地址值指向對應的物件型別,訪問堆記憶體中的物件是透過地址值訪問的。
-
二、淺複製
-
淺複製,指的是建立新的資料,這個資料有著原始資料屬性值的一份精確複製。
-
如果屬性是基本型別,複製的就是基本型別的值。如果屬性是引用型別,複製的就是記憶體地址。
-
即淺複製是複製一層。
-
下面簡單實現一個淺複製:
function shallowClone (obj) { const newObj = {} for (let prop in obj) { if (obj.hasOwnProperty(prop)) { newObj[prop] = obj[prop] } } return newObj }
-
在JavaScript中,存在淺複製的現象有:
Object.assign()
Array.prototype.slice()
Array.prototype.concat()
使用擴充套件運算子實現的複製
-
Object.assign()
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({}, fxObj)
-
slice()
const fxArr = ['One', 'Two', 'Three'] const fxArrs = fxArr.slice(0) fxArrs[1] = 'love' consloe.log(fxArr) // ['One', 'Two', 'Three'] consloe.log(fxArrs) // ['One', 'love', 'Three']
-
concat()
const fxArr = ['One', 'Two', 'Three'] const fxArrs = fxArr.concat() fxArrs[1] = 'love' consloe.log(fxArr) // ['One', 'Two', 'Three'] consloe.log(fxArrs) // ['One', 'love', 'Three']
-
擴充套件運算子
const fxArr = ['One', 'Two', 'Three'] const fxArrs = [...fxArr] fxArrs[1] = 'love' consloe.log(fxArr) // ['One', 'Two', 'Three'] consloe.log(fxArrs) // ['One', 'love', 'Three']
三、深複製
-
深複製開闢一個新的棧,兩個物件相同,但是對應兩個不同的地址,修改一個物件的屬性,不會改變另一個物件的屬性。
-
常見的深複製方式有:
_.cloneDeep()
jQuery.extend()
JSON.stringify()
迴圈遞迴
-
_.cloneDeep()
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()
const obj = { name: 'A', name1: 'undefined', name2: function () {}, name3: Symbol('A') } const obj2 = JSON.parse(JSON.stringify(obj1)) // 會忽略undefined、Symbol、函式 console.log(obj2) // { name: 'A' }
-
迴圈遞迴
function deepClone (obj, hash = new WeakMap()) { if (obj === null) return obj // 如果是null或者undefined,就不進行複製操作 if (obj instanceof Date) return new Date(obj) if (obj instanceof RegExp) return new RegExp(obj) // 可能是物件或者普通的值,如果是函式的話不需要深複製 if (typeof obj !== 'Object') return obj // 如果是物件,就進行深複製 if (hash.get(obj)) return hash.get(obj) let cloneObj = new obj.constructor() // 找到的是所屬型別原型上的constructor,而原型上的constructor指向的是當前類本身 hash.set(obj, cloneObj) for (let key in obj) { if (obj.hasOwnProperty(key)) { // 實現一個遞迴複製 cloneObj[key] = deepClone(obj[key], hash) } } return cloneObj }
四、區別
-
淺複製只複製記憶體地址,而不復制物件本身,新舊物件還是共享同一塊記憶體,修改物件屬性會影響原物件。
// 淺複製 const obj1 = { name: 'init', arr: [1, [2, 3], 4] } const obj3 = shallowClone(obj1) // 一個淺複製方法 obj3.name = 'update‘ obj3.arr[1] = [5, 6, 7] // 新舊物件還是共享同一塊記憶體 console.log('obj1', obj1) // obj1 { name: 'init', arr: [1, [5, 6, 7], 4] } console.log('obj3', obj3) // obj3 { name: 'update', arr: [1, [5, 6, 7], 4] }
-
深複製會另外創造一個一模一樣的物件,新物件與原物件不共享記憶體,修改新物件不會改到原物件。
// 深複製 const obj1 = { name: 'init', arr: [1, [2, 3], 4] } const obj4 = deepClone(obj1) // 一個深複製方法 obj4.name = 'update‘ obj4.arr[1] = [5, 6, 7] // 新物件與原物件不共享記憶體 console.log('obj1', obj1) // obj1 { name: 'init', arr: [1, [2, 3], 4] } console.log('obj4', obj4) // obj4 { name: 'update', arr: [1, [5, 6, 7], 4] }
-
當複製型別為引用型別時:
- 淺複製是複製一層,屬性為物件時,淺複製是複製,兩個物件指向同一個地址。
- 深複製是遞迴複製深層次,屬性為物件時,深複製是新開棧,兩個物件指向不同的地址。