javascript库中mixin和extend函数终极pk

很多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:
    • r Functionthe object to modify.
    • s Functionthe object to inherit.
    • px Objectprototype properties to add/override.
    • sx Objectstatic properties to add/override.
  • 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:
    • 0Default. 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 truemerge 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

    });

});

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值