JS继承的奇怪时刻

时间:2014-02-11 22:42:32

标签: javascript prototype prototypal-inheritance

这是我的超类:

function Element() {
   var name;
   this.setName(n) = func()...{};
   this.getName() = func()..{return name};
}

我的另一个孩子班:

Select = null;
...
Select =
   function (n) {
       if (typeof n !== "undefined")
          this.setName(n);
   ...
}
Select.prototype = new Element();
Select.prototype.constructor = Select;

那么,我在谈论什么样的“怪异时刻”?这是:

var e1 = new Select("element1");
e1.getName();  // return "element1"
var e2 = new Select();  // WITHOUT NAME
e2.getName();  // return "element1"!!! should be ""!

这是一个相当可预测的行为,但如何解决这个问题呢? 当然,我可以在this.clear()中创建类似Element的内容,它会清除属性并将此方法放在Select函数中,但也许有一个合适的解决方案?

2 个答案:

答案 0 :(得分:3)

您应该将此行Element.call(this, n)添加到Select。这很难解释,我觉得脾气暴躁,因为我不喜欢javascript中的假私有属性,但是,我必须提供一些细节,以便你了解你目前在做什么,否则我将无法到达睡眠。

因此,new Element()创建一个新的上下文,name可以在不打扰任何人的情况下生活。此外,还创建了两个名为setNamegetName的新函数,并绑定到prototype的{​​{1}}对象。

从现在开始,如果您创建Select的新实例,则可以调用这些函数,因为它们在实例的原型中可用。这实际上发生在Select。实际上,new Select("element1")已定义,因此nsetName调用,它引用实例。

但最重要的是,将this设置为setName时调用n也会将"element1"设置为name。然后,如果您创建了"element1"的第二个实例,但未定义Select,则不会调用n,因此,setName仍设置为name对于两个实例

为什么为两个实例?因为两个实例共享相同的"element1"方法,绑定到原型的方法,并且此方法引用唯一的setName变量 - 请记住name仅被调用一次。

最后,为什么新行Element会阻止Element.call(this, n)被分享?因为每次调用name都会创建一个新的上下文,也就是说,新的Element,新的name和新的setName,并将这两种方法绑定到新创建的实例。

嗯,希望这是你的早晨,如果你因我的错而陷入睡眠障碍,我会很抱歉......


正如BergiHMR所提到的,这种创建原型的方式 - getName - 已经过时了,会让你陷入死胡同。请记住,原型是用于创建实例的模板。知道这一点并考虑你的代码,我们至少可以做两个观察:

  1. 如您所知,构造函数旨在初始化实例。在您的情况下,初始化过程 - Child.prototype = new Parent内的内容 - 被不必要地执行,因为目前的目标是设置将在其上创建实例的模板。
  2. 假设Element中需要name,否则您的程序会崩溃。你会给原型什么名字?这个问题显然没用,因为你不想给模板命名。
  3. 要绕过这些问题,您需要一个中间人,如下面的代码所示(从Bergi共享的link复制)。如您所见,原型的创建被委托给一个空的“虚拟”构造函数。这样做可以解决上面提出的两个问题。

    new Element(name)

    或者,您可以使用内置的function Dummy () {} Dummy.prototype = Element.prototype; Select.prototype = new Dummy(); Select.prototype.constructor = Select;

    Object.create()

    进一步阅读:

答案 1 :(得分:2)

您应该使用Element的原型,以便不在实例之间共享变量:

function Element() {}    
Element.prototype.setName = function(n) { this.name = n; };
Element.prototype.getName = function() { return this.name; };

在这种情况下,e2.getName()将返回undefined