diveDylan / blog

My blog, detail is in the issues list
2 stars 0 forks source link

DeepClone #41

Open diveDylan opened 4 years ago

diveDylan commented 4 years ago

预备工作 我们需要科隆的对象可能有Array,Object,Date, Reg,因此我们需要判断四类对象,常用的判断对象方法有三种 typeof, instanceof,toString

typeof

首先我们测试typeof,顺带复习下原知识

typeof new Date() // 'object'
typeof /1/    // 'object'
typeof []     // 'object'
typeof {}      // 'object'
typeof null    // 'object'
typeof 1 //  'number'
typeof '1' // 'string'
typeof undefined // 'undefined'

因为无法区分RegExp,Date,Array数据格式,所以typeof的方法不可取

instanceof

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上

通俗的理解可以认为检测A是否为B的实例

1 instance Number // false
new Number(1) instanceof Number // true
Number(1) instanceof Number // false
'1' instanceof String // false
new String(1) instanceof String // true
String(1) instanceof String // false
[] instanceof Array   // true
[] instanceof Object // true
/3/ instanceof RegExp // true
/3/ instanceof Object // true
{} instanceof Object // SyntaxError
const a = {}
a instanceof Object // true
new Date() instanceof Date // true
new Date() instanceof Object // true

因为DateRegExp等本质也是对象所以无法区分,该方法也不适用

toString

toStirng的判断方法主要还是利用Object.prototype.toString.call(this)的方法,该方法返回 "[object type]"

const getType =  obj => Object.prototype.toString.call(obj)
getType(1)  // "[object Number]"
getType('1')  // "[object String]"
getType(undefined)  // "[object Undefined]"
getType(null) // "[object Null]"
getType([]) // "[object Array]"
getType(/1/) // "[object RegExp]"
getType(new Date()) // "[object Date]"
getType({}) // "[object Object]"
getType(true) // "[object Boolean]"

toString的表现堪称完美。

clone without propoperty

如果不考虑原型按照我们上面的测试代码应该是这样的

const types = {
  OBJECT: '[object Object]',
  ARRAY: '[object Array]',
  DATE: '[object Date]',
  REGEXP: '[object RegExp]'
}

const toStr = obj => Object.prototype.toString.call(obj)

const isType = (obj, typeName) => toStr(obj) === types[typeName]

function deepClone(obj) {
  if (isType(obj, 'OBJECT')) {
    const objCopy = {}
    Object
      .keys(obj)
      .forEach(key => {
        objCopy[key] = deepClone(obj[key])
      })
    return objCopy
  }
  if (isType(obj, 'ARRAY')) {
    return obj.map(key => deepClone(key))
  }
  if (isType(obj, 'Date')) {
    return new Date(date.getTime())
  }
  if (isType(obj, 'REGEXP')) {
    return new RegExp(obj)
  }
  return obj   // 其他类型直接赋值即可
}

clone property

为什么要clone原型?因为对象很可能是一个实例,我们举一个列子

class Person {
 constructor() {
   this.name = 'dylan'
 }
 say() {
     console.log('hello world')
 }
}

deepClone实例后你可能发现下图所示的现象 image 其实解决起来也相当简单,进行原型赋值,就跟继承一样


const types = {
  OBJECT: '[object Object]',
  ARRAY: '[object Array]',
  DATE: '[object Date]',
  REGEXP: '[object RegExp]'
}

const isOriginObject = obj => obj.__proto__.constructor === Object

const toStr = obj => Object.prototype.toString.call(obj)

const isType = (obj, typeName) => toStr(obj) === types[typeName]

function deepClone(obj) {
  if (isType(obj, 'OBJECT')) {
    let objCopy
    if (!isOriginObject(obj)) {
      objCopy = Object.create(null)
      objCopy.__proto__ = Object.create(obj.__proto__.constructor.prototype)
    } else {
      objCopy = {}
    }
    Object
      .keys(obj)
      .forEach(key => {
        objCopy[key] = deepClone(obj[key])
      })
    return objCopy
  }
  if (isType(obj, 'ARRAY')) {
    return obj.map(key => deepClone(key))
  }
  if (isType(obj, 'Date')) {
    return new Date(date.getTime())
  }
  if (isType(obj, 'REGEXP')) {
    return new RegExp(obj)
  }
  return obj
}