深克隆-Ramda源码中的实现

知识点总结

写一个深克隆的方法,咱们须要掌握的知识点数组

  1. js的数据类型:原始类型,引用类型。
  2. js对象的理解。Date,ExpRep等内置对象的使用。
  3. 使用typeof、Object.prototype.toString进行类型判断。
  4. 闭包的应用。
  5. for in 语句。

js的数据类型

js的类型分两种bash

  • 原始类型:Undefined、Null、Boolean、Number、String、Symbol
  • 引用类型:Object 在引用类型中,有内置类型Array、Date、RegExp这样的内置类型,也有用户本身用构造函数建立的类型。

深克隆 vs 浅克隆

由于有了引用类型,才有了深克隆和浅克隆的区别。闭包

浅克隆会对于原始类型,能够彻底复制,对于引用类型,只会复制它的引用,对其内部的值,不会复制。函数

深克隆不论对于原始类型,仍是引用类型,都会彻底复制。生成一个跟原来的对象,彻底没有关联的新对象。ui

浅克隆的实现

写一个最简单的实现,只考虑简单对象的浅克隆spa

function clone(value) {
  var cloneValue = {};
  for (var prop in value) {
    cloneValue[prop] = value[prop];
  }
  return cloneValue;
}
复制代码

深克隆的实现

写一个简单的实现,只考虑简单对象的深克隆prototype

function clone(value) {
  var cloneValue = {};
  for (var key in value) {
    if(typeof value[key] === 'object') {
        cloneValue[key] = clone(value[key]);
      } else {
        cloneValue[key] = value[key];
      }
  }
  return cloneValue;
}
复制代码

在浅克隆的基础上,判断对象的属性值,是否仍是个对象,若是是,调用clone,造成递归,咱们的深克隆方法就实现啦。code

目前这个方法还不完善,增长咱们克隆对象的复杂度,不止是简单对象了,还要能克隆数组。对象

function type(value) {
  return Object.prototype.toString.call(value).match(/\[object (.*?)\]/)[1];
}

function clone(value) {
  var copyFunc = function(copiedValue){
    for(var key in value) {
      if(typeof value[key] === 'object') {
        copiedValue[key] = clone(value[key]);
      } else {
        copiedValue[key] = value[key];
      }
    }
    return copiedValue;
  }
  
  switch (type(value)) {
    case 'Object': return copyFunc({});
    case 'Array': return copyFunc([]);
    default: return value;
  }
}

复制代码

此次咱们除了clone方法,先写了一个type方法,利用Object.prototype.toString判断入参的类型。在clone方法里,咱们将上次的clone方法变成一个闭包函数copyFunc,如今的clone方法,先判断入参是对象仍是数组,再传入{}或者[]给copyFunc,利用for in语句既能够遍历对象,又能够遍历数组,完成克隆。递归

如今,咱们考虑更多的可克隆对象,除了对象、数组,还有Date、RegExp、这样的内置对象。

function _cloneRegExp(pattern){
  return new RegExp(pattern.source, (
     (pattern.global ? 'g' : '') +
     (pattern.ignoreCase ? 'i' : '') +
     (pattern.multiline ? 'm' : '') +
     (pattern.sticky ? 'y' : '') +
     (pattern.unicode ? 'u' : '') +
  ));
}

function clone(value) {
  var copyFunc = function(copiedValue){
    for(var key in value) {
      if(typeof value[key] === 'object') {
        copiedValue[key] = clone(value[key]);
      } else {
        copiedValue[key] = value[key];
      }
    }
    return copiedValue;
  }
  
  switch (type(value)) {
    case 'Object': return copyFunc({});
    case 'Array': return copyFunc([]);
    case 'Date': return new Date(value.valueOf());
    case 'RegExp': return _cloneRegExp(value);
    default: return value;
  }
}
复制代码

只需在swith下添加两个case便可。写到这里,咱们的克隆方法基本完毕了,能够说这个实现,是我目前看过最清晰的深克隆实现了。

再考虑一种状况,就是对象的多个属性,引用同一个对象多状况。咱们但愿保证克隆的对象,一样能保持多个属性引用的是同一个克隆出来的对象。

var childObj = { key: 1 };
var obj = {
    key1:childObj,
    key2:childObj,
}
复制代码

好比上面这个obj对象,咱们要克隆它,能够用下面的深克隆实现。

function clone(value, refFrom = [], refTo = []) {
  var copyFunc = function(copiedValue){
    var len = refFrom.length;
    var idx = 0;
    while (idx < len) {
      if (value === refFrom[idx]) {
        return refTo[idx];
      }
      idx += 1;
    }
    refFrom[idx + 1] = value;
    refTo[idx + 1] = copiedValue;
    
    for(var key in value) {
      if(typeof value[key] === 'object') {
        copiedValue[key] = clone(value[key], refFrom, refTo);
      } else {
        copiedValue[key] = value[key];
      }
    }
    return copiedValue;
  }
  
  switch (type(value)) {
    case 'Object': return copyFunc({});
    case 'Array': return copyFunc([]);
    case 'Date': return new Date(value.valueOf());
    case 'RegExp': return _cloneRegExp(value);
    default: return value;
  }
}
复制代码

这个版本的深克隆,就是ramda源码中对于克隆的实现,优雅又简洁。

相关文章
相关标签/搜索