jQuery源码分析 -- 数组对象的深浅合并

extend

jQuery.extend = jQuery.fn.extend = function () { } / jQuery.extend({ })

  1. 把用户自定义的方法扩展到jQuery / jQuery.fn上 (也是合并:把自己传递进来对象中的方法合并到jQuery / jQuery.fn上)
    1. 把jQuery当做对象,扩展到其上面的的私有的属性和方法【完善类库】
    $.extend({
    	xxx : function(){ console.log("XXX") },
    });
    $.xxx();
    
    1. 向其原型上扩展属性和方法,供实例对象(基于JQ选择器获取的结果)调取使用【写JQ插件】
     $.fn.extend({
    	xxx(){ console.log("XXX") },
    });
    $(".box").xxx();
    
  2. 实现两个及多个对象的合并【浅比较浅合并 && 深比较深合并】
    1. 类似于Object.assign
  3. 传参类型:
    1. $ / $.fn.extend({})
    2. $.extend(true,{})
    3. $.extend({}, obj1, obj2)
    4. $.extend(true, {}, obj1, obj2)

分析JQ 源码中的jQuery.extend

jQuery.extend = jQuery.fn.extend = function () {
	var options, name, src, copy, copyIsArray,copyIsObject, clone,
		target = arguments[0] || {},//被替换对象
		i = 1,
		length = arguments.length,
		deep = false;
	//第一个值是布尔值,处理深浅合并
	if (typeof target === "boolean") {	
		deep = target;	//深合并
		target = arguments[i] ||{}; //次项是被替换对象
		i++;//为了获取替换对象
	}
	//target必须是对象
	if (typeof target !== "object" && !isFunction(target)) target = {};
	// 没有传递第二个对象,只是想把第一个传递的对象中的内容合并到$/ $.fn上
	if (i === length) { //只传入一个对象
		target = this;	 //target = $/ $.fn ,即合并原型或者对象上
		i--; // ->此时i索引对应的是传递的那个对象
	};
	//target 代表最终要被替换的对象,最后返回target
	// 接下来的循环就是拿到剩余的传递的对象,(可能是一个,也可能是多个),拿它们依次替换target
	for (; i < length; i++) {
		// options:每一轮循环中拿到的剩余的其中一个替换target的对象
		options = arguments[i];
		if (options == null) continue;
		for (name in options) {
			//copy 获取对象中的每一项,替换target中同名这一项
			copy = options[name];
			//获取的是对象的原型,被替换对象和替换对象相同,则忽略不作比较替换,退出当前循环,进行下一轮循环。[避免比较“套娃操作的对象”]
			if (name === "__proto__" || target === copy) continue ;			
			
			var copyIsObject = jQuery.isPlainObject(copy),//是否为纯粹对象
				copyIsArray = Array.isArray(copy);//是否为数组			
			// 深度合并options中的这一项需要是纯粹对象或者是数组对象,才有必要和target中对应的这一项进行深度对比,从而实现深度合并			
			if (deep && copy && copyIsObject || copyIsArray){
				// copy代表的是options中的这一项, 对象/数组	
				// src 代表的是target 中的这一项[clone]
				src = target[name];
				if (copyIsArray && !Array.isArray(src)) {
				// 如果options中的这一项是数组,但是target中的这一项不是数组,则让clone是一个数组
					clone = [];
				} else if (!copyIsArray && !jQuery.isPlainObject(src)) {
				// 如果options中的这一项不是数组,且target中的这一项也不是纯粹对象,则让clone是一个空对象
					clone = {};
				} else {	
					//target中的这一项是数组或者对象时,clone等于这一项			
					clone = src;
				};
				copyIsArray = false;
				// 基于递归的方式实现当前项的深度比较和深度合并
				target[name] = jQuery.extend(deep, clone, copy);			
			//不需要深度合并的直接用options中的这一项替换target中的这一项即可
			} else if (copy !== undefined) { 
			//值是undefined的时候是假删除状态,不再作合并处理
				target[name] = copy;// 浅合并
			};
		};
	};
	return target;//返回被合并替换的对象(目标对象)
};

封装数组和对象深/浅合并插件merge

// 实现数组和对象深/浅合并
var merge = function merge() {
    var options,  target = arguments[0] || {},  i = 1,
        length = arguments.length,
        treated = arguments[length - 1], deep = false;
    if (typeof target === "boolean") {
        deep = target;
        target = arguments[i] || {};
        i++;
    };
    
    //treated解决了jq源码中的也同样存在的递归溢出bug
    if (Array.isArray(treated) && treated.treated) {
        // 传递了记录已经处理过的内容的集合
        // 后期循环的时候少循环处理一项
        length--;
    } else {
        // 没传递,最后传递的这个值,不是用来存储处理过的内容,而是进行合并比较的
        treated = [];
        treated.treated = true;
    }
    //保证target必须是对象或者函数
    if (typeof target !== "object" && !isFunction(target)) target = {};
    //循环除target以外的剩下的对象
    for (; i < length; i++) {
        options = arguments[i];
        //options不存在退出当前循环
        if (options == null) continue;
        // 防止死递归处理的操作
        if (treated.indexOf(options) > -1) return options;
        treated.push(options);
        each(options, function (copy, name) {
            var copyIsArray = Array.isArray(copy),//是否为数组
                copyIsObject = isPlainObject(copy),//是否为纯对象
                src = target[name],
                clone = src;
            if (deep && copy && (copyIsArray || copyIsObject)) {
                if (copyIsArray && !Array.isArray(src)) clone = [];
                if (copyIsObject && !isPlainObject(src)) clone = {};
                target[name] = merge(deep, clone, copy, treated);
            } else if (copy !== undefined) {
                target[name] = copy;
            }
        });
    }
    return target;
};

插件中用到的 isPlainObject/ each 方法均来自utils.js(封装了一些常用的方法)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值