代码之家  ›  专栏  ›  技术社区  ›  cdmckay

带原型的javascript函数继承

  •  13
  • cdmckay  · 技术社区  · 14 年前

    在道格拉斯·克罗克福德 javascript:好的部分 他建议我们使用功能继承。下面是一个例子:

    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"});
    

    我的主要问题是每次我 mammal cat javascript解释器必须重新编译其中的所有函数。也就是说,您不能在实例之间共享代码。

    我的问题是:如何使此代码更高效?例如,如果我 对象,修改此模式以利用 prototype 对象?

    4 回复  |  直到 11 年前
        1
  •  8
  •   Plynx    14 年前

    如果你打算做很多 mammal cat . 相反,用传统的方式(原型)并按属性继承。您仍然可以按照上面的方法来执行构造函数,但不是 that my 你使用了隐式 this 以及一些表示基类的变量(在本例中, this.mammal )

    cat.prototype.purr = function() { return this.mammal.clearThroat() + "purr"; }
    

    我会用另一个名字 我的 用于基本访问并存储在 构造函数。在这个例子中,我使用 哺乳动物 但如果你想的话,这可能不是最好的 静止的 访问全球 哺乳动物 对象。另一个选项是命名变量 base .

        2
  •  1
  •   Mohsen    11 年前

    让我给你介绍一个从未使用过的经典遗产 prototype . 这是一个糟糕的编码练习,但会教你 古典继承 它总是与 原型继承 :

    做一个客户:

    function Person(name, age){
      this.name = name;
      this.age = age;
      this.sayHello = function(){return "Hello! this is " + this.name;}
    }
    

    创建另一个继承它的Cunstructor:

    function Student(name, age, grade){
      Person.apply(this, [name, age]);
      this.grade = grade
    }
    

    很简单! Student 调用(应用) Person 就其本身而言 name age 争论解决了 grade 论点本身。

    现在我们来举一个例子 学生 .

    var pete = new Student('Pete', 7, 1);
    

    pete 对象现在将包含 名称 , 年龄 , 等级 sayHello 属性。它 拥有 所有这些属性。它们不会被上传到 通过原型。如果我们改变 对此:

    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";
      }
    }
    

    皮特 不会收到更新。如果我们打电话 pete.sayHello ,TI将返回 Hello! this is pete . 它不会得到新的更新。

        3
  •  0
  •   caroen    13 年前

    如果你想要隐私,你不喜欢打印,你可能喜欢,也可能不喜欢这种方法:

    (注:使用jquery.extend)

    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();
    

    唯一的缺点是,由于隐私范围的原因,您只能扩展虚拟类,而不能扩展实例类。

    下面是我如何扩展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);
    };
    
        4
  •  0
  •   DATEx2    11 年前

    要正确使用基于javascript原型的继承,可以使用 fastClass https://github.com/dotnetwise/Javascript-FastClass

    你有更简单的 inheritWith 风味:

      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
    

    如果你非常关心表现,那么 FAST类 风味:

    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
    

    顺便说一句,您的初始代码没有任何意义,但我确实尊重它。

    FAST类 效用:

    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;
    };
    

    define 效用:

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

    [*免责声明,我是开源软件包的作者,方法本身的名称将来可能会被重命名`*]