很多javascript库中都提供了mixin/extend函数,方便用户将多个object合并或者生成新的object。这个函数会在OO中用到,但是不仅仅局限于OO的继承。
基本思想:
function mixin(old,my,bOverwrite) {
var my = my || {},
key,bOverwrite = bOverwrite || false;
for (key in old) {
if (old.hasOwnProperty(key)) { //不像 for in operator, hasownproperty 不追踪prototype chain
if(typeof(my[key]) != 'undefined' && bOverwrite){
continue;
}
my[key] = old[key];
}
}
return my;
}
jQuery的extend:
http://api.jquery.com/jQuery.extend/
输入参数时注意是要要从object1加入到object2,还是从两个建立起一个新的object。
源码中关键就是用for in,然后target[ name ] = copy; 没有检查hasownproperty,也就是说追踪prototype chain;忽略undefined;检查array
jQuery.extend = jQuery.fn.extend = function() {
var options, name, src, copy, copyIsArray, clone,
target = arguments[0] || {},
i = 1,
length = arguments.length,
deep = false;
// Handle a deep copy situation
if ( typeof target === "boolean" ) {
deep = target;
target = arguments[1] || {};
// skip the boolean and the target
i = 2;
}
// Handle case when target is a string or something (possible in deep copy)
if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
target = {};
}
// extend jQuery itself if only one argument is passed
if ( length === i ) {
target = this;
--i;
}
for ( ; i < length; i++ ) {
// Only deal with non-null/undefined values
if ( (options = arguments[ i ]) != null ) {
// Extend the base object
for ( name in options ) {
src = target[ name ];
copy = options[ name ];
// Prevent never-ending loop
if ( target === copy ) {
continue;
}
// Recurse if we're merging plain objects or arrays
if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
if ( copyIsArray ) {
copyIsArray = false;
clone = src && jQuery.isArray(src) ? src : [];
} else {
clone = src && jQuery.isPlainObject(src) ? src : {};
}
// Never move original objects, clone them
target[ name ] = jQuery.extend( deep, clone, copy );
// Don't bring in undefined values
} else if ( copy !== undefined ) {
target[ name ] = copy;
}
}
}
}
// Return the modified object
return target;
};
在YUI中被细分为4种:mix/augment/extend/merge
http://yuilibrary.com/yui/docs/yui/yui-mix.html
http://yuilibrary.com/yui/docs/yui/yui-augment.html
- Create Class Hierarchies with `extend`
- extend ( r s px sx ) ObjectProvided by the oop module.
Defined in oop/js/oop.js:155
Utility to set up the prototype, constructor and superclass properties to support an inheritance strategy that can chain constructors and methods. Static members will not be inherited.
Parameters: - Returns:the extended object.
- Compose Classes of Objects with `augment`
- augment ( receiver supplier [overwrite=false] [whitelist] [args] ) FunctionProvided by the oop module.
Defined in oop/js/oop.js:31
Augments the receiver with prototype properties from the supplier. The receiver may be a constructor function or an object. The supplier must be a constructor function.
If the receiver is an object, then the supplier constructor will be called immediately after receiver is augmented, with receiver as the this object.
If the receiver is a constructor function, then all prototype methods of supplier that are copied to receiver will be sequestered, and the supplierconstructor will not be called immediately. The first time any sequestered method is called on the receiver's prototype, all sequestered methods will be immediately copied to the receiver's prototype, the supplier's constructor will be executed, and finally the newly unsequestered method that was called will be executed.
This sequestering logic sounds like a bunch of complicated voodoo, but it makes it cheap to perform frequent augmentation by ensuring that suppliers' constructors are only called if a supplied method is actually used. If none of the supplied methods is ever used, then there's no need to take the performance hit of calling the supplier's constructor.
Parameters:- receiver Function | ObjectObject or function to be augmented.
- supplier FunctionFunction that supplies the prototype properties with which to augment the receiver.
- [overwrite=false] Boolean optionalIf true, properties already on the receiver will be overwritten if found on the supplier's prototype.
- [whitelist] String[] optionalAn array of property names. If specified, only the whitelisted prototype properties will be applied to the receiver, and all others will be ignored.
- [args] Array | any optionalArgument or array of arguments to pass to the supplier's constructor when initializing.
Returns:Function: Augmented object.
- Add Behaviors to Objects with `mix`
- mix ( receiver supplier [overwrite=false] [whitelist] [mode=0] [merge=false] ) Function | Object | YUIDefined in yui/js/yui-core.js:80
Mixes supplier's properties into receiver.
Properties on receiver or receiver's prototype will not be overwritten or shadowed unless the overwrite parameter is true, and will not be merged unless the merge parameter is true.
In the default mode (0), only properties the supplier owns are copied (prototype properties are not copied). The following copying modes are available:- 0: Default. Object to object.
- 1: Prototype to prototype.
- 2: Prototype to prototype and object to object.
- 3: Prototype to object.
- 4: Object to prototype.
Parameters:- receiver Function | ObjectThe object or function to receive the mixed properties.
- supplier Function | ObjectThe object or function supplying the properties to be mixed.
- [overwrite=false] Boolean optionalIf true, properties that already exist on the receiver will be overwritten with properties from the supplier.
- [whitelist] String[] optionalAn array of property names to copy. If specified, only the whitelisted properties will be copied, and all others will be ignored.
- [mode=0] Number optionalMix mode to use. See above for available modes.
- [merge=false] Boolean optionalIf true, objects and arrays that already exist on the receiver will have the corresponding object/array from the supplier merged into them, rather than being skipped or overwritten. When both overwrite and merge are true, merge takes precedence.
Returns:Function | Object | YUI: The receiver, or the YUI instance if the specified receiver is falsy.
- Combine Data Sets with `merge`
- merge ( objects ) ObjectDefined in yui/js/yui-core.js:55
Returns a new object containing all of the properties of all the supplied objects. The properties from later objects will overwrite those in earlier objects.
Passing in a single object will create a shallow copy of it. For a deep copy, use clone().
Parameters:- objects Object multipleOne or more objects to merge.
Returns:Object: A new merged object.
相比jQuery,YUI做的太专业了,选项很多,区别在于:extend是OO中给class用的,augment是模块化用的;mix和merge是给所有的object用的。mix是把b mix 到a;merge比较简单,就是把两样东西合成一样新东西,把a和b生成c
kissy库中的mixin依赖于YUI,以下是kissy中自动测试的代码:
describe('kissy.js', function() {
var S = KISSY,
host = S.__HOST;
it('S.mix', function() {
var o1 = { a: 1, b: 2 },
o2 = { a: 1, b: 2 },
o3 = { a: 1, b: 2 },
//o4 = { a: 1, b: 2 },
o = { a: 'a', c: true };
S.mix(o1, o);
expect(o1.a).toBe('a');
// turn off override
S.mix(o2, o, false);
expect(o2.a).toBe(1);
// whitelist
S.mix(o3, o, true, ['c']);
expect(o3.a).toBe(1);
// deep object mix testcase
var r = {
x:1,
y:{ z:1 },
q:[2,5]
};
S.mix(r, {
x:2,
y:{
s:1
},
q:[6]
});
expect(r.x).toBe(2);
expect(r.y.s).toBe(1);
expect(r.y.z).toBeUndefined();
expect(r.q + "").toBe([6] + "");
// 1.2 新加 deep 参数测试
r = {
x:1,
y:{
z:1
},
q:[2,5]
};
S.mix(r, {
x:2,
y:{
s:1
},
q:[undefined,6]
}, undefined, undefined, true);
expect(r.x).toBe(2);
expect(r.y.s).toBe(1);
expect(r.y.z).toBe(1);
expect(r.q + "").toBe([2,6] + "");
});
it('S.merge', function() {
var a = {
'bool' : false,
'num' : 0,
'nul' : null,
'undef': undefined,
'str' : 'blabber'
},
b = {
'bool' : 'oops',
'num' : 'oops',
'nul' : 'oops',
'undef': 'oops',
'str' : 'oops'
};
var c = S.merge(a, b);
expect(c.bool).toBe('oops');
expect(c.num).toBe('oops');
expect(c.nul).toBe('oops');
expect(c.undef).toBe('oops');
expect(c.str).toBe('oops');
});
it('S.augment', function() {
function Bird(name) {
this.name = name;
}
Bird.prototype = {
getName: function() {
return this.name;
},
fly: function() {
}
};
function Pig(name) {
this.name = name;
}
S.augment(Pig, Bird, { prop: 'test prop' });
S.augment(Pig, { weight: '100kg' });
var pig = new Pig('Babe');
expect(typeof pig.fly).toBe('function');
expect(pig.prop).toBe('test prop');
expect(pig.weight).toBe('100kg');
});
it('S.extend', function() {
function Bird(name) {
this.name = name;
}
Bird.prototype = {
getName: function() {
return this.name;
}
};
function Chicken(name) {
Chicken.superclass.constructor.call(this, name);
}
Chicken.prototype.featcher = 2;
S.extend(Chicken, Bird);
var chicken = new Chicken('Tom');
expect(chicken.constructor).toBe(Chicken);
expect(chicken.getName()).toBe('Tom');
expect(chicken.featcher).toBe(2); // keep existed prototype members
});
});