javascript深拷贝函数封装

在javascript,python这种带引用类型的高级程序设计语言中,很容易出现浅拷贝而导致错误。

本篇文章,我浅述一下我对js中深拷贝函数封装的思考。

思路很简单,封装一个递归函数,终止条件是‘不是引用类型’(不是对象)

如下所示

function deepCopy(src) {
    if (!(src instanceof Object)) {
        return src;
    }
    var dst = new Object();
    for (let k in src) {
        dst[k] = deepCopy(src[k]);
    }
    return dst;
}

其中在选取递归形式上也有一些考量。如果利用引用类型的特点不用返回值,直接改变,首先函数形式上,不太优雅,且到了非引用类型那一层的时候,又无法得到改变,形式不统一。故采用接收返回值的递归形式。

但有这样一个问题,因为数组是object,看似兼容了数组,其实由于数组的特殊性,这样做,返回的dst没有length属性,因为for...in数组不会得到'k',实验一下就知道了

var arr = [1,2,3];
var copy = deepCopy(arr);
console.log(arr);
console.log(copy);

浏览器中是这样的

如果你调试的话,你就能发现for...in并不想所想的那样,而是略过了length属性

这属于是array的特性了,但如果你dst那里new的是一个Array而非Object,for...in在读完属性名'0','1','2'后,同样略过length,但dst里会自动生成一个正确的length

那怎么办?...就在添加一个判断好了(这里我是很疑惑的,我不知道是否还有其它特殊的存在,这里我光发现了array这个特例)

function deepCopy(src) {
    if (!(src instanceof Object)) {
        return src;
    }
    else if (src instanceof Array) {
        let dst = new Array();
        for (let i = 0; i < src.length; i++) {
            dst[i] = deepCopy(src[i]);
        }
        return dst;
    } 
    else {
        let dst = new Object();
        for (let k in src) {
            dst[k] = deepCopy(src[k]);
        }
        return dst;
    }
}

其实还有这样么一个问题,或者说倾向于强迫症了,我举一个例子。

class Position {
    constructor() {
        this.x = x;
        this.y = y;
    }
}
var a = new Position(1, 2);
var b = deepCopy(a);
console.log(a);
console.log(b);

在浏览器上,结果是这样的。

问题在哪?b没有Position,确实,b的属性跟方法都跟a一样,但严格来说,b并不是Position类的对象,用js里的理论,就是b的构造函数的名字不叫Position。

这两个问题其实是一体的,可能有些类像array一样有特殊的行为,如果不是相同的类而泛指object,很可能一些属性是无法得到的,比如array的length,这种角度来说可能并不是'强迫症',而是避免某种'糟糕的未知的情况'

可能会想到修改函数名,但很遗憾,函数的name属性是read only(如果可能的话,很大可能上仅仅是解决强迫症罢了)

于是我这样修改这个deepCopy函数

function deepCopy(src) {
    if (!(src instanceof Object)) {
        return src;
    }
    var dst = new (eval(src.constructor.name))();
    for (let k in src) {
        dst[k] = deepCopy(src[k]);
    }
    return dst;
}

 这样的确可以实现b是Position类的对象,并且数组的特殊情况也解决了,然而,这里也出现了一个问题。

拿刚才那个Position类继续说明,这要求Position类的构造函数是‘无参‘的......

然而很多情况下都不是无参,我继续思考,能否像c++一样,有default constructor?

很遗憾,js不支持函数重载,并且直截了当地告诉我,只能有一个constructor函数

为什么js这样设计?我想到一个可能的原因,js的函数是可变参,还记得arguments吗?

我可以这样写一个Position类

class Position {
    constructor() {
        this.x = arguments[0];
        this.y = arguments[1];
    }
}

var a = new Position();
console.log(a);

这样写也没问题

class Position {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
}

var a = new Position();
console.log(a);

它们都不会报错,x,y值是undefined,perfect!也就意味着构造函数'无参'并不重要,但你最好不要传数组等一些类型,并索引访问,be like

class Position {
    constructor(pos) {
        this.x = pos[0];
        this.y = pos[1];
    }
}

var a = new Position();
console.log(a);

这毫无疑问是会报错的

有了这种特性就很好办了,上面封装的函数应当是没有问题的

临近结尾,再贴一遍

function deepCopy(src) {
    if (!(src instanceof Object)) {
        return src;
    }
    var dst = new (eval(src.constructor.name))();
    for (let k in src) {
        dst[k] = deepCopy(src[k]);
    }
    return dst;
}

以上是我个人对JavaScript中深拷贝的一些思考,仅供个人记录,如有错误,还请斧正,也欢迎交流。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值