【问题标题】:JavaScript prototypal inheritance workaroundJavaScript 原型继承解决方法
【发布时间】:2011-10-07 00:45:09
【问题描述】:

这是一个例子:

var Box = function() {
    this.id = generateUniqueId();
};
Box.prototype = {
    add:function(parent) {
        parent.appendChild(this.elm);
    }
};

var NewBox = function() {
    this.newThing = true;
};
NewBox.prototype = new Box();
NewBox.prototype.remove = function() {
    this.elm.parentNode.removeChild(this.elm);
};

var a = new NewBox();
var b = new NewBox();
alert(a.id); // alerts 0
alert(b.id); // also alerts 0! :@

我希望a和b(基本上每次我创建一个NewBox)都有自己的id。我知道NewBox 正在使用原型继承,因此从Box 获得的生成id 的单个实例继承,但我希望每个NewBox 都有自己的id 而没有必须在 NewBox 构造函数中明确说明。

也许这是不可能的,或者我做错了什么,所以请帮忙!

非常感谢!

【问题讨论】:

    标签: javascript inheritance prototype-programming


    【解决方案1】:

    在您的示例中,Box 构造函数仅在您设置 NewBox.prototype 对象时才会执行。

    您可以通过使用Function.prototype.apply 方法调用NewBox 内的Box 构造函数来解决此问题,以设置this 值并转发所有参数值,例如:

    //..
    var NewBox = function() {
        Box.apply(this, arguments);
        this.newThing = true;
    };
    //..
    
    var a = new NewBox();
    var b = new NewBox();
    
    // assuming your `generateUniqueId` function
    // increments a number
    
    alert(a.id); // will alert 1
    alert(b.id); // will alert 2
    

    现在,每次调用NewBox 构造函数来创建一个新的对象实例(new NewBox();),它都会调用Box 函数来应用它的所有逻辑。这将帮助您避免一遍又一遍地重复父构造函数的逻辑。

    apply 用于调用“父”构造函数,将 this 值设置为由 NewBox 构造函数创建的对象实例,我们将提供的所有参数传递给该函数。

    您的示例还显示了一个常见问题,当您通过NewBox.prototype = new Box(); 表达继承关系时,Box 构造函数被调用并且它具有副作用,这个新对象将被初始化并且您的generateUniqueId 函数将被执行为第一次,如果你想避免这种情况,你需要使用临时构造函数,只是为了创建一个继承自 Box.prototype 的新对象,或者出于相同目的使用新的 ECMAScript 5 Object.create 方法,例如:

    function inherit(o) {
      function Tmp() {}
      Tmp.prototype = o;
      return new Tmp();
    }
    
    //.....
    NewBox.prototype = inherit(Box.prototype);
    

    或者:

    NewBox.prototype = Object.create(Box.prototype);
    

    这样,您无需第一次运行 Box 构造函数即可表达相同的继承层次结构,从而避免了它可能导致的任何副作用。

    最后但同样重要的是,每当你替换一个函数的原型属性时,总是建议恢复这个新原型对象的constructor属性,否则它会指向错误的函数。

    在您的示例中,由于您将NewBox.prototype 替换为Box 的新实例,因此NewBox.prototype.constructor 属性将指向Box,(您的实例受到影响,例如a.constructor === Box; // true)而不是@ 987654350@ 如您所料,我们需要将其设置回来,例如:

    NewBox.prototype = someObject; // as any of the examples above
    NewBox.prototype.constructor = NewBox;
    

    您可以将这些细节抽象成一个函数,就像我在上面的 inherit 函数中所做的那样:

    function inherits(child, parent) {
      var obj, Tmp = function () {};
      Tmp.prototype = parent.prototype;
      obj = new Tmp();
      child.prototype = obj;
      child.prototype.constructor = child;
    }
    
    //...
    
    // instead of setting the `NewBox.prototype` manually
    inherits(NewBox, Box); // "NewBox inherits from Box"
    
    //...
    

    【讨论】:

    • 非常感谢您的回答!我学到了很多;我就像你说的那样实现了它,它工作得很好。这是我一直在寻找的甜蜜 javascript 解决方法。
    • 这是调用 Box 作为一个函数,而不是作为一个构造函数,所以你只是在 NewBox 构造函数中设置 id,根据马特的回答.
    • @RobG,是的,我只是说“调用 Box 构造函数”我不是说“调用 Box 作为构造函数”。或许“将 Box 构造函数作为函数调用”“使用 Function.prototype.apply 调用 Box 函数” 应该够清楚了吧?
    • @tau,不客气!如果我的回答完全满足您的问题,您可以将其标记为已接受...
    • 这应该被标记为正确答案。 Matt Ball 的回答很好,但这确实解释了问题所在,也就是说,OP 只是为了设置继承而实例化一个基础对象。这是我的一篇关于如何正确设置继承的帖子的插件js-bits.blogspot.com/2010/08/…
    【解决方案2】:

    也许这是不可能的,或者我做错了什么,所以请帮忙!

    你做错了什么。

    如果您需要特定于实例的值,请在构造函数中初始化它们,而不是在原型中。

    【讨论】:

    • 这就是我想出的答案,但我希望围绕它有一些甜蜜的 javascript 技巧。谢谢!
    【解决方案3】:

    Matt Ball 有正确的想法。而是尝试:

    var Box = (function(){ 
        var numberOfBoxes = 0;
        function() {
            this.id = numberOfBoxes++;
        }
    })();
    

    或者如果您希望所有(不同的)类都具有唯一的 ID:

    var generateUniqueID = (function(){
        var runningCount = 0;
        return function (){
            return runningCount++;
        }
    })();
    
    var Box = function() {
        this.id = generateUniqueId();
    };
    
    var NewBox = function() {
        this.id = generateUniqueId();
        this.newThing = true;
    };
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-03-19
    • 1970-01-01
    • 1970-01-01
    • 2013-05-09
    • 2010-09-28
    • 2018-02-21
    • 1970-01-01
    相关资源
    最近更新 更多