1

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

//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();

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

//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 的任何原则?

4

4 回答 4

2
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();
};
于 2011-11-11T23:50:27.377 回答
2

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

var global = this;

Widget.prototype.clone = function() {
  if (global[this.type])
    return new global[this.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();

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

于 2011-11-12T04:04:38.970 回答
1

如果支持 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);
于 2011-11-12T00:10:30.003 回答
1

您需要的信息已在constructor酒店中提供。prototype但是,正如我最近在这里解释的那样,覆盖会丢失它。

使用我自己的 ECMAScript版本 3版本 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();
于 2011-11-12T13:27:08.250 回答