第5集丨JavaScript 中浅拷贝(extendCopy)和深拷贝(deepCopy)——最佳实践

我们在上集中,封装extend() 函数时知道,当对象类型的属性被拷贝时,实际上拷贝的只是该对象在内存中的位置指针,这一过程我们陈之为浅拷贝。在这种情况下,如果我们修改了拷贝对象,就等同于修改了原对象。那么,与之想对的,当然就是所谓的深拷贝了。

本集重点内容,就是带领诸君,如何实现浅拷贝和深拷贝。做一件事情的时候,最佳时间就是现在。因此,姑且就称之为最佳实践吧。

实践出真知,没有实践知识就不会变成真知,久而久之,就变成了一个空谈主义者了。

通过实践而发现真理,又通过实践而证实真理和发展真理。从感性认识而能动地发展到理性认识,又从理性认识而能动地指导革命实践,改造主观世界和客观世界。实践、认识、再实践、再认识,这种形式,循环往复以至无穷,而实践和认识之每一循环的内容,都比较地进到了高一级的程度。这就是辩证唯物论的全部认识论,这就是辩证唯物论的知行统一观。

一、引用拷贝

事实上,对象类型(包括数组函数)通常都是以引用形式进行传递的,这有时会导致一些不可预测的结果。

1.1 代码实现

function extend2(Child, Parent){
    var p = Parent.prototype;
    var c = Child.prototype;
    for (var i in p){
        c[i] = p[i];
    }
    c.uber = p; //扩展子类原型,增加一个uber属性
}

var A = function(){};
var B = function(){};
A.prototype.stuff = [1,2,3];
A.prototype.name = "a";

extend2(B,A);
console.log(B.prototype.hasOwnProperty('name')); //true
console.log(B.prototype.hasOwnProperty('stuff')); //true
console.log(B.prototype.stuff === A.prototype.stuff);  //true,说明了传递的是引用
B.prototype.stuff.push(4,5,6);
console.log(A.prototype.stuff); //[1,2,3,4,5,6],这里可以清楚的看到B对stuff操作影响了A

//当然如果我们用另一个对象对B的stuff属性进行完全重写(而不是修改现有属性)事情就不一样了
//在这种情况下,A的stuff会继续引用原有对象,而B的stuff属性指向了新的对象
B.prototype.stuff = ['a','b','c'];
console.log(A.prototype.stuff); //[1,2,3,4,5,6],这里可以看到A依然持有原来对象

1.2 代码分析

这段代码的主要思想是:当某些东西被创建为一个对象时,他们就被存储在内存中的某个物理位置上,相关变量和属性就会指向这些位置。而当我们将一个新的对象赋值给B.prototype.stuff时,就相当于告诉它:“喂,忘了那个对象把,将指针转移到现在这个新的对象上来”。

二、浅拷贝(extendCopy)

2.1 思路分析

到目前为止,前面所有的示例都是以 构造器(构造函数) 创建对象为前提的,现在我们直接通过对象标识符来创建对象,这个时候该如何实现继承呢?

首先我们用var o = { } 语句创建一个对象作为画板,然后在逐步为其添加属性,但这次我们不用this来实现,而是直接将现有对象的属性全部拷贝过来,例如下面:

function extendCopy(p){
    var c = {};
    for(var i in p){
        c[i] = p[i];
    }
    c.uber = p;
    return c;
}

2.2 功能测试

单纯属性的全拷贝是一种简单的模式,适用范围很广。下面来看这个函数的应用:

//shape对象
var shape = {
    name: 'shape',
    toString: function(){return this.name;}
}

//twoDee对象
var twoDee = extendCopy(shape);
twoDee.name = "2D shape";
twoDee.toString = function () {
    return this.uber.toString() + "," + this.name;
}

//triangle对象
var triangle = extendCopy(twoDee);
triangle.name = "triangle";
triangle.getArea = function () {
    return this.side * this.height / 2;
}
//手动赋值
triangle.side = 5;
triangle.height = 10;
//测试
console.log(triangle.getArea());    //25
console.log(triangle.toString());   //shape,2D shape,triangle

对于这种方法而言,可能的问题就在于初始化一个新的triangle对象的过程过于繁琐。因为我们要手动为该对象的sideheight属性进行赋值。但这个问题可以通过一个函数就能解决,例如我们可以创建一个init()方法

三、深拷贝(deepCopy)

在某些场景下,我们希望拷贝一个对象的是深拷贝,而不是浅拷贝,那么我们该如何实现呢?

3.1 代码实现

  • 核心思想:在于判断当前对象是否是基础属性,如果是就直接拷贝,如果不是进行递归循环拷贝
//深拷贝
function deepCopy(p, c){
    var c = c || {};
    for(var i in  p){
        if(typeof p[i] === 'object'){
            c[i] = (p[i].constructor === Array) ? [] : {};
            deepCopy(p[i],c[i]);
        }else{
            c[i] = p[i];
        }
    }
    return c;
}

3.2 功能测试

var parent = {
    numbers: [1,2,3],
    letters: ['a','b','c'],
    obj: {
        prop: 1
    },
    bool: true
};

var mydeep = deepCopy(parent);
var myshallow = extendCopy(parent);

mydeep.numbers.push(4,5,6);
console.log(mydeep.numbers);    //[1,2,3,4,5,6]
console.log(parent.numbers);    //[1,2,3]

myshallow.numbers.push(10);
console.log(myshallow.numbers); //[1,2,3,10]
console.log(parent.numbers);    //[1,2,3,10]
console.log(mydeep.numbers);    //[1,2,3,4,5,6]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值