【问题标题】:Javascript prototypal inheritance and OOPJavascript原型继承和OOP
【发布时间】:2011-11-11 23:45:15
【问题描述】:

我正在创建一个允许用户创建小部件的应用程序。有几种不同类型的小部件,我使用原型继承定义了它们。即

//the base widget that all widgets inherit from
var Widget = function(){}        
Widget.prototype.someFunction = function(){}


//widget type A
var A = function(){}    
A.prototype = new Widget();


//widget type B
var B = function(){}    
B.prototype = new Widget();

我发现在基类上添加一个可以创建相同类型的新小部件实例的方法会很方便。即

//the base widget
var Widget = function(){};        

Widget.prototype.clone = function(){
    switch(this.type){
         case 'A':
             return new A();
             break;
         case 'B':
             return new B();
             break;
         default:
             break;
    }
};

这将允许我使用以下代码获得相同类型的新小部件:

var widgetTypeA = new A();
var cloneOfWidgetTypeA = widgetTypeA.clone();

我担心的是,基本小部件现在必须明确了解从它继承的每种小部件类型。这是否违反了良好 OOP 的任何原则?

【问题讨论】:

  • 类感知。考虑您提出的任何可维护的解决方案。
  • switch 语句和 OO 编程是双重概念。我强烈推荐阅读一下expression problem
  • 在JavaScript中只能使用原型继承来定义,JavaScript没有对象继承的概念。

标签: javascript oop web-application-design


【解决方案1】:
Widget.prototype.clone = function() {
  var constructor = window[this.type];
  return new constructor();
};

当然,假设您的所有子类都声明为全局变量。

但老实说,我会在 Widget 命名空间中删除那些子类,并通过它访问它们,而不是让所有内容都全局化。

Widget.A = function(){};
Widget.A.prototype = new Widget();

Widget.prototype.clone = function() {
  var constructor = Widget[this.type];
  return new constructor();
};

【讨论】:

  • 是的。但是咖啡脚本很好地解决了它。 jashkenas.github.com/coffee-script/#classes
  • 实际上,JavaScript 的/ECMAScript 的原型继承比经典继承具有更强的表现力——只是有点令人费解。
  • @Squeegy 他们不是全局变量,但我知道你要去哪里,我喜欢它。谢谢。
【解决方案2】:

鉴于您的构造函数是全局的,您可以执行以下操作:

var global = this;

Widget.prototype.clone = function() {
  if (global[this.type])
    return new global[this.type]();
};

假设每个实例都有一个 type 属性,其值为构造函数的名称。或者您可以修复构造函数原型的构造函数属性并执行以下操作:

Widget.prototype.clone = function() {
    return new this.constructor();
};

function A() { };
A.prototype = new Widget();
A.prototype.constructor = A;

var a = new A();
var aa = a.clone();

但是,这假设您没有要传递的任何参数。如果您确实有要传递的参数,那么您可能必须知道您正在制作哪种类型,因此无论如何都可以调用正确的构造函数。

【讨论】:

    【解决方案3】:

    如果支持 ECMA5:

    • 使用Object.create(Object.getPrototypeOf(this));

    如果不支持 ECMA5:

    • 创建匿名函数
    • 设置匿名函数原型为非标准属性this.__proto__

    例子:

    var Widget = function() { };
    
    Widget.prototype.clone = function() {
      /*
       Non-ECMA5: 
    
       var newClone = function() {};
       newClone.prototype = this.__proto__;
       return new newClone(); 
      */
    
      // ECMA5
      return Object.create(Object.getPrototypeOf(this));
    }
    
    
    var A = function() { };
    A.prototype = new Widget();
    A.prototype.name = "I'm an A";
    
    var B = function() { };
    B.prototype = new Widget();
    B.prototype.name = "I'm a B";
    
    var x1 = new A();
    var y1 = x1.clone();
    
    console.log("y1 should be A: %s", y1.name);
    
    var x2 = new B();
    var y2 = x2.clone();
    
    console.log("y2 should be B: %s", y2.name);
    

    【讨论】:

    • 并非所有不支持 ES5 的浏览器都支持proto。另请注意,在 JavaScript 中,proto 已弃用,因此请不要使用它。
    【解决方案4】:

    您需要的信息已在constructor 属性中提供。但是,覆盖prototype 会丢失它,因为我最近explained here

    使用我自己的 ECMAScript 类实现 version 3version 5,您的示例将如下所示:

    var Widget = Class.extend({
        someFunction : function() {
            alert('someFunction executed');
        },
    
        clone : function() {
            return new this.constructor;
        }
    });
    
    var A = Widget.extend();
    
    var B = Widget.extend({
        constructor : function(arg) {
            Widget.call(this); // call parent constructor
            this.arg = arg;
        },
    
        // override someFunction()
        someFunction : function() {
            alert('someFunction executed, arg is ' + this.arg)
        },
    
        // clone() needs to be overriden as well:
        // Widget's clone() doesn't know how to deal with constructor arguments
        clone : function() {
            return new this.constructor(this.arg);
        }
    });
    
    var a = new A;
    var a2 = a.clone();
    a2.someFunction();
    
    var b = new B(42);
    var b2 = b.clone();
    b2.someFunction();
    

    【讨论】:

    • 这里有很多东西要学。我什至不知道有一个构造函数属性。无论如何,你的类实现看起来很方便。这个周末我会看看它。谢谢。
    猜你喜欢
    • 1970-01-01
    • 2013-02-09
    • 2013-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-16
    • 2016-06-25
    相关资源
    最近更新 更多