Source: core/Class.js

/**
 * Class是提供类的创建的辅助工具。
 * @namespace Class
 * @example
 * const Bird = Hilo3d.Class.create({
 *     Extends: Animal,
 *     Mixes: EventMixin,
 *     constructor: function(name){
 *         this.name = name;
 *     },
 *     fly: function(){
 *         console.log('I am flying');
 *     },
 *     Statics: {
 *         isBird: function(bird){
 *             return bird instanceof Bird;
 *         }
 *     }
 * });
 *
 * const swallow = new Bird('swallow');
 * swallow.fly();
 * Bird.isBird(swallow);
 */
const Class = (function() {
    let defineProperty;
    let defineProperties;
    try {
        defineProperty = Object.defineProperty;
        defineProperties = Object.defineProperties;
        defineProperty({}, '$', {
            value: 0
        });
    } catch (e) {
        if ('__defineGetter__' in Object) {
            defineProperty = function(obj, prop, desc) {
                if ('value' in desc) obj[prop] = desc.value;
                if ('get' in desc) obj.__defineGetter__(prop, desc.get); // eslint-disable-line
                if ('set' in desc) obj.__defineSetter__(prop, desc.set); // eslint-disable-line
                return obj;
            };
            defineProperties = function(obj, props) {
                for (const prop in props) {
                    if (props.hasOwnProperty(prop)) { // eslint-disable-line
                        defineProperty(obj, prop, props[prop]);
                    }
                }
                return obj;
            };
        }
    }

    /**
     * 混入属性或方法。
     * @memberOf Class
     * @method mix
     * @param {any} target 混入目标对象。
     * @param {...any} source 要混入的属性和方法来源。可支持多个来源参数。
     * @returns {any} 混入目标对象。
     */
    const mix = function(target) {
        for (let i = 1, len = arguments.length; i < len; i++) {
            const source = arguments[i];
            let defineProps;
            for (const key in source) {
                const prop = source[key];
                if (prop && typeof prop === 'object') {
                    if (prop.value !== undefined || typeof prop.get === 'function' || typeof prop.set === 'function') {
                        defineProps = defineProps || {};
                        defineProps[key] = prop;
                        continue;
                    }
                }
                target[key] = prop;
            }
            if (defineProps) defineProperties(target, defineProps);
        }

        return target;
    };

    const createProto = (function() {
        if (Object.__proto__) { // eslint-disable-line
            return function(proto) {
                return {
                    __proto__: proto
                };
            };
        }
        const Ctor = function() {};
        return function(proto) {
            Ctor.prototype = proto;
            return new Ctor();
        };
    }());

    const classMutators = /** @ignore */ {
        Extends(parent) {
            const existed = this.prototype;
            const proto = createProto(parent.prototype);
            // inherit static properites
            mix(this, parent);
            // keep existed properties
            mix(proto, existed);
            // correct constructor
            proto.constructor = this;
            // prototype chaining
            this.prototype = proto;
            // shortcut to parent's prototype
            this.superclass = parent.prototype;
        },

        Mixes(items) {
            if (!(items instanceof Array)) {
                items = [items];
            }
            const proto = this.prototype;
            let item;

            while (item = items.shift()) { // eslint-disable-line
                mix(proto, item.prototype || item);
            }
        },

        Statics(properties) {
            mix(this, properties);
        }
    };

    const implement = function(properties) {
        const proto = {};
        let value; let
            key;
        for (key in properties) {
            value = properties[key];
            if (classMutators.hasOwnProperty(key)) { // eslint-disable-line
                classMutators[key].call(this, value);
            } else {
                proto[key] = value;
            }
        }

        mix(this.prototype, proto);
    };

    /**
     * 根据参数指定的属性和方法创建类。
     * @memberOf Class
     * @method create
     * @param {Object} params 要创建的类的相关属性和方法。
     * @param {Object} [params.Statics] 指定类的静态属性或方法。
     * @param {Object} [params.Extends] 指定要继承的父类。
     * @param {Object} [params.Mixes] 指定要混入的成员集合对象
     * @param {Function} [params.constructor] 构造函数
     * @param {any} [params.[value:string]] 其他创建类的成员属性或方法。
     */
    const create = function(properties) {
        properties = properties || {};
        const clazz = properties.hasOwnProperty('constructor') ? properties.constructor : function() {}; // eslint-disable-line
        implement.call(clazz, properties);
        return clazz;
    };


    return {
        create,
        mix
    };
}());

export default Class;