天天看點

JavaScript複制對象和數組

JavaScript 中,資料類型分為兩大類型,基本類型和引用類型。

簡述一下,基本類型是指我們常見的Undefined、Null、String、Number、Boolean,是儲存在 棧記憶體 中的簡單資料段;而引用類型則是指Object、Array、RegExp、Date、Function、特殊的基本包裝類型(String、Number、Boolean)以及單體内置對象(Global、Math)。

先來了解一下最常見的Object和Array,作為最常用引用類型,Object本身是個鍵值對的集合,組成一個擁有多屬性的資料。Array則是個有序的資料清單。兩者都能儲存各種基本類型資料。

在JavaScript聲明變量的過程中,對Object和Array的聲明實際上并不是給變量指派,而是類似于C語言中的指針,将變量指向于 堆記憶體 中,通過變量名尋址找到相應的對象。當沒有任何變量指向于該對象時,現代浏覽器會進行銷毀回收記憶體空間。

是以在對JavaScript了解不夠的時候,經常會對引用類型進行類似于以下的操作:

var a = { key: 'test' }
var b = a

// 更改key的值
b.key = 
           

這種操作會直接導緻兩個變量引用的對象值發生變化,當重新通路 a.key 的,實際拿到的就是 1 了,這個就是我們常說的淺複制。

那麼怎麼操作才能複制好一個對象出來呢?不妨思考一下,上文可以得知,對象和數組的組成其實就是我們常用的基本類型或引用類型組成的,要複制他們,其實就是建立出相同的資料結構,把對應的值賦上去,,而引用類型中包含引用類型,則也可以執行上述操作,直到複制出兩個對象。

以下用遞歸實作一下原理。

/** 
 * 深度複制樸素對象和數組
 * 除此之外都做淺複制處理
 * @author [email protected]
 * @param obejcts
 * 待複制變量
 * @return 合并的對象
 * @example
 * 
 *  var extend = require('./extend')
 *  var y = {
 *          a: ,
 *          b: '2',
 *          c: { d: [] }
 *      },
 *      z = {
 *          a: ,
 *          e: ,
 *          f: {
 *              g: 
 *          }
 *      }
 *
 *  var x = extend(y, z)
 *
 *  x.c.d = 
 *  z.f.g = 
 *
 *
 */

const Extend = (obj, temp) => {

    // 類型
    let typeStr = obj.constructor.name

    // 類型為樸素對象或數組
    if (typeStr === 'Object' || typeStr === 'Array') {

        // 副本初始化類型
        if (temp === undefined) {
            temp = obj instanceof Array ? [] : {}
        }

        // 周遊
        for (let key in obj) {

            // 如果周遊子也是複雜類型,遞歸調用
            if (obj[key] instanceof Object) {

                temp[key] = Extend(obj[key])

            } else {

                // 直接指派
                temp[key] = obj[key]

            }
        }
    } else {

        // 簡單類型 / 非數組 / 非樸素對象 直接複制
        temp = obj

    }

    return temp

}

module.exports = (...objs) => {

    let concats = {}

    for (let obj of objs) {
        Extend(obj, concats)
    }

    return concats

}
           

除了這種方法,代碼實作還有各種各樣的,這個就不一一介紹了。

繼續閱讀