由js深拷贝引起的对内存空间的一些思考

时间:2023-02-13 08:13:42

数据类型

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];

内存图应该是这样:

由js深拷贝引起的对内存空间的一些思考
arr是一个基本类型的变量,该变量内部存储着数组的地址/指针,通过该地址可以找到存在于heap中的Array对象。通常我们说arr是一个引用类型我觉得是不严谨的,应该说:变量arr指向一个引用类型。

 

这时候会出现一个问题:允许两个指针指向同一个堆数据,这意味着通过其中一个指针篡改了数值那么会影响另一个指针。

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

以上变量在内存中的图是这样的:

由js深拷贝引起的对内存空间的一些思考
解决该问题的方法就是重新在heap中创建一个与a指向的引用对象一模一样的对象,然后让b指向它,
像这样:
由js深拷贝引起的对内存空间的一些思考
这样一个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:

由js深拷贝引起的对内存空间的一些思考

 

接上述,直接将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]

以上为自己对于指针的一点思考,欢迎指正与扩展。