【问题标题】:JavaScript: Adding inherited class to array does not workJavaScript:将继承的类添加到数组不起作用
【发布时间】:2012-06-15 20:45:11
【问题描述】:

我在使用 JavaScript 时遇到了一些问题。我有以下代码:

<html>
<head>
<title>Test</title>
<script  type="text/javascript">
function Control(){
    var name;

    this.setName = function(newName){
        name = newName;
    };

    this.getName = function(){
        return name;
    };
}

function SpecializedControl(){
}
SpecializedControl.prototype = new Control();

function Form(){
    var formControls = [];

    this.addControl = function(control){
         formControls.push(control);

         alert(formControls[0].getName());
    };
}

var form = new Form();

var control1 = new SpecializedControl();
control1.setName("Control1");
form.addControl(control1);

var control2 = new SpecializedControl();
control2.setName("Control2");
form.addControl(control2);

</script>
</head>
<body>
</body>
</html>

SpecializedControl 继承自 Control 类。

Form 类中的 addControl 函数只是将控件添加到数组中。

问题是,当我添加多个 SpecializedControl 时,数组中的值会被覆盖,这意味着当我访问数组中的第一项(应该是“Control1”)时,我会得到“Control2”。 Control1 不再在数组中。

当我使用与 Control 对象相同的函数作为参数时,一切都按预期工作。

有人知道为什么会发生这种情况以及可以做些什么来纠正这个问题吗?

【问题讨论】:

    标签: javascript arrays inheritance prototype


    【解决方案1】:

    您的 get/setName 函数正在获取/设置 name 变量的值,该变量是 Control 构造函数的本地变量。

    您调用该函数的唯一一次是创建一个实例作为SpecializedControlprototype 对象。因此,每次您从 SpecializedControl 实例调用 setName 时,都会更新该单个变量。

    因此,由于引用该变量的 get/setName 方法位于所有 SpecializedControl 实例的原型链中,它们都将观察到相同的 name


    setName,你应该这样做...

    this.name = newName;
    

    getName,你应该这样做......

    return this.name;
    

    【讨论】:

      【解决方案2】:

      数组中的值没有被覆盖;问题是两个控件共享相同的name 变量。因为Control 函数只执行一次,所以只声明了一个name 变量。

      您有两个主要选项来解决此问题。 (1) 使name 成为特定于每个单独控件的实例变量(例如this._name)。 (2) 从SpecializedControl 构造函数内部执行Control 函数。 (实际上,IMO,对于一个平衡良好且彻底的继承模型,您应该对这两种方法都进行一些操作)。

      这里有三个可行的解决方案。前两个分别使用选项 (1) 和 (2)。第三个结合了这两种方法,是我会做的方式(但需要joi)。

      选项 1:

      function Control(){
      
          this.setName = function(newName){
              this._name = newName;
          };
      
          this.getName = function(){
              return this._name;
          };
      }
      

      选项 2:

      function SpecializedControl(){
          Control.apply(this, arguments);
      }
      

      选项 3:

      var Control = joi.Unit.sub(function() {
      
          function constructor() {
              this.base();
          }
      
          constructor.prototype = {
              '#name': null,
              setName: function(name) {
                  this['#name'] = name;
              },
              getName: function() {
                  return this['#name'];
              }
          };
      
          return constructor;
      
      }());
      
      var SpecializedControl = Control.sub(function() {
      
          function constructor() {
              this.base();
          }
      
          return constructor;
      
      }());
      
      var Form = joi.Unit.sub(function() {
      
          function constructor() {
              this.base();
              this['#formControls'] = [];
          }
      
          constructor.prototype = {
              '#formControls': null,
              addControl: function(control) {
                  this['#formControls'].push(control);
                  alert(this['#formControls'][0].getName());
              }
          };
      
          return constructor;
      
      }());
      
      var form = new Form();
      
      var control1 = new SpecializedControl();
      control1.setName("Control1");
      form.addControl(control1);
      
      var control2 = new SpecializedControl();
      control2.setName("Control2");
      form.addControl(control2);​
      

      【讨论】:

      • 我推荐第二种方案,在子类构造函数中调用超类的构造函数。这似乎是正确的做法。第一个选项需要公开name 变量,第三个选项使用一个额外的库来尝试实现常规的基于类的继承来替代 JavaScript 的原型继承。
      • joi 使用原型继承,而不是经典继承
      • 哦,我的错。尽管如此,#name 按照惯例只是内部的,实际上并不能阻止您从外部访问它。使用闭包,您可以拥有真正的私有变量。
      • 选项 2 对我来说看起来不错并解决了问题。谢谢:-)
      • 是的,选项 2 是一个不错的选择。他们都有自己的好处和一些缺点。顺便说一下 Mattias,选项 2 的一个缺点是它不允许在构造函数的原型上覆盖 setName 和 getName 方法。由于每次调用构造函数时都会生成新的闭包,因此它也比将函数放在原型上使用更多的内存。不过,选项 2 并没有错;值得指出的是,您可能会选择其他选项。
      猜你喜欢
      • 2014-02-25
      • 1970-01-01
      • 2011-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多