带有原型的Javascript函数继承

带有原型的Javascript函数继承,javascript,inheritance,performance,prototypal-inheritance,Javascript,Inheritance,Performance,Prototypal Inheritance,在Douglas Crockford的JavaScript:The Good Parts中,他建议我们使用函数继承。下面是一个例子: var mammal = function(spec, my) { var that = {}; my = my || {}; // Protected my.clearThroat = function() { return "Ahem"; }; that.getName = function

在Douglas Crockford的JavaScript:The Good Parts中,他建议我们使用函数继承。下面是一个例子:

var mammal = function(spec, my) {
    var that = {};
    my = my || {};

    // Protected
    my.clearThroat = function() { 
        return "Ahem";
    };

    that.getName = function() {
        return spec.name;
    };

    that.says = function() {
        return my.clearThroat() + ' ' + spec.saying || '';
    };

    return that;
};

var cat = function(spec, my) {
    var that = {};
    my = my || {};

    spec.saying = spec.saying || 'meow';
    that = mammal(spec, my);

    that.purr = function() { 
        return my.clearThroat() + " purr"; 
    };

    that.getName = function() { 
        return that.says() + ' ' + spec.name + ' ' + that.says();
    };

    return that;
};

var kitty = cat({name: "Fluffy"});
我遇到的主要问题是,每次我制作
哺乳动物
时,JavaScript解释器都必须重新编译其中的所有函数。也就是说,您不能在实例之间共享代码


我的问题是:如何使这段代码更高效?例如,如果我制作了数千个
cat
对象,那么修改此模式以利用
原型
对象的最佳方式是什么

好吧,如果你打算大量生产
哺乳动物
,你就不能这样做。取而代之的是用老式的方式(原型)继承财产。您仍然可以按照上面的方法来构造构造函数,但是您可以使用隐式的
this
和一些表示基类的变量(在本例中,
this.demalator
),而不是
that
my


我会使用
my
以外的另一个名称作为基本访问,并将其存储在
this
中的
cat
构造函数中。在本例中,我使用了
哺乳动物
,但如果您希望静态访问全局
哺乳动物
对象,这可能不是最好的方法。另一个选项是命名变量
base

,如果您想要隐私并且不喜欢protyping,您可能喜欢也可能不喜欢这种方法:

(注意:它使用jQuery.extend)

var namespace=namespace | |{};
//虚拟基类
namespace.base=函数(子,未定义){
var base={instance:this};
base.hierarchy=[];
base.fn={
//检查base是否属于某个类(必须委派)
is:功能(CONTR){
返回(this.hierarchy[this.hierarchy.length-1]==constr);
},
//检查base是否扩展了某个类(必须委派)
继承:函数(constr){
for(var i=0;i
唯一的缺点是,由于隐私范围,您只能扩展虚拟类,而不能扩展实例类

下面是我如何扩展namespace.base以绑定/取消绑定/触发自定义事件的示例

// virtual base class for controls
namespace.controls.base = function (sub) {

    var base = { instance: this };

    base.keys = {
        unknown: 0,
        backspace: 8,
        tab: 9,
        enter: 13,
        esc: 27,
        arrowUp: 38,
        arrowDown: 40,
        f5: 116
    }

    base.fn = {

        // bind/unbind custom events. (has to be called via delegate)
        listeners: {

            // bind custom event
            bind: function (type, fn) {

                if (fn != undefined) {

                    if (this.listeners[type] == undefined) {
                        throw (this.type + ': event type \'' + type + '\' is not supported');
                    }

                    this.listeners[type].push(fn);
                }

                return this;
            },

            // unbind custom event
            unbind: function (type) {

                if (this.listeners[type] == undefined) {
                    throw (this.type + ': event type \'' + type + '\' is not supported');
                }

                this.listeners[type] = [];

                return this;
            },

            // fire a custom event
            fire: function (type, e) {

                if (this.listeners[type] == undefined) {
                    throw (this.type + ': event type \'' + type + '\' does not exist');
                }

                for (var i = 0; i < this.listeners[type].length; i++) {

                    this.listeners[type][i](e);
                }

                if(e != undefined) e.stopPropagation();
            }
        }
    };

    base.public = {
        bind: base.fn.listeners.bind,
        unbind: base.fn.listeners.unbind
    };

    base = new namespace.base(base);

    base.fire = base.fn.delegate(base, base.fn.listeners.fire);

    return base.extend(sub);
};
//控件的虚拟基类
namespace.controls.base=函数(子){
var base={instance:this};
base.keys={
未知:0,
退格:8,
表9:,
输入:13,
esc:27,
截至:38,
向下箭头:40,
f5:116
}
base.fn={
//绑定/取消绑定自定义事件。(必须通过委托调用)
听众:{
//绑定自定义事件
绑定:函数(类型,fn){
如果(fn!=未定义){
if(this.listeners[type]==未定义){
抛出(this.type+':不支持事件类型\'+'type+'\');
}
this.listeners[type].push(fn);
}
归还这个;
},
//解除绑定自定义事件
解除绑定:函数(类型){
if(this.listeners[type]==未定义){
抛出(this.type+':不支持事件类型\'+'type+'\');
}
this.listeners[type]=[];
归还这个;
},
//触发自定义事件
火灾:功能(类型e){
if(this.listeners[type]==未定义){
抛出(this.type+':事件类型\''+type+'\'不存在);
}
对于(var i=0;i
让我向您介绍从未使用原型的经典继承。这是一个糟糕的编码练习,但会教给你真正的经典继承,它总是与原型继承相比较:

var namespace = namespace || {};

// virtual base class
namespace.base = function (sub, undefined) {

    var base = { instance: this };

    base.hierarchy = [];

    base.fn = {

        // check to see if base is of a certain class (must be delegated)
        is: function (constr) {

            return (this.hierarchy[this.hierarchy.length - 1] === constr);
        },

        // check to see if base extends a certain class (must be delegated)
        inherits: function (constr) {

            for (var i = 0; i < this.hierarchy.length; i++) {

                if (this.hierarchy[i] == constr) return true;
            }
            return false;
        },

        // extend a base (must be delegated)
        extend: function (sub) {

            this.hierarchy.push(sub.instance.constructor);

            return $.extend(true, this, sub);
        },

        // delegate a function to a certain context
        delegate: function (context, fn) {

            return function () { return fn.apply(context, arguments); }
        },

        // delegate a collection of functions to a certain context
        delegates: function (context, obj) {

            var delegates = {};

            for (var fn in obj) {

                delegates[fn] = base.fn.delegate(context, obj[fn]);
            }

            return delegates;
        }
    };

    base.public = {
        is: base.fn.is,
        inherits: base.fn.inherits
    };

    // extend a sub-base
    base.extend = base.fn.delegate(base, base.fn.extend);

    return base.extend(sub);
};

namespace.MyClass = function (params) {

    var base = { instance: this };

    base.vars = {
        myVar: "sometext"
    }

    base.fn = {
        init: function () {

            base.vars.myVar = params.myVar;
        },

        alertMyVar: function() {

            alert(base.vars.myVar);
        }

    };

    base.public = {
        alertMyVar: base.fn.alertMyVar
    };

    base = namespace.base(base);

    base.fn.init();

    return base.fn.delegates(base,base.public);
};

newMyClass = new namespace.MyClass({myVar: 'some text to alert'});
newMyClass.alertMyVar();
// virtual base class for controls
namespace.controls.base = function (sub) {

    var base = { instance: this };

    base.keys = {
        unknown: 0,
        backspace: 8,
        tab: 9,
        enter: 13,
        esc: 27,
        arrowUp: 38,
        arrowDown: 40,
        f5: 116
    }

    base.fn = {

        // bind/unbind custom events. (has to be called via delegate)
        listeners: {

            // bind custom event
            bind: function (type, fn) {

                if (fn != undefined) {

                    if (this.listeners[type] == undefined) {
                        throw (this.type + ': event type \'' + type + '\' is not supported');
                    }

                    this.listeners[type].push(fn);
                }

                return this;
            },

            // unbind custom event
            unbind: function (type) {

                if (this.listeners[type] == undefined) {
                    throw (this.type + ': event type \'' + type + '\' is not supported');
                }

                this.listeners[type] = [];

                return this;
            },

            // fire a custom event
            fire: function (type, e) {

                if (this.listeners[type] == undefined) {
                    throw (this.type + ': event type \'' + type + '\' does not exist');
                }

                for (var i = 0; i < this.listeners[type].length; i++) {

                    this.listeners[type][i](e);
                }

                if(e != undefined) e.stopPropagation();
            }
        }
    };

    base.public = {
        bind: base.fn.listeners.bind,
        unbind: base.fn.listeners.unbind
    };

    base = new namespace.base(base);

    base.fire = base.fn.delegate(base, base.fn.listeners.fire);

    return base.extend(sub);
};
function Person(name, age){
  this.name = name;
  this.age = age;
  this.sayHello = function(){return "Hello! this is " + this.name;}
}
function Student(name, age, grade){
  Person.apply(this, [name, age]);
  this.grade = grade
}
var pete = new Student('Pete', 7, 1);
function Person(name, age){
  this.name = name;
  this.age = age;
  this.sayHello = function(){
    return "Hello! this is " + this.name + ". I am " this.age + " years old";
  }
}
  var Mammal = function (spec) {
    this.spec = spec;
}.define({
    clearThroat: function () { return "Ahem" },
    getName: function () {
        return this.spec.name;
    },
    says: function () {
        return this.clearThroat() + ' ' + spec.saying || '';
    }
});

var Cat = Mammal.inheritWith(function (base, baseCtor) {
    return {
        constructor: function(spec) { 
            spec = spec || {};
            baseCtor.call(this, spec); 
        },
        purr: function() {
            return this.clearThroat() + " purr";
        },
        getName: function() {
            return this.says() + ' ' + this.spec.name + this.says();
        }
    }
});

var kitty = new Cat({ name: "Fluffy" });
kitty.purr(); // Ahem purr
kitty.getName(); // Ahem Fluffy Ahem
var Mammal = function (spec) {
    this.spec = spec;
}.define({
    clearThroat: function () { return "Ahem" },
    getName: function () {
        return this.spec.name;
    },
    says: function () {
        return this.clearThroat() + ' ' + spec.saying || '';
    }
});

var Cat = Mammal.fastClass(function (base, baseCtor) {
    return function() {
        this.constructor = function(spec) { 
            spec = spec || {};
            baseCtor.call(this, spec); 
        };
        this.purr = function() {
            return this.clearThroat() + " purr";
        },
        this.getName = function() {
            return this.says() + ' ' + this.spec.name + this.says();
        }
    }
});

var kitty = new Cat({ name: "Fluffy" });
kitty.purr(); // Ahem purr
kitty.getName(); // Ahem Fluffy Ahem
Function.prototype.fastClass = function (creator) {
    var baseClass = this, ctor = (creator || function () { this.constructor = function () { baseClass.apply(this, arguments); } })(this.prototype, this)

    var derrivedProrotype = new ctor();

    if (!derrivedProrotype.hasOwnProperty("constructor"))
        derrivedProrotype.constructor = function () { baseClass.apply(this, arguments); }

    derrivedProrotype.constructor.prototype = derrivedProrotype;
    return derrivedProrotype.constructor;
};
Function.prototype.inheritWith = function (creator, makeConstructorNotEnumerable) {
    var baseCtor = this;
    var creatorResult = creator.call(this, this.prototype, this) || {};
    var Derrived = creatorResult.constructor ||
    function defaultCtor() {
        baseCtor.apply(this, arguments);
    }; 
    var derrivedPrototype;
    function __() { };
    __.prototype = this.prototype;
    Derrived.prototype = derrivedPrototype = new __;

    for (var p in creatorResult)
        derrivedPrototype[p] = creatorResult[p];

    if (makeConstructorNotEnumerable && canDefineNonEnumerableProperty) //this is not default as it carries over some performance overhead
        Object.defineProperty(derrivedPrototype, 'constructor', {
            enumerable: false,
            value: Derrived
        });

    return Derrived;
};
Function.prototype.define = function (prototype) {
    var extendeePrototype = this.prototype;
    if (prototype)
        for (var p in prototype)
            extendeePrototype[p] = prototype[p];
    return this;
}