Javascript прототип наследования и ООП - PullRequest
1 голос
/ 12 ноября 2011

Я создаю приложение, которое позволяет пользователю создавать виджеты. Существует несколько различных типов виджетов, и я определил их с помощью наследования прототипов. т.е.

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

Меня беспокоит то, что базовый виджет теперь должен явно знать каждый из типов виджетов, которые наследуются от него. Это нарушает какие-либо принципы хорошего ООП?

Ответы [ 4 ]

2 голосов
/ 12 ноября 2011

Учитывая, что ваши конструкторы являются глобальными, вы можете сделать что-то вроде:

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

Однако это предполагает, что у вас нет параметров для передачи.Если у вас есть параметры для передачи, то вы, вероятно, должны знать, какой тип вы делаете, и в любом случае можете вызвать правильный конструктор.

2 голосов
/ 12 ноября 2011
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();
};
1 голос
/ 12 ноября 2011

Необходимая информация уже доступна в свойстве 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();
1 голос
/ 12 ноября 2011

Если поддерживается 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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...