Javascript原型继承和OOP

Javascript prototypal inheritance and OOP

本文关键字:OOP 继承 原型 Javascript      更新时间:2023-09-26

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

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

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

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

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

然而,这是假设你没有任何参数要传递。如果你有参数要传递,那么你可能必须知道你正在生成哪种类型,所以无论如何都可以调用正确的构造函数。

如果支持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);

您需要的信息已在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();