zoukankan      html  css  js  c++  java
  • js中有关类、对象的增强函数

    javascript中继承的实现

    基础实现

    function Range(from,to){
        this.from =from;
        this.to =to;
    }
    Range.prototype = {
        includes:function(x){
            return this.from<=x &&this.to >=x;
        },
        foreach:function(f){
            for(var x = Math.ceil(this.from);x<=this.to;x++){
                f(x);
            }
        },
        toString:function(){
            return "("+this.from+"..."+this.to+")";
        }
    }
    var r= new Range(1,3);
    console.log(r.includes(2));
    // 可以借鉴下面这种用法
    r.foreach(alert); // 弹出三次
    console.log(r.toString());
    

    线上链接

    寄生组合继承

    function SuperType() {
        this.property = true;
    }
    SuperType.prototype.superMethod = function () {
    }
    
    function LowType() {
        SuperType.call(this);
        this.lowProperty = true;
    }
    function inheritPrototype(subType, superType) {
        var prototype1 = object(superType.prototype);
        prototype1.constructor = subType;
        subType.prototype = prototype1;
    }
    inheritPrototype(LowType, SuperType);
    LowType.prototype.lowMethod = function () {
    };
    var lowType = new LowType();
    

    线上链接。线上链接主要讲最后一个例子,寄生组合继承,其实这里命名,在我后来都没遇到
    还有,如果实例对应的类原型的引用变化了,这个实例指向的原型会变吗?不会的。

    最优雅的写法

    /* @article https://johnresig.com/blog/simple-javascript-inheritance/
     * Simple JavaScript Inheritance
     * By John Resig http://ejohn.org/
     * MIT Licensed.
     */
    
    // Inspired by base2 and Prototype
    (function () {
        var initializing = false, fnTest = /xyz/.test(function () {
            xyz;
        }) ? /_super/ : /.*/;
        // The base Class implementation (does nothing)
        this.Class = function () {
        };
    
        // Create a new Class that inherits from this class
        Class.extend = function (prop) {
            var _super = this.prototype;
            // Instantiate a base class (but only create the instance,
            // don't run the init constructor)
            initializing = true;
            var prototype = new this();
            initializing = false;
    
            // Copy the properties over onto the new prototype
            for (var name in prop) {
                // Check if we're overwriting an existing function
                prototype[name] = typeof prop[name] == "function" &&
                typeof _super[name] == "function" && fnTest.test(prop[name]) ?
                        (function (name, fn) {
                            return function () {
                                var tmp = this._super;
                                // Add a new ._super() method that is the same method
                                // but on the super-class
                                this._super = _super[name];
    
                                // The method only need to be bound temporarily, so we
                                // remove it when we're done executing
                                var ret = fn.apply(this, arguments);
                                this._super = tmp;
                                return ret;
                            };
                        })(name, prop[name]) :
                        prop[name];
            }
    
    
            // The dummy class constructor
            function Class() {
                // All construction is actually done in the init method
                if (!initializing && this.init)
                    this.init.apply(this, arguments);
            }
    
            // Populate our constructed prototype object
            Class.prototype = prototype;
            // Enforce the constructor to be what we expect
            Class.prototype.constructor = Class;
            // And make this class extendable
            Class.extend = arguments.callee;
            return Class;
        };
    })();
    

    线上链接。这个继承是大牛写的,是我见过最简洁写法了,但是在工作中,一直没看到有人在子类方法内,要用到父类方法,直接使用this._super方法,还有init方法construction,也只会调用一次。继承中构造函数保持在原型链中。这个实现不支持继承静态方法,但以后如果有需求,我会尽量用这个方法。

    最繁重的写法

    var Class = (function () {
        // Class
        // -----------------
        // Thanks to:
        //  - http://mootools.net/docs/core/Class/Class
        //  - http://ejohn.org/blog/simple-javascript-inheritance/
        //  - https://github.com/ded/klass
        //  - http://documentcloud.github.com/backbone/#Model-extend
        //  - https://github.com/joyent/node/blob/master/lib/util.js
        //  - https://github.com/kissyteam/kissy/blob/master/src/seed/src/kissy.js
    
    
        // The base Class implementation.
        function Class(o) {
            // Convert existed function to Class.
            if (!(this instanceof Class) && isFunction(o)) {
                return classify(o)
            }
        }
    
    
        // Create a new Class.
        //
        //  var SuperPig = Class.create({
        //    Extends: Animal,
        //    Implements: Flyable,
        //    initialize: function() {
        //      SuperPig.superclass.initialize.apply(this, arguments)
        //    },
        //    Statics: {
        //      COLOR: 'red'
        //    }
        // })
        //
        Class.create = function (parent, properties) {
            if (!isFunction(parent)) {
                properties = parent
                parent = null
            }
    
            properties || (properties = {})
            parent || (parent = properties.Extends || Class)
            properties.Extends = parent
    
            // The created class constructor
            function SubClass() {
                // Call the parent constructor.
                parent.apply(this, arguments)
    
                // Only call initialize in self constructor.
                if (this.constructor === SubClass && this.initialize) {
                    this.initialize.apply(this, arguments)
                }
            }
    
            // Inherit class (static) properties from parent.
            if (parent !== Class) {
                mix(SubClass, parent, parent.StaticsWhiteList)
            }
    
            // Add instance properties to the subclass.
            implement.call(SubClass, properties)
    
            // Make subclass extendable.
            return classify(SubClass)
        }
    
    
        function implement(properties) {
            var key, value
    
            for (key in properties) {
                value = properties[key]
    
                if (Class.Mutators.hasOwnProperty(key)) {
                    Class.Mutators[key].call(this, value)
                } else {
                    this.prototype[key] = value
                }
            }
        }
    
    
        // Create a sub Class based on `Class`.
        Class.extend = function (properties) {
            properties || (properties = {})
            properties.Extends = this
    
            return Class.create(properties)
        }
    
    
        function classify(cls) {
            cls.extend = Class.extend
            cls.implement = implement
            return cls
        }
    
    
        // Mutators define special properties.
        Class.Mutators = {
    
            'Extends': function (parent) {
                var existed = this.prototype
                var proto = createProto(parent.prototype)
    
                // Keep existed properties.
                mix(proto, existed)
    
                // Enforce the constructor to be what we expect.
                proto.constructor = this
    
                // Set the prototype chain to inherit from `parent`.
                this.prototype = proto
    
                // Set a convenience property in case the parent's prototype is
                // needed later.
                this.superclass = parent.prototype
            },
    
            'Implements': function (items) {
                isArray(items) || (items = [items])
                var proto = this.prototype, item
    
                while (item = items.shift()) {
                    mix(proto, item.prototype || item)
                }
            },
    
            'Statics': function (staticProperties) {
                mix(this, staticProperties)
            }
        }
    
    
        // Shared empty constructor function to aid in prototype-chain creation.
        function Ctor() {
        }
    
        // See: http://jsperf.com/object-create-vs-new-ctor
        var createProto = Object.__proto__ ?
                function (proto) {
                    return {__proto__: proto}
                } :
                function (proto) {
                    Ctor.prototype = proto
                    return new Ctor()
                }
    
    
        // Helpers
        // ------------
    
        function mix(r, s, wl) {
            // Copy "all" properties including inherited ones.
            for (var p in s) {
                if (s.hasOwnProperty(p)) {
                    if (wl && indexOf(wl, p) === -1) continue
    
                    // 在 iPhone 1 代等设备的 Safari 中,prototype 也会被枚举出来,需排除
                    if (p !== 'prototype') {
                        r[p] = s[p]
                    }
                }
            }
        }
    
    
        var toString = Object.prototype.toString
    
        var isArray = Array.isArray || function (val) {
                    return toString.call(val) === '[object Array]'
                }
    
        var isFunction = function (val) {
            return toString.call(val) === '[object Function]'
        }
    
        var indexOf = Array.prototype.indexOf ?
                function (arr, item) {
                    return arr.indexOf(item)
                } :
                function (arr, item) {
                    for (var i = 0, len = arr.length; i < len; i++) {
                        if (arr[i] === item) {
                            return i
                        }
                    }
                    return -1
                }
    
        return Class
    })()
    

    线上链接。这个继承是功能最全面的,包括类实现里,怎么实现静态方法,但是它的语法糖太多了,ExtendsImplementsStaticsinitialize,继承中的构造函数提出来,放在当前类的构造函数中执行,new初始化时的钩子,是initialize,只会调用一次,继承中initialize也可以调用。

    brix里的实现

    // http://g.alicdn.com/thx/brix-release/1.0.0-beta.9/brix-base/dist/base-debug.js
    
    var _ = {}
    var toString = Object.prototype.toString
    
    _.each = function (obj, iterator, context) {
        if (obj === null || obj === undefined) return obj
        if (obj.forEach) {
            obj.forEach(iterator, context);
        } else if (obj.length === +obj.length) {
            for (var i = 0, length = obj.length; i < length; i++) {
                iterator.call(context, obj[i], i, obj)
            }
        } else {
            for (var prop in obj) {
                iterator.call(context, obj[prop], prop, obj)
            }
        }
        return obj
    }
    
    _.each('Boolean Number String Function Array Date RegExp Object Error'.split(' '), function (name) {
        _['is' + name] = function (obj) {
            return toString.call(obj) == '[object ' + name + ']'
        }
    })
    _.extend = function () {
        var target = arguments[0]
        var index = 1
        var length = arguments.length
        var deep = false
        var options, name, src, copy, clone
    
        if (typeof target === "boolean") {
            deep = target
            target = arguments[index] || {}
            index++
        }
    
        if (typeof target !== "object" && typeof target !== "function") {
            target = {}
        }
    
        if (length === 1) {
            target = this
            index = 0
        }
    
        for (; index < length; index++) {
            options = arguments[index]
            if (!options) continue
    
            for (name in options) {
                src = target[name]
                copy = options[name]
    
                if (target === copy) continue
                if (copy === undefined) continue
    
                if (deep && (_.isArray(copy) || _.isObject(copy))) {
                    if (_.isArray(copy)) clone = src && _.isArray(src) ? src : []
                    if (_.isObject(copy)) clone = src && _.isObject(src) ? src : {}
    
                    target[name] = _.extend(deep, clone, copy)
                } else {
                    target[name] = copy
                }
            }
        }
    
        return target
    }
    
    /*
     This function is loosely inspired by Backbone.js.
     http://backbonejs.org
     */
    function extend(protoProps, staticProps) {
        var parent = this
    
        // 构造函数 Initialize constructor
        var constructor = protoProps && protoProps.hasOwnProperty('constructor') ?
                protoProps.constructor : // 自定义构造函数 Custom constructor
                parent // 父类构造函数 Base constructor
    
        // 子类 Subclass
        var child = function () {
            parent.__x_created_with = child.__x_created_with
            var instance = constructor.apply(this, arguments) || this
    
            // instance.options vs parameter options
            var options = arguments[0]
            if (options && !instance.hasOwnProperty('options')) {
                instance.options = _.extend(true, {}, this.options, options)
            }
    
            // 如果模块带有 __x_created_with,则一切初始化行为都交给第三方;否则调用 .create() 方法。
            // If the child module has a property named as __x_created_with, the third-library will be response for the rest of initialization actions.
            // If not, the child module will call the `.create()`.
            if (!child.__x_created_with && instance.created && instance.constructor === child) {
                instance.created.apply(instance, instance.created.length ? [instance.options] : []) // @edit
            }
    
            return instance
        }
    
        // 静态属性 Static properties
        _.extend(child, parent, staticProps)
    
        // 原型链 Build prototype chain.
        var Surrogate = function () {
    //      this.constructor = constructor // @remove
        }
        Surrogate.prototype = parent.prototype
        child.prototype = new Surrogate()
        child.prototype.constructor = child // @add
    
        // 原型属性 Copy prototype properties from the parameter protoProps to the prototype of child
        if (protoProps) _.extend(child.prototype, protoProps)
    
        // Add keyword __super__
        child.__super__ = parent.prototype
    
        child.extend = extend
    
        return child
    }
    
    function Brix() {
    }
    Brix.prototype = {
        created: function () {
            if (this.init) this.init()
            if (this.render) this.render()
        },
        constructor: Brix // @add
    }
    Brix.extend = extend
    

    Brix Base里关于继承的实现,因为为了支持框架Brix Loader,所以继承里做了特殊处理,比如options参数,比如__x_created_with,继承中的构造函数提出来,放在当前类的构造函数中执行,new时初始化钩子,是created方法调用init、render,所以初始化语句可以放在init、render,按正常结果只调用一次,但这里调用了两次,重复了。

    magix里实现

    magix中View如何实现继承?也是类似brix,但它不包含任何语法糖,new初始化钩子,是

    View.extend = function(props, statics, ctor) {
    

    第三个参数ctor是初始化钩子,这里会多次调用

    总结

    为了实现类似Person.extend能生成新类,extend要向封装传入原型上方法,父类,静态方法,每次执行extend时要定义新类的构造函数。返回的新类也能extend再生成子类。
    封装内,将父类的构造函数和原型拆分。新类的构造函数执行父类的构造函数,新类的原型指向Object.create(父类的原型)。构造函数在实例化时才执行,原型链实现,在实现继承时就执行。
    新类的构造函数中执行父类的构造函数,父类的构造函数再执行祖先的构造函数...,直到最顶层。最顶层构造函数可自由实现。在simple-inherit中是封装外面的Class,在brix中是首次直接赋值了extend方法的类。complex-inherit中,是由Class.create传入的。
    实例化类的时候,要自动执行初始化的操作,正常是在构造函数中实现的,extend封装里的构造函数是统一写死的,无法自由做一些初始化,要在构造函数里提供个钩子,构造函数执行这个钩子函数,钩子函数,在定义新类的时候传入,也就是Person.extend时传入。
    钩子一般是定义在原型上的某方法,比如initialize方法,因为构造函数里是调父类的构造函数,一直到最顶层基类,也要多次执行构造函数内钩子吗?钩子是这样执行的this.initialize.apply(this, arguments),this在每次执行时都不变的,也没必要多次执行,只要执行一次即可。并且只执行原型链中,第一个有钩子方法的原型,剩下的都不执行了。
    实现原型链继承时,要注意原型的constructor属性,为了子类方法能调父类方法,给每个类加个superclass属性,指向父类的原型对象。

    mix

    	// https://g.alicdn.com/mm/pubplus/0.4.12/app_debug/exts/arale/class.js
        function mix(r, s, wl) {
          // Copy "all" properties including inherited ones.
          for (var p in s) {
            if (s.hasOwnProperty(p)) {
              if (wl && indexOf(wl, p) === -1) continue
    
              // 在 iPhone 1 代等设备的 Safari 中,prototype 也会被枚举出来,需排除
              if (p !== 'prototype') {
                r[p] = s[p]
              }
            }
          }
        }
    

    extend在库里实现

    underscore

    将后面对象的自有可枚举属性,拷贝到第一个对象,并返回第一个对象

      // http://g.alicdn.com/thx/brix-release/1.0.0-beta.9/underscore/underscore.js	
      // Extend a given object with all the properties in passed-in object(s).
      _.extend = function(obj) {
        if (!_.isObject(obj)) return obj;
        var source, prop;
        for (var i = 1, length = arguments.length; i < length; i++) {
          source = arguments[i];
          for (prop in source) {
            if (hasOwnProperty.call(source, prop)) {
                obj[prop] = source[prop];
            }
          }
        }
        return obj;
      };
    

    jquery

    支持深度拷贝,深拷贝除了支持对象还包括数组,改变第一个对象,并返回第一个对象

    // http://g.alicdn.com/thx/brix-release/1.0.0-beta.9/jquery/dist/jquery.js
    jQuery.extend = jQuery.fn.extend = function() {
    	var src, copyIsArray, copy, name, options, clone,
    		target = arguments[ 0 ] || {},
    		i = 1,
    		length = arguments.length,
    		deep = false;
    
    	// Handle a deep copy situation
    	if ( typeof target === "boolean" ) {
    		deep = target;
    
    		// skip the boolean and the target
    		target = arguments[ i ] || {};
    		i++;
    	}
    
    	// 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 ( i === length ) {
    		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;
    };  
    

    kissy1.3中有关对象的方法

    mix

    改变并返回第一个参数,下面实现方法中MIX_CIRCULAR_DETECTION标记,感觉是为了调试,看deep了几次

    /*
     * for example:
     *     @example
     *     var t = {};
     *     S.mix({x: {y: 2, z: 4}}, {x: {y: 3, a: t}}, {deep: TRUE}) => {x: {y: 3, z: 4, a: {}}}, a !== t
     *     S.mix({x: {y: 2, z: 4}}, {x: {y: 3, a: t}}, {deep: TRUE, overwrite: false}) => {x: {y: 2, z: 4, a: {}}}, a !== t
     *     S.mix({x: {y: 2, z: 4}}, {x: {y: 3, a: t}}, 1) => {x: {y: 3, a: t}}
     */
    mix: function (r, s, ov, wl, deep) {
        if (typeof ov === 'object') {
            wl = ov['whitelist'];
            deep = ov['deep'];
            ov = ov['overwrite'];
        }
        var cache = [],
                c,
                i = 0;
        mixInternal(r, s, ov, wl, deep, cache);
        while (c = cache[i++]) {
            delete c[MIX_CIRCULAR_DETECTION];
        }
        return r;
    }
    
    
    function mixInternal(r, s, ov, wl, deep, cache) {
        if (!s || !r) {
            return r;
        }
    
        if (ov === undefined) {
            ov = TRUE;
        }
    
        var i = 0, p, keys, len;
    
        // 记录循环标志
        s[MIX_CIRCULAR_DETECTION] = r;
    
        // 记录被记录了循环标志的对像
        cache.push(s);
    
        if (wl) {
            len = wl.length;
            for (i = 0; i < len; i++) {
                p = wl[i];
                if (p in s) {
                    _mix(p, r, s, ov, wl, deep, cache);
                }
            }
        } else {
            // mix all properties
            keys = S.keys(s);
            len = keys.length;
            for (i = 0; i < len; i++) {
                p = keys[i];
                if (p != MIX_CIRCULAR_DETECTION) {
                    // no hasOwnProperty judge!
                    _mix(p, r, s, ov, wl, deep, cache);
                }
            }
        }
    
        return r;
    }
    
    function _mix(p, r, s, ov, wl, deep, cache) {
        // 要求覆盖
        // 或者目的不存在
        // 或者深度mix
        if (ov || !(p in r) || deep) {
            var target = r[p],
                    src = s[p];
            // prevent never-end loop
            if (target === src) {
                // S.mix({},{x:undefined})
                if (target === undefined) {
                    r[p] = target;
                }
                return;
            }
            // 来源是数组和对象,并且要求深度 mix
            if (deep && src && (S.isArray(src) || S.isPlainObject(src))) {
                if (src[MIX_CIRCULAR_DETECTION]) {
                    r[p] = src[MIX_CIRCULAR_DETECTION];
                } else {
                    // 目标值为对象或数组,直接 mix
                    // 否则 新建一个和源值类型一样的空数组/对象,递归 mix
                    var clone = target && (S.isArray(target) || S.isPlainObject(target)) ?
                            target :
                            (S.isArray(src) ? [] : {});
                    r[p] = clone;
                    mixInternal(clone, src, ov, wl, TRUE, cache);
                }
            } else if (src !== undefined && (ov || !(p in r))) {
                r[p] = src;
            }
        }
    }
    

    merge

    合并所有obj到返回新

    merge: function (var_args) {
        var_args = S.makeArray(arguments);
        var o = {},
            i,
            l = var_args.length;
        for (i = 0; i < l; i++) {
            S.mix(o, var_args[i]);
        }
        return o;
    },
    

    augment

    只对原型对象进行复制,改变并返回第一个参数

    augment: function (r, var_args) {
        var args = S.makeArray(arguments),
            len = args.length - 2,
            i = 1,
            ov = args[len],
            wl = args[len + 1];
    
        if (!S.isArray(wl)) {
            ov = wl;
            wl = undefined;
            len++;
        }
        if (!S.isBoolean(ov)) {
            ov = undefined;
            len++;
        }
    
        for (; i < len; i++) {
            S.mix(r.prototype, args[i].prototype || args[i], ov, wl);
        }
    
        return r;
    },
    

    extend

    继承的实现,跟上面封装的继承相比,还是有缺点的,首先新类的构造函数执行时候,父类的构造函数不会自动执行,总觉得superclass的指向的原型,中间多了一层

    extend: function (r, s, px, sx) {
        if (!s || !r) {
            return r;
        }
    
        var sp = s.prototype,
            rp;
    
        // add prototype chain
        rp = createObject(sp, r);
        r.prototype = S.mix(rp, r.prototype);
        r.superclass = createObject(sp, s);
    
        // add prototype overrides
        if (px) {
            S.mix(rp, px);
        }
    
        // add object overrides
        if (sx) {
            S.mix(r, sx);
        }
    
        return r;
    },
        
    function Empty() {
    }
    function createObject(proto, constructor) {
        var newProto;
        if (ObjectCreate) {
            newProto = ObjectCreate(proto);
        } else {
            Empty.prototype = proto;
            newProto = new Empty();
        }
        newProto.constructor = constructor;
        return newProto;
    }
    

    更多例子以及在例子中,我对继承的摸索http://sprying.github.io/webtest/class/index.html

  • 相关阅读:
    第三节:MySQL的存储引擎及常用数据类型介绍
    第一节:MySQL的安装及使用
    10-单例模式
    windows linux—unix 跨平台通信集成控制系统----系统硬件信息获取
    windows linux—unix 跨平台通信集成控制系统----文件搜索
    Linux下查看内核、CPU、内存及各组件版本的命令和方法
    Linux下使用popen()执行shell命令
    图像边缘检测--OpenCV之cvCanny函数
    OpenCV GUI基本操作,回调函数,进度条,裁剪图像等
    图像边缘检测--OpenCV之cvCanny函数
  • 原文地址:https://www.cnblogs.com/sprying/p/6907004.html
Copyright © 2011-2022 走看看