通过递归可以简单实现对象的深拷贝,但是这种方法不管是 ES6 还是 ES5 实现,都有同样的缺陷,就是只能实现特定的 object 的深度复制(比如数组和函数),不能实现包装对象 Number,String , Boolean,以及 Date 对象,RegExp 对象的复制。
(1)简单的深拷贝
- function deepClone(obj) {
- var newObj = obj instanceof Array ? [] : {}
-
- for (var i in obj) {
- newObj[i] = typeof obj[i] == 'object' ? deepClone(obj[i]) : obj[i]
- }
-
- return newObj
- }
这种方法可以实现一般对象和数组对象的拷贝,比如:
- var arr = [1, 2, 3]
-
- var newArr = deepClone(arr)
-
- // newArr->[1,2,3]
-
- var obj = {
- x: 1,
- y: 2
- }
-
- var newObj = deepClone(obj)
-
- // newObj={x:1,y:2}
但是不能实现例如包装对象 Number,String,Boolean,以及正则对象 RegExp 和 Date 对象的拷贝,比如:
- //Number 包装对象
-
- var num = new Number(1)
-
- typeof num // "object"
-
- var newNum = deepClone(num)
-
- //newNum -> {} 空对象
-
- //String 包装对象
-
- var str = new String('hello')
-
- typeof str //"object"
-
- var newStr = deepClone(str)
-
- //newStr-> {0:'h',1:'e',2:'l',3:'l',4:'o'};
-
- //Boolean 包装对象
-
- var bol = new Boolean(true)
-
- typeof bol //"object"
-
- var newBol = deepClone(bol)
-
- // newBol ->{} 空对象
(2)valueof()函数
所有对象都有 valueOf 方法,valueOf 方法对于:如果存在任意原始值,它就默认将对象转换为表示它的原始值。对象是复合值,而且大多数对象无法真正表示为一个原始值, 因此默认的 valueOf()方法简单地返回对象本身,而不是返回一个原始值。数组、函数和正则表达式简单地继承了这个默认方法,调用这些类型的实例的 valueOf()方法只是简单返回这个对象本身。
对于原始值或者包装类:
- function baseClone(base) {
- return base.valueOf()
- }
-
- //Number
-
- var num = new Number(1)
-
- var newNum = baseClone(num)
-
- //newNum->1
-
- //String
-
- var str = new String('hello')
-
- var newStr = baseClone(str)
-
- // newStr->"hello"
-
- //Boolean
-
- var bol = new Boolean(true)
-
- var newBol = baseClone(bol)
-
- //newBol-> true
其实对于包装类,完全可以用=号来进行拷贝,其实没有深拷贝一说,这里用 valueOf 实现,语法上比较符合规范。
对于 Date 类型:
因为 valueOf 方法,日期类定义的 valueOf()方法会返回它的一个内部表示:1970 年 1 月 1 日以来的毫秒数.因此我们可以在 Date 的原型上定义拷贝的方法:
- Date.prototype.clone = function () {
- return new Date(this.valueOf())
- }
-
- var date = new Date('2010')
-
- var newDate = date.clone()
-
- // newDate-> Fri Jan 01 2010 08:00:00 GMT+0800
- 对于正则对象 RegExp:
-
- RegExp.prototype.clone = function () {
- var pattern = this.valueOf()
-
- var flags = ''
-
- flags += pattern.global ? 'g' : ''
-
- flags += pattern.ignoreCase ? 'i' : ''
-
- flags += pattern.multiline ? 'm' : ''
-
- return new RegExp(pattern.source, flags)
- }
-
- var reg = new RegExp('/111/')
-
- var newReg = reg.clone()
-
- //newReg-> /\/111\//
最终解决方案:
// 在深拷贝的基础上,对(包装对象Number,String,Boolean;Date 对象,RegExp正则对象)进行深拷贝
- function deepClone(obj) {
- let newObj = obj instanceof Array ? [] : {}
- for (let i in obj) {
- // for...in 会遍历原型上的属性,此处只拷贝obj对象自身的属性
- if (obj.hasOwnProperty(i)) {
- let type = Object.prototype.toString.call(obj[i])
- if (typeof obj[i] == 'object') {
- // 拷贝的值为对象,则需要深拷贝
- if (type == '[object Date]') {
- newObj[i] = new Date(obj[i].valueOf())
- } else if (type == '[object RegExp]') {
- // 正则对象
- let pattern = obj[i].valueOf()
- let flags = ''
- flags += pattern.global ? 'g' : ''
- flags += pattern.ignoreCase ? 'i' : ''
- flags += pattern.multiline ? 'm' : ''
- newObj[i] = new RegExp(pattern.source, flags)
- } else if (type == '[object Array]' || type == '[object Object]') {
- // 数组或对象
- newObj[i] = deepClone(obj[i])
- } else {
- // 包装对象Number,String,Boolean
- newObj[i] = obj[i].valueOf()
- }
- } else if (typeof obj[i] == 'function') {
- // 函数
- newObj[i] = new Function('return ' + obj[i].toString())()
- } else {
- // 拷贝的值为原始值,则直接复制该值
- newObj[i] = obj[i]
- }
- }
- }
- return newObj
- }
测试:
-
- let obj = {
- name: 'zs',
- age: 20,
- food: ['apple', 'banana', 'orange'],
- obj1: {
- school: 'nyist'
- },
- reg: new RegExp('/A-Z/', 'gi'),
- date: new Date(),
- bol: new Boolean(true),
- num: new Number(999),
- fn: function () {
- this.age++
- }
- }
-
- let res = deepClone(obj)
-
- res.obj1.school = '清华'
- res.fn = function (a, b) {
- console.log(a, b)
- }
- res.food[1] = '香蕉'
-
- console.log('res', res, 'obj', obj)
- console.log(res.reg === obj.reg)
-

原文链接:https://blog.csdn.net/weixin_52624519/article/details/129265211