Pull to refresh

Comments 19

Кстати, сейчас новые браузеры поддерживают Object.create, поэтому я последнее время стал использовать такой шаблон:
function MyClass(…) {
	…
}

MyClass.prototype = Object.extend(Object.create(MyOtherClass.prototype), {
	constructor: MyClass,
	…
});


Object.extend (нестандартный метод) просто копирует свойства. Object.create для старых браузеров сделать просто.
Онеееет, ещё одно наследование в JS…
Ну сколько можно? Почему каждый начинающий программист на JS пишет сначала своё наследование, потом свои события, потом свою систему модулей?
Вероятно потому, что именно на процессе создания велосипедов и зиждется понимание концепций?
Но зачем же это выкладывать? Да и для более глубокого понимания JS ни в уоем случае нельзя первым делом туда тащить за уши объектную модель из других языков.

Она в JS и своя неплохая: для развития стоит, наверное, попробовать думать по-другому, а не тащить с собой багаж костылей с других языков.
Мне кажется, неплохая она в первую очередь по тому, что позволяет её изменять, писать мощные фреймворки. Вот я всегда пытался писать через стандартную, и для небольших скриптов она великолепна, но при создании проектов побольше рутина и излишки байтов привели к такому варианту.
значит, вы что-то делаете не так. попробуйте ещё раз.
Спасибо, как раз искал этот коммент, но не смог быстро найти.
Приведу, что-ли, свой пример наследования. Может кому понадобится:
Object.inherit = function(Child, Parent) {
	(Child.prototype = Object.create(Child.superclass = Parent.prototype)).constructor = Child;
}
/** @constructor */
function A() {
  this.say = function() { console.log("may") }

  this.init = function() { console.log("init A instance") }
}
/** @constructor */
function B() {
  B.superclass.constructor.apply(this, arguments);//A.apply(this, arguments);

  /** @override */
  this.say = function() { console.log("gav") }

  var superInit = this.init;

  this.init = function() {
    superInit();//Parent `init` function
    console.log("init B instance")
  }
}
Object.inherit(B, A);

var b = new B;
b.init();
>> init A instance
>> init B instance
b.say();
>> gav
При таком использовании, как в примере выше, ему место в отдельном неймспейс-объекте
Если честно, то не хотелось городить отдельный namespace `Class` только ради одной функции. А до `Object` быстрее «дотянутся». Я понимаю, что в будущих версиях языка могу ввести стандартную `Object.inherit` и это поведение сломается, но я регулярно просматриваю ES.Next и пока ничего подобного там не вижу.
UFO landed and left these words here
Всё зависит от задачи. Свой View без наследования (а когда мы говорим про наследование в JS, мы имеем ввиду наследование на прототипах) написать очень сложно.
UFO landed and left these words here
Как-то недавно я смотрел сколько у меня занимает инициализация классов.
Она у меня была чуть более сложная чем у вас.
Что я могу сказать — получилось где-то 100мсек исключительно чтобы собрать хитрые классы.
Это не позволительно много.
У Вас примерно таже проблема — очень долго будут собираться классы при подключении скрипта.
Ваш код, это реальный ад :)
Зачем такие сложности? Зачем нужен stat? Почему не использовать прототипное наследование?
Совсем не ясно какие плюсы дает ваша реализиация?
С ее помощью проще создавать классы? Нет, только сложнее.
Меньше писать кода? Нет, кода получается на порядок больше чем надо.
В общем не ясно зачем все это.

В коде весьма сложно разобраться, но вот несколько моментов:
1.
  child.statConstructor.prototype.proto = function() {
      return;
  }

Можно проще
  child.statConstructor.prototype.proto = Function()

// вместо
if (parent && ("statConstructor" in parent) && parent.statConstructor && typeof (parent.statConstructor) === "function") { ... }

// достаточно
if (parent && typeof parent.statConstructor == "function") { ... }

2.
* У классов есть параметр stat, предназначенный для статических ф-ий и данных.

Статические функции и данные в динамическом языке… сильно ;)
3.
* Можно запретить наследовать метод, объявляя его без prototype.

Противоречит принципам ООП. Если метод(свойство) есть в классе, то он должен быть во всех его потомках. То что вы пытаетесь сделать — это член класса, и в этом случае добавляется в класс (его конструктор), а не в его прототип.
4.
      try {
        delete child.stat[name];
      } catch(e) {

      }

Правда ожидаете исключение? delete его не вызывает, и всегда возвращает true, даже когда свойство не было удалено. Единственный момент, что в IE6 выбрасывается исключении при попытке удалить некоторые свойства объекта window… но вы явно этого здесь не делаете, да и вряд ли ориентируeтесь на IE6.
5.
  if(child.stat.construct) {
    // Вызывается при создании класса или создании потомка без stat.construct
    child.stat.construct();
  }

  // уж лучше так
  if(typeof child.stat.construct ==  'function') {
    // Вызывается при создании класса или создании потомка без stat.construct
    child.stat.construct();
  }

6.
  arguments.callee.caller

callee и caller — не кроссбраузерны, и запрещены в strict mode. Считайте что этих свойств нет у функций, для вашего же блага.
7.
А мне хочется, чтобы все объекты, наследники класса Foo имели уникальный id и предупреждали пользователя, что умеют взрываться.
Для реализации этого — я создаю специальный метод cnstruct (constructor — уже занято), и выполняю его при создании каждого объекта. Чтобы не забыть его выполнять, отказываюсь от создания объектов через new Foo() и создаю объекты через статический метод Foo.stat.create().

Достигается подобное так
function createClass(super, ext){
  if (!super)
    super = Function();

  var superConstruct = super.prototype.cunstruct;

  var newClass = function(){
    if (typeof superConstruct == 'function')
      superConstruct.apply(this, arguments)
    if (typeof this.construct == 'function')
      this.construct.apply(this, arguments);
  }

  var tempClass = Function();
  tempClass.prototype = super.prototype;
  newClass.prototype = new tempClass();

  for (var key in ext)
    newClass.prototype[key] = ext[key];
  
  return newClass;
}

Но это все плохая идея, потому что:
  • Иногда не нужно вызывать инициализирующую часть супер класса
  • Часто нужно управлять порядком инструкций, что-то делать до вызова инициализирующей части супер класса, а что-то после
  • Класс потомок, может получать другой набор параметров в конструктор, который отличается от набора супер класса

Поэтому лучше явно вызывать construct или любой другой метод у супер класса
var Foo = function(a, b){ .. }
var Bar = function(c, a){
  // делаем что-то до

  Foo.call(this, a, 'contant value');

  // Делаем что-то после  
}
Bar.prototype = new Foo();

И это касается любого метода.
8.
child.prototype.protoFunc = child.statConstructor.prototype.protoFunc = function(callerFuncName, args, applyFuncName) {
    /*
     * Позволяет вызвать функцию более ранней версии в иерархии прототипов ...

Это вы выполняете роль прототипного наследования. На подобных вызовах вы будете терять огромное количество времени.
Правильный подход (кратко, pure js)
var Foo = function(){};
Foo.prototype = {
  method: function(){ .. }
};
var Bar = function(){};
Bar.prototype = new Foo();
Bar.prototype.method = function(){
  Foo.prototype.method.apply(this, arguments);
}

Да, немного длиннее, но на порядки быстрее.

И далее далее…

Поизучайте подобные реализации в различных популярных фреймворках и библиотеках. В большинстве случаев, их конструктор классов на самом деле упрощает конструирование и использование классов.
Вот примерно таких разгромных комментариев и ожидал. Даже обиделся, что ни кто не обвиняет по существу. Особенно ждал нападок на медленную реализацию protoFunc, но об отсутствии caller.calle в strict mode — не догадывался. В общем огромное спасибо за такой развернутый комментарий. Пошел серьезно пересматривать свои привычки.
Sign up to leave a comment.

Articles