zoukankan      html  css  js  c++  java
  • JavaScript的深拷贝的实现

    JavaScript的数据类型

    简单数据类型

    1. string
    2. number
    3. boolean
    4. function
    5. null
    6. undefined

    复杂数据类型

    1. String
    2. Number
    3. Boolean
    4. Function
    5. Date
    6. Array
    7. RegExp
    8. Object

    各种类型的深复制方式:

    先来看看简单类型的复制方式:

    //string
    var s1 = 'abc';
    var s2 = s1;
    s2 = 'ccc';
    console.log(s1);
    
    //number
    var n1 = 12.1;
    var n2 = n1;
    n2 = 7410;
    console.log(n1);
    
    //boolean
    var b1 = true;
    var b2 = b1;
    b2 = false;
    console.log(b1);
    
    //null
    var nu1 = null;
    var nu2 = nu1;
    nu2 = 'abc';
    console.log(nu1);
    
    //undefined
    var u1 = undefined;
    var u2 = u1;
    u2 = 'abc';
    console.log(u1);
    

    从以上的代码可以看出,简单类型,只需要直接赋值就是深复制了。但是也有一个例外,那就是function。

    接着来看看String、Number、Boolean、Date的深复制:

    //String
    var s1 = new String('s1');
    var s2 = new String(s1);
    console.log(s2);
    
    //Number
    var n1 = new Number('1');
    var n2 = new Number(n1);
    console.log(n2);
    
    //Boolean
    var b1 = new Boolean(1);
    var b2 = new Boolean(b1);
    console.log(b2);
    
    //Date
    var d1 = new Date();
    var d2 = new Date(d1);
    console.log(d2);
    

    除以上的做法之外,还需要对实例属性进行拷贝。那么剩下的Function、function、RegExp和Array还有Object又该怎么拷贝呢?这几个比较特殊,我们一个一个来:

    对于Function和function的深拷贝,我们可以按照如下的方式来做:

    var f1 = new Function('a', 'console.log("f1" + a);');
    var f2 = function(b){console.log('f2' + b);};
    
    //通过toString获取源代码(有浏览器兼容问题)
    var code = f1.toString();
    //利用eval进行复制
    var f1_copy = (function(functionCode){
      eval('var f = ' + functionCode);
      return f;
    })(code);
    
    f1_copy('abc');
    
    //当然f2也可以用同样的方式来复制。
    

    接着,我们来看下RegExp,可以同样同时eval来执行拷贝,也可以使用如下方式:

    var reg1 = /abc/g;
    var reg2 = new RegExp('abc', 'gmi');
    
    var reg1_copy = (function(reg){
      var pattern = reg.valueOf();
      var flags = (pattern.global ? 'g' : '') + 
        (pattern.ignorecase ? 'i' : '') + (pattern.multiline ? 'm' : '');
      return new RegExp(pattern.source, flags);
    })(reg1);
    

    最后,我们来说一说Array的复制,有的人可以说,直接用slice复制一份出来就是了,那我们来看看,是否真的达到效果的呢?

    var o = {name: 'Jay'};
    var arr1 = [o, '22', 1];
    var arr2 = arr1.slice(0);
    arr2[0].name = 'Arr2';
    console.log(arr1[0].name);
    

    很简短的代码,直接就把slice抛弃了,slice只能保证Array是新的,并不意味着内部的元素是深拷贝的,那么如何做呢?就是遍历元素,对每个元素进行深拷贝了。代码如下:

    var o = {name: 'Jay'};
    var arr1 = [o, '22', 1];
    
    var arr2 = [];
    for(var i = 0, len = arr1.length; i < len; i++){
      //注意,deepClone还未实现
      arr2.push(deepClone(arr1[i]));
    }
    

    以上对针对不同的类型,特殊的代码,那么如何来拷贝实例属性呢?代码如下:

    var o = {p1: '1', p2: 2, p3: function(){}};
    
    var copy = {};
    for(var p in o){
      //注意deepClone还未实现
      copy[p] = deepClone(o[p]);
    }
    

    注意:针对复杂类型,还需要同时copy.constructor = source.constructor来保证构造函数一致。

    最终的深复制代码

    通过以上的分析与代码示例,那么我们最终的代码又是怎样的呢?详细代码如下:

    //自调用函数,防御性编程
    ;
    (function (window) {
      'use strict';
    
      function getCustomType(obj) {
        var type = typeof obj,
          resultType = 'object';
        //简单类型
        if (type !== 'object' || obj === null) {
          resultType = 'simple';
        } else if (obj instanceof String || obj instanceof Number || obj instanceof Boolean || obj instanceof Date) {
          resultType = 'complex';
        } else if (obj instanceof Function) {
          resultType = 'function';
        } else if (obj instanceof RegExp) {
          resultType = 'regexp';
        } else if (obj instanceof Array) {
          resultType = 'array';
        }
        return resultType;
      }
    
      function cloneProperties(dest, source) {
        dest.constructor = source.constructor;
        for (var p in source) {
          dest[p] = deepClone(source[p]);
        }
        return dest;
      }
    
      function cloneSimple(obj) {
        return obj;
      }
    
      function cloneComplex(obj) {
        var result = new obj.constructor(obj);
        return cloneProperties(result);
      }
    
      function cloneFunction(obj) {
        var funCopy = (function (f) {
          eval('var abcdefg_$$$$ = ' + obj.toString());
          return abcdefg_$$$$;
        })(obj);
        return cloneProperties(funCopy);
      }
    
      function cloneRegExp(obj) {
        var pattern = obj.valueOf();
        var flags = (pattern.global ? 'g' : '') +
          (pattern.ignorecase ? 'i' : '') + (pattern.multiline ? 'm' : '');
        var reg = new RegExp(pattern.source, flags);
        return cloneProperties(reg);
      }
    
      function cloneArray(obj) {
        var resultArr = [];
        for (var i = 0, len = obj.length; i < len; i++) {
          resultArr.push(deepClone(obj[i]));
        }
        for (var p in obj) {
          if (typeof p === 'number' && p < len) {
            continue;
          }
          resultArr[p] = deepClone(obj[p]);
        }
        return resultArr;
      }
    
      function cloneObject(obj) {
        var result = {};
        result.constructor = obj.constructor;
        for (var p in obj) {
          result[p] = deepClone(obj[p]);
        }
        return result;
      }
    
      function deepClone(obj) {
        var f = undefined;
        switch (getCustomType(obj)) {
        case 'simple':
          f = cloneSimple;
          break;
        case 'complex':
          f = cloneComplex;
          break;
        case 'function':
          f = cloneFunction;
          break;
        case 'regexp':
          f = cloneRegExp;
          break;
        case 'array':
          f = cloneArray;
          break;
        case 'object':
          f = cloneObject;
          break;
        }
        return f.call(undefined, obj);
      }
    
      //挂载到window对象上
      window.deepClone = deepClone;
    })(window);
    
  • 相关阅读:
    CSS HACK:IE6、IE7、IE8、Firefox兼容性问题解决方案
    C# @符号的多种使用方法
    16个Javascript的Web UI库、框架及工具包
    【分享】20个很不错的UI图标集资源
    JQuery/AjaX/Javascript/DIV+CSS资源下载地址
    发个csdn泄露账户查询地址,没下数据库的童鞋来查一下自己
    【总结】CSS透明度大汇总
    C#综合揭秘——细说事务
    ASP.NET获取客户端、服务器端基础信息集合
    收集的网络上大型的开源图像处理软件代码(提供下载链接)
  • 原文地址:https://www.cnblogs.com/humin/p/4705331.html
Copyright © 2011-2022 走看看