【问题标题】:JavaScript build a constructor of constructorsJavaScript 构建构造函数的构造函数
【发布时间】:2014-02-12 22:05:36
【问题描述】:

这是我想要的一个简单示例:

var ConstBuilder = function() {
    var constructor = function() {} ;
    constructor.prototype = {} ;
    return constructor ;
} ;

ConstBuilder.prototype = {
    add : function(name, value) {
        this.prototype[name] = value ;
    }
} ;

var A = new ConstBuilder() ;
A.add('test', function() {
    console.log('test') ;
}) ;

var a = new A() ;
a.test() ;

此代码将失败,因为 A 不是 ConstBuilder 的实例(因为 A 来自返回的 var constructor = function() {} 并且不会在其原型 (add) 中定义方法。

但这对于将超级构造函数的原型修改为具有以下内容很有用:

ConstBuilder.prototype.remove = function(name) {
    delete this.prototype[name] ;
} ;

A.remove('test') ;

a.test ; // undefined

有没有办法让一个函数作为另一个函数的实例?所以这个函数可能会隐式地“继承”其构造函数原型中定义的所有方法。

或者,如果您有其他建议,我的目标是构建可模块化的构造函数 - 就像带有原型的实例一样。

【问题讨论】:

    标签: javascript constructor prototype


    【解决方案1】:

    请确保您了解.prototype 属性和内部继承原型之间的区别。


    代码将失败,因为 A 不是 ConstBuilder 的实例。有没有办法让一个函数作为另一个函数的实例?

    A 是每个构造函数都需要的Function。因此,如果您只在 Function.prototype 上定义您的 addremove 方法,它将起作用:

    Function.prototype.add = function(name, value) {
        this.prototype[name] = value;
    };
    Function.prototype.remove = function(name) {
        delete this.prototype[name];
    };
    
    function A() {}
    A.add('test', function(){console.log('test');});
    var a = new A();
    a.test(); // test
    
    A.remove('test');
    a.test; // undefined
    

    但是,不可能让函数继承自 Function.prototype 以外的其他东西 - 请参阅 Can a JavaScript object have a prototype chain, but also be a function?。如果不想修改原生的Function.prototype对象,还是可以使用mixin pattern

    var Constr = (function() {
        function add(name, value) {
            this.prototype[name] = value;
        }
        function remove(name) {
            delete this.prototype[name];
        }
        return function mixin(c) {
            c.add = add;
            c.remove = remove;
            return c;
        };
    })();
    
    var A = Constr(function() {…});
    A.add("test", …);
    var a = new A();
    a.test(); // test
    

    我的目标是构建可模块化的构造函数

    您可以使用builder pattern,就像您刚刚尝试过的那样。

    function ConstBuilder() {
        this.prototype = {};
    };
    
    ConstBuilder.prototype = {
        add: function(name, value) {
            this.prototype[name] = value;
        },
        remove: function(name) {
            delete this.prototype[name];
        },
        getConstructor: function() {
            var constructor = function() {};
            constructor.prototype = this.prototype;
            this.prototype.constructor = constructor;
            return constructor;
        }
    };
    
    var A = new ConstBuilder().add('test', function() {
        console.log('test');
    }).getConstructor();
    var a = new A();
    a.test(); // test
    

    以后要删除函数,您需要保存对builder 的引用。

    【讨论】:

    • 我也想到了Function.prototype,但我不想修改通用的Function 对象。但是您的第二个答案可能是最好的解决方案,即使我更愿意直接使用构造函数。现在我知道没有别的办法了。 :) 谢谢!
    • 在两者之间还有混合模式,您可以将其应用于仅将您的方法提供给少数选定的(构造函数)函数:-)
    • 这就是我所做的,但没有更多的可模块化性,而且我的上下文也有问题:在混合方法之后,我必须将它们全部与构造函数绑定。
    • 你可能想问另一个问题:-)
    【解决方案2】:

    我认为您正在寻找如何进行 JavaScript 的“原型继承”的示例。当 JavaScript 查找对象的属性时,它首先检查对象本身。接下来它检查原型。但是,由于 JavaScript 中的一切都是对象,而原型也是对象

    function Root(){}
    Root.prototype.fromRoot = function() { console.log("I'm on Root's prototype."); };
    function Child(){}
    Child.prototype = new Root();
    Child.prototype.fromChild = function() { console.log("I'm on Child's prototype."); };
    
    var r = new Root();
    var c = new Child();
    
    r.fromRoot();  // works
    c.fromRoot();  // works
    c.fromChild(); // works
    r.fromChild(); // fails
    

    【讨论】:

    • 不好的例子,您在 Parent 的实例中创建以设置 Child 继承的原型部分,并且您不会通过在 Child 的正文中使用 Parent.call(this,args); 来重新使用 Parent 的 cosntructor 代码。即使不需要,因为在这种情况下没有实例成员。在正常情况下,您希望重新使用 Parent 构造函数代码,而您的示例没有显示如何。
    【解决方案3】:
    function a (x,y,construct)
        {
        if (!construct) return;
        this.x=x;
        this.y=y;
        }
    
    a.prototype.methoda=function ()
        {
        return x+y;
        }
    
    function b (x,y,d,e)
        {
        a.call (this,x,y,true) //--- this would inherit all own Objects and Properties of a and become own properties of b
        this.d=d;
        this.e=e;
        }
    
      b.prototype=new a ();  //--- this would only inherit the prototype, construct becomes false and isnt worked through, which if not would result in adding propertiy x and y to prototype instead of directly to instance of b,
      b.prototype.constructor=b;
    
      var test=new b (1,2,3,4);
      b.methoda ();
    

    第二种方式

    function a (x,y)
        {
        if (arguments.callee.doNotConstruct) return;
        this.x=x;
        this.y=y;
        }
    
    a.prototype.methoda=function ()
        {
        return x+y;
        }
    
    
    function b (x,y,d,e)
        {
        a.call (this,x,y) //--- this would inherit all own Objects and Properties of a and become own properties of b
        this.d=d;
        this.e=e;
        }
      a.doNotConstruct=true;
      b.prototype=new a ();  //--- this would only inherit the prototype, construct becomes false and isnt worked through, which if not would result in adding propertiy x and y to prototype instead of directly to instance of b,
      a.doNotConstruct=false;
    
      b.prototype.constructor=b;
    
      var test=new b (1,2,3,4);
      b.methoda ();
    

    把它放在一个函数中

     function prototypeInheritance (inheritor,parent)
     {
     parent.doNotConstruct=true;
     inheritor=new parent ();
     inheritor.prototype.constructor=inheritor;
     inheritor.parent=parent;
     parent.doNotConstruct=false;
     }
    

    您可以在继承构造函数中使用 (arguments.callee.parent) 调用父属性,并且可以在父构造函数中使用 arguments.callee.doNotConstruct 来检查 doNotConstruct

    【讨论】:

    • 您不应该创建 Parent 的实例来设置 Child 中继承的原型部分,因为这可能会导致问题。如果没有传递构造参数,则通过返回来解决问题。很好,很有创意,但现在你必须在每次创建实例时传递它。要设置原型部分,您可以使用 Object.create 并使用 polyfil 如果您想支持旧浏览器。
    • 对不起,HMR,我从 2002 年开始就是 Javascript 的程序员,我一直这样做,没有任何问题。每个构造函数都有一个构造属性,这是例行公事。 Object.create 是新的,旧浏览器不支持。
    • Object.create 在闭包库中被使用了很多年。至少是接受一个论点的 polyfil。这是解决仅获取原型部分的好方法,但我个人会发现每次我想创建一个实例时都必须向每个构造函数传递一个额外的参数很不方便。
    • 也可以在构造函数constructor.doNotConstruct=true中添加一个属性,然后将if语句改为if (arguments.callee.doNotConstruct) return;并在对原型进行继承之前设置 doNotConstruct 属性,然后将其重置……如果闭包库完全可以克隆原型,我不会感到痛苦……如果您有具有获取或设置功能的属性怎么办由原型中的 Object.defineProperty 定义
    • Object.create 不会克隆。它返回参数 2 或带有参数 1 的空对象作为原型。 goog.inherits 和 MDN polyfil 所做的正是如此。除了他们不支持第二个论点。将构造属性设置为 false 完全相同,返回一个空对象,构造函数原型作为它的原型。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-06-30
    • 2011-05-13
    • 1970-01-01
    • 2021-01-08
    • 2013-10-13
    • 2017-01-21
    相关资源
    最近更新 更多