朋友,jquery的extend了解吗?深复制了解下

js的深复制不管是在开发中还是在找工作的笔试面试中,都是频繁出现的。使用jquery的人一定知道jquery有个extend方法,这个方法相当强大,不仅可以实现对jquery插件的扩展,也实现的对对象的扩展(包括了深复制)的功能,开发当中也是使用频率相当高的一个方法。本文将实现一个实用性比较高的深复制方法。

涉及到知识点

1、跨帧判断一个对象是否是数组
2、如何判断一个对象是纯粹对象
3、jquery extend方法是如何实现完美深复制

如何实现深复制

一般我们实现深复制就是利用递归的方式,判断要复制的对象的属性值是一个Object或者是Array的时候,我们需要利用递归进行复制。

function deepClone(target) {
  var copy;
  // 复制对象,根据被复制对象的类型初始化
  if(target instanceof Array) {
    copy = [];
  } else if(target instanceof Object) {
    copy = {};
  } else {
    return target;
  }
  for(var key in target) {
    // 被复制的属性值
    var value = target[key];
  
    if(value instanceof Array || value instanceof Object) {
      // 如果是Array/Object类型,则递归复制
      copy[key] = deepClone(value)
    } else {
      copy[key] = target[key];
    }
  }
  return copy;
}
复制代码

这样就基本实现深复制了,大功告成了。
呵呵呵,少年,too young too simple。这段代码有几个问题。
1、判断对象是否是数组,在多frame的情况下不准。
2、用instanceof判断一个对象是否是Object不合理。我们都知道,instanceof会把DOM对象、BOM对象、Function、RegExp、Date、Array等全部都认为是Object(原型链的最顶部就是Object)。
3、无法对Date和RegExp对象进行复制。

解决方案

问题1

其实问题不是很大,我们很少会遇到跨frame操作对象,不过这里还是讲下如果做到兼容性的判断一个对象是否是数组

function isArray(arr) {
    return Object.prototype.toString.call(arr) === "[object Array]"
}
复制代码

为什么跨frame下判断一个对象是否是数组无效?其实浏览器中,所有的全局对象都是挂载在window对象下,而不同的frame是有不同的window的,因此,一个frame的全局对象,自然不会是另外一个frame的。

问题2

一般我们要复制的对象是对象直面量或者是new方式创建的对象,称为纯粹对象,需要去掉DOM和BOM对象的复制,而对于Date,RegExp一般不考虑,因此我们需要有一种更准确的判断一个对象是否是普通对象,参照jquery的isPlainObject,它可以区分出纯粹对象。

function isPlaginObject = function(obj) {
    // 排除不是对象类型/DOM对象/window对象
    if(typeof obj !== 'object' || obj.nodeType || obj.self = obj) {
        return false;
    }
    
    // 如果判断为对象,但对象的__proto__没有isPrototype方法,则说明不是通过new方式创建或者对象字面量,同时RegExp和Date对象也被区分开来
    if(obj.constructor && !Object.prototype.hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf")) {
        return false;
    }
    
    return true;
}
复制代码

问题3

instanceof判断Date和RegExp都会是Object类型,所以上面的deepClone复制的时候用走for in遍历复制这两种类型的对象的属性,但是这个时候是没有可遍历的属性的,所以结果会是空对象,明显不是我们要的结果。
针对RegExp对象,我们可以通过新建一个新的RegExp对象,传递source和flags

if(target[key] instanceof RegExp) {
    copy[key] = new RegExp(target[key].source, target[key].flags)
}
复制代码

针对Date对象,同样可以通过新建一个Date对象进行拷贝。

if(target[key] instanceof Date) {
    copy[key] = new Date(+target[key]); //+号会将date转为时间戳,原理就是+操作符会调用Date对象的valueOf方法
}
复制代码

完整代码

/**
** 判断一个对象是否是数组-兼容frame版
**
**/
function isArray(arr) {
    return Object.prototype.toString.call(arr) === "[object Array]";
}

/**
** 判断一个对象是否是纯粹对象
**
**/
function isPlainObject(obj) {
  // 排除简单类型/DOM对象/window对象
  if(typeof obj !== "object" || obj.nodeType || obj.self === self) {
    return false;
  }
  // 排除不是new操作符新建的对象以及RegExp和Array
  if(obj.constructor && !Object.prototype.hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf")) {
    return false;
  }
  return true;

}

/**
** 复制对象
** @target 被复制对象
**
**/
function deepCopy(target) {
    var copy = null,
        copyAttribute = null, // 被复制对象的某个属性,有可能还是object类型或者array类型
        length = arguments.length,
        i = 1;
    
    // 目标对象不是纯粹对象或者数组,则不复制
    if(!isPlainObject(target) && !isArray(target)) {
        return null;
    } else if(isPlainObject(target)){
        copy = {};
    } else {
        copy = [];
    }
    
    if(isPlainObject(target) || isArray(target)) {
      for(var key in target) {
        // 不复制原形链里面的属性
        if(target.hasOwnProperty(key)) {
          if(isPlainObject(target[key])) {
            // 如果是普通对象
            copyAttribute = this.isPlainObject(target[key]) ? target[key] : {};
            // 普通对象,通过递归调用的方式复制
            copy[key] = deepCopy(copyAttribute, target[key]);
          } else if(isArray(target[key])) {
            // 如果是数组
            copyAttribute = isArray(target[key]) ? target[key] : [];
            // 数组对象,通过递归调用的方式复制
            copy[key] = deepCopy(copyAttribute, target[key]);
          } else if(target[key] instanceof RegExp) {
            copy[key] = new RegExp(target[key].source, target[key].flags);
          } else if(target[key] instanceof Date) {
            copy[key] = new Date(+target[key]);
          }else {
            copy[key] = target[key];
          }
        }
      }
    } else {
      return copy;
    }
    
    return copy;
}
复制代码

git地址: github.com/VikiLee/Uti…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
jQueryextend方法是一个常用的扩展方法,在编写插件时经常使用。它有多个重载的原型,最常见的是extend(dest, src1, src2, src3...)。这个方法用于将src1、src2、src3等对象中的属性和方法合并到dest对象中。为了方便扩展jQuery对象的插件,可以使用jQuery.fn.extend方法,它将方法合并到jQuery对象的原型链上。例如,可以将一个hello方法合并到jQuery的实例对象中,使得可以通过$("selector").hello()调用该方法。另外,通过$.extend方法,还可以在jQuery的全局对象中扩展一个命名空间,从而可以调用$.net.hello()这样的方法。最后,extend方法的实现原理就是将src对象中的属性和方法合并到dest对象中,这样就可以实现对象之间的属性和方法的合并。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [jQuery.extend 函数详解](https://blog.csdn.net/weixin_30520015/article/details/95824097)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* [jQuery extend 方法使用及实现](https://blog.csdn.net/weixin_45242865/article/details/120586505)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值