JavaScript Object.extend 实现深层对象合并
本文分析jQuery内置extend方法在处理嵌套对象时的局限性,并提供一个自定义的Object.extend函数实现。通过判断参数类型与递归调用,该函数支持对任意深度的子对象进行属性复制,解决了浅层合并导致子对象丢失的问题,适用于需要完整继承复杂对象结构的场景。
jQuery extend的局限性
在函数开发中,通常需要为参数对象设置默认值,并通过扩展机制将传入的参数覆盖到默认参数中。例如:
代码示例如下:
代码如下:
giant.ui.imageshow = function(options) {
this.opts = $.extend({}, giant.ui.imageshow.defaults, options);
}
giant.ui.imageshow.defaults = {
id:"imageshow",
isAuto:true,
speed:3000
}; jQuery框架提供了一个名为extend的工具函数:
jQuery.extend(target,obj1,[objN])
该函数用于将一个或多个源对象扩展到一个目标对象,并返回被修改的目标对象。
它常用于简化继承操作。
Extend one object with one or more others, returning the original, modified, object.
This is a great utility for simple inheritance.
返回值类型为Object。
参数说明:
target (Object):目标对象,即被修改的对象。
object1 (Object):源对象,其属性将被合并到目标对象中。
objectN (Object):可选,其他待合并到目标对象的源对象。
然而,框架内置的extend方法存在明显缺陷,即无法继承对象内部的嵌套对象。举例说明:
代码示例如下:
代码如下:
var obj1 = {},
var obj2={name:"karry",email:"karry@a.com",tel:{homeTel:"158255",officeTel:"02112585"}}
obj1 = $.extend({},obj1 ,obj2 ); 执行后,obj1仅包含name和email属性。由于tel本身是一个对象,其内部的homeTel和officeTel属性未能被继承。
我的目标是实现一种能够对子对象的子属性进行完整复制(继承)的功能,无论嵌套层级有多深。
自定义Object.extend实现
首先分析该方法参数,包含三个参数:target(目标对象)、source(源对象)和deep(布尔值,指示是否递归复制嵌套对象)。若deep为true,则继承所有层级;若为false,则与jQuery默认行为一致,不继承子对象。
代码实现如下:
代码如下:
Object.extend = function(target, /*optional*/source, /*optional*/deep) {} 我将第一个参数target设为必选,而source和deep设为可选。这带来一个问题:当只传入两个参数时,如何区分第二个参数是source还是deep?因此需要判断第二个参数的类型。
代码示例如下:
代码如下:
target = target || {}; //target默认为空对象
var sType = typeof source;
//如果第二个参数的类型为undefined或boolean
if( sType === 'undefined' || sType === 'boolean' ) {
deep = sType === 'boolean' ? source : false;
source = target; //将target赋值给source
target = this; //这里的this指向Object
} 部分读者可能对最后两行代码存疑。我的处理逻辑是:如果target和source都存在且source不是布尔值,则将source的内容复制到target;否则,将target复制到Object对象。此时deep默认为false。
为确保安全,还需判断source是否满足条件且不是Object类型,或者它是Function对象(涉及其他问题),此时无法进行复制。因此,将source设为空Object,即不进行复制操作。
代码示例如下:
代码如下:
if( typeof source !== 'object' && Object.prototype.toString.call(source) !== '[object Function]' )
source = {}; 注:Function对象在执行typeof操作时返回“object”,但无法在此方法中正确复制,因此必须排除。
接下来进行循环复制,利用递归实现。
代码示例如下:
代码如下:
var i=1,option;
//外层循环依次修改options,先设为target,后设为source
while(i <= 2) {
options = i === 1 ? target : source;
if( options != null ) {
//内层循环复制对应属性
for( var name in options ) {
var src = target[name], copy = options[name];
if(target === copy)
continue;
//如果deep设为true,且该属性是对象
if(deep && copy && typeof copy === 'object' && !copy.nodeType)
//递归调用
target[name] = this.extend(src ||(copy.length != null ? [] : {}), copy, deep);
else if(copy !== undefined)
target[name] = copy;
}
}
i++;
} 此处利用递归方式依次复制对象内的嵌套对象,功能完成。完整代码如下:
代码示例如下:
代码如下:
/*
* @param {Object} target 目标对象。
* @param {Object} source 源对象。
* @param {boolean} deep 是否复制(继承)对象中的对象。
* @returns {Object} 返回继承了source对象属性的新对象。
*/
Object.extend = function(target, /*optional*/source, /*optional*/deep) {
target = target || {};
var sType = typeof source, i = 1, options;
if( sType === 'undefined' || sType === 'boolean' ) {
deep = sType === 'boolean' ? source : false;
source = target;
target = this;
}
if( typeof source !== 'object' && Object.prototype.toString.call(source) !== '[object Function]' )
source = {};
while(i <= 2) {
options = i === 1 ? target : source;
if( options != null ) {
for( var name in options ) {
var src = target[name], copy = options[name];
if(target === copy)
continue;
if(deep && copy && typeof copy === 'object' && !copy.nodeType)
target[name] = this.extend(src ||
(copy.length != null ? [] : {}), copy, deep);
else if(copy !== undefined)
target[name] = copy;
}
}
i++;
}
return target;
}; 使用示例
使用示例:
代码示例如下:
代码如下:
var source = {id:1, name:'Jack Source'}, target = {name:'Jack Target', gender:1,tel:{homeTel:"158255",officeTel:"02112585"}};
var newObj1 = Object.extend(target, source);JS语法错误会阻止加载吗?区分SyntaxError与资源加载失败
2026-11-05 16:08:46
JavaScript常见SyntaxError语法错误排查与修复方法
2026-11-05 13:58:44
JS中slice、substring和substr截取字符串的区别
2026-11-05 13:43:47
JS 转 TS 手动迁移最佳实践:从配置 tsconfig 到消除 any 类型
2026-11-05 11:53:32
为什么JS数组push后得到的是数字而不是数组?
2026-11-05 09:59:18
