1. 程式人生 > >由js深拷貝引起的對內存空間的一些思考

由js深拷貝引起的對內存空間的一些思考

typeof pro 應該 ack RR -c IT init webp

數據類型

js常用數據類型分為基本類型和引用類型

  • 基本類型:null、undefined、數值型、字符串型、布爾型
  • 引用類型:數組、對象

內存空間

var a = [1, 2, 3];
var b = a;
b[2] = 4;
a;  // ??

我們都知道結果是[1, 2, 4],因為b和a指向了同一個引用對象所以都可以改變該對象的值,我們用內存空間來深入理解一下。

我們知道在內存中存在兩塊區域,一個是棧stack,一個是堆heap。 通常我們的基本數據類型存儲在棧中,而我們的引用數據類型存於堆中。棧中會有一個指針指向存在於堆中的數據以便於引用。

var arr = [1, 2, 3];

內存圖應該是這樣:

技術分享圖片 arr是一個基本類型的變量,該變量內部存儲著數組的地址/指針,通過該地址可以找到存在於heap中的Array對象。通常我們說arr是一個引用類型我覺得是不嚴謹的,應該說:變量arr指向一個引用類型。

這時候會出現一個問題:允許兩個指針指向同一個堆數據,這意味著通過其中一個指針篡改了數值那麽會影響另一個指針。

var a = [1, 2, 3];
var b = a;

以上變量在內存中的圖是這樣的:

技術分享圖片 解決該問題的方法就是重新在heap中創建一個與a指向的引用對象一模一樣的對象,然後讓b指向它, 像這樣: 技術分享圖片 這樣一個b改變了就不會影響a了。

說個題外話:了解這一機制對理解prototype是有很大好處的

function Person(name) {
    this.name = name;
}
Person.prototype = {
    sayHi() {
        console.log("hi, " + this.name);
    }
};

var p1 = new Person(‘lan‘);
p1.sayHi();  // ‘hi, lan‘

內存圖如下,可以自己理解一下,找一找哪些存在於stack哪些存在於heap:

技術分享圖片

接上述,直接將a賦值為b這樣為淺拷貝,而上上圖中則為深拷貝。

js實現深拷貝思路:

  • 先判斷所賦值的類型,如果為基本類型則直接拷貝,若為對象類型則在heap中重新生成一個對象,再遞歸的將值賦值過去。
function deepCopy(obj) {
    var result = ‘‘;
    
    // 為基本類型
    if(obj == null || obj == undefined || typeof obj != ‘object‘)
        return obj;

    // 為引用類型,判斷為數組還是為對象
    if(obj instanceof Array) 
        result = [];
    else 
        result = {};  

    for(var key in obj) {
        var current = obj[key];
        if(current == null || current == undefined || typeof current != ‘object‘)
            result[key] = current;
        else
            // 不直接調用deepCopy而用arguments.callee
            // 以避免函數被賦值給其他變量而出現錯誤
            result[key] = arguments.callee(current); 
     }

     return result;
}

var a = {
    name: ‘lan‘,
    age: 20,
    birth: [1,2,3,4],
    like: {
        food: ‘fruit‘,
        color: [‘pink‘, ‘blue‘]
    }
};

var b = deepCopy(a);
b.birth[2] = 5;
a.birth;  // [1, 2, 3, 4]

以上為自己對於指針的一點思考,歡迎指正與擴展。

由js深拷貝引起的對內存空間的一些思考