深克隆-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源碼中對於克隆的實現,優雅又簡潔。

相關文章
相關標籤/搜索