| 
 "Классический" ООП в JavaScript Несколько раз начинал писать этот пост, но в итоге решил не распинаться, а спросить коротко и ясно. Используете ли вы эмуляцию "классического" ООП (с классами и наследованием на классах) в своих проектах, или пользуетесь конструкторами и наследованием на прототипах? Или вообще не пользуетесь наследованием? | 
| 
 Зависит от используемого фреймворка. В данный момент в основном Ext, соответственно и наследование через Ext.extend. Если что-то делается без фреймворка, то, как правило, и наследование не особо нужно. | 
| 
 Kolyaj, Другими словами, если бы ты решал задачу уровня ExtJS, но без фреймворка, то реализовал бы классическую модель ООП? | 
| 
 Цитата: 
 Цитата: 
 | 
| 
 Чтобы более точно представить картину, надо понять, что, если говорить о разнице в подходах, то нужно говорить больше не о "класс vs. прототип" и даже не о "наследование vs. делегация", а в большей мере о "статика vs. динамика". Я практически всегда для примера привожу классовую модель Python'a, чтобы показать, что разницы между "динамическим ООП на классах" и "ООП на прототипах" практически нет (если нужно будет привести примеры и показать сходство, скажите). Что же касается статической классовой ООП модели (aka Java или C++), то ее просто физически нельзя сделать в JS (даже эмуляцию). Во сколько бы обертор мы не обернули бы конструкторы, внутри все равно будет prototype-base inheritance. Единственное, для чего можно использовать обертки - это улучшение code reuse - вынос повторяющегося блока наследования (обеспечение связки прототипов) в обертку. Как потом эту обертку называют - "Class", "Extend", "Inherit" и т.д. - не суть важно, главное, чтобы человек понимал, что он делает. Другой вопрос, когда начинается непонимание технологии ввиду того, что человек не имеет достаточного объема информации и может доказывать то, что к JS не имеет никакого отношения. Часто причиной этому могут быть статьи с громкими заголовками типа "Наследование на классах в JS", которые подсознательно могут откладываться в умах людей, который пока поверхностно разбираются в JS, как "доведем до ума этот бесклассовый недоязык". Это уже неправильно. А в целом - никто не ограничивает делать обертки (для улучшения code reuse'a) и называть как вздумается. У меня в одном проекте был виджет-фреймворк написан, где было построено наследование виджетов (aka Widget -> WidgetPanel -> WidgetWindow -> WidgetDialog и т.д.). Для чего я использовал там обертку? - Чтобы вынести в нее блок кода, обеспечивающий связку прототипов. Все. Объективно, это единственная причина, т.к. вся эта мишура с оберткой, могла бы быть описана и без нее - и без потери какой-либо читабельности и функциональности. Но мне было удобней с обреткой. Поэтому, если анализировать различия, то, повторю, в большей мере надо говорить о "статика vs. динамика"; пара же "прототип vs. класс" очень больших отличий не покажет. | 
| 
 Kolyaj, Цитата: 
 Цитата: 
 | 
| 
 Цитата: 
 | 
| 
 Цитата: 
 | 
| 
 Dmitry A. Soshnikov, ну я в рамках предложенного определения Цитата: 
 | 
| 
 Dmitry A. Soshnikov, Спорить по вопросу статика vs. динамика в рамках JavaScript, как я понимаю, бессмысленно. Поэтому мы говорим только о динамической модели. Ясно, что реализация динамического классового ООП на JavaScript это то же прототипное ООП, вид сбоку. Практически все фреймворки стремятся к динамической классовой модели, причем это не только обертка для перелинковки прототипов, но и установка дополнительных свойств и фабрика конструкторов. Насколько это оправданно? И насколько сложнее реализация/поддержка/рефакторинг без такой обертки? Kolyaj, Цитата: 
 Просто я не видел пример "элегантного" наследования на прототипах, которое бы сохраняло функциональность конструктора родительского класса, выстраивала цепочку прототипов (для instanceOf) и правильно линковала constructor. И чтоб она была без прослойки. | 
| 
 Цитата: 
 | 
| 
 Цитата: 
 Ну вот найди сильные отличия: 
# python
class A(object):
  def __init__(self):
    self.a = 10
// аналог на JS
function A() {
  this.a = 10
}
x = A() # py
var x = new A(); // js
x.a # 10, py
x.a // 10, js
A.b = 20 # добавим новое свойство в класс, доступное всем инстансам
A.prototype.b = 20; // аналог на js
x.b # 20, py
x.b // 20, js
// Однако, свойство "b" не является родным для объектов "x"
// сделаем его родным
x.b = 30 # py
x.b = 30 // js
x.b # 30, py
x.b // 30, js
// Удалим родное свойство "b"
del x.b # py
delete x.b // js
// Снова, за счет делегации к классу (к цепи классов; в Питоне)
// и к прототипу (к цепи прототипов; в JS)
// доступно свойство "b"
x.b # 20, py
x.b // 20, js
// Более того, можно расширять класс / прототип через инстанс:
x.__class__.c = 30 # py
x.__proto__.c = 30 // js (__proto__ - спечифично для конкретной реализации, но - не суть)
x.c # 30, py
x.c // 30, js
// Наследование
# py
class B(A):
  def __init__(self):
    A.__init__(self)
// js
function B() {
  B.superclass.apply(this, arguments);
}
// блок, для вынесения в обертку (для улучшения code reuse, чтобы каждый раз не повторять одно и то же)
// одна из реализаций
var __inheritance = function () {};
__inheritance.prototype = A.prototype;
B.prototype = new __inheritance();
B.prototype.constructor = B;
B.superclass = A;
// при этом, конструктор может издохнуть после порождения
// объекта, и объект за счет своей (неявной) ссылки на прототип
// будет иметь доступ к прототипу
// аналогичная картина и Python'e:
// сслыка на класс - "А" - может издохнуть,
// но порожденный объект все еще будет иметь
// доступ к классу за счет ссылки на класс, включая явную - __class__
// примеры не привожу, здесь понятно.
Цитата: 
 | 
| 
 Dmitry A. Soshnikov, Спасибо за разъяснения. Получается, что простая реализация наследования реализуется с избыточным кодом: 
var __inheritance = function () {};
__inheritance.prototype = A.prototype;
B.prototype = new __inheritance();
B.prototype.constructor = B;
B.superclass = A;
Который рациональнее прятать в отдельную функцию. Получается, что это скорее "стандартный прием", чем простое упрощение. Цитата: 
 Фреймворки и убирают этот момент, делая динамические классы: 
var myClass = new Class({
    "extend": myParentClass,
    "constructor": function() { /* "конструктор" */ },
    "property": "value"
});
Я и хотел узнать, насколько такой подход необходим и насколько считается "плохим тоном", говоря о JavaScript. Получается, что такой вариант помогает убрать избыточность, которую порождает обычный подход конструктор-прототип. Хоть я и разобрался в наследовании на прототипах, но думал, что что-то упустил. Потому что не понимал, зачем во фреймворках используется "эмуляция классического ООП", а именно - вид динамического ООП на классах. | 
| 
 Цитата: 
 Касательно же обертки - никакого плохого тона. Иначе "плохим тоном" можно было бы назвать и это: 
for (var k = 0; k < 3; k++) {
  alert(k);
}
У нас есть повторяющийся кусок кода, мы его выносим в цикл. Можно было бы привести пример - выносим в функцию - не суть, то же самое. Иначе: alert(0); alert(1); alert(2); Это, стало быть, не "плохой тон", т.к. мы не используем обертку для повторяющегося куска кода. Ну, да, code reuse методом copy-paste - очень "красивый" метод ;) | 
| 
 Цитата: 
 | 
| 
 Zeroglif, Там ничего против, естественно, не происходит. Я просто хотел узнать насколько необходимо использование оберток в реализации наследования. | 
| 
 Цитата: 
 | 
| 
 Вставлю свои "пять копеек"... Основная суть данной темы - не красивость или какая-то верность принципам классического ООП. Просто классовое наследование может существенно облегчить жизнь при реализации сложных проектов. Что я понимаю под классовым наследованием? 1. Возможность четкого определения принадлежности объекта классу (instanceOf) 2. Возможность вызова конструктора или любого метода базового класса Это то, что хочется получить. Очевидно, что в JavaScript без "обёрток" не обойтись. Ну, а чем "элегантнее" и проще это будет сделано, тем лучше. Кстати, интересно будет узнать ваше мнение, коллеги, по поводу моей "обёртки" (Классическое наследование в JavaScript). Буду благодарен за любые отзывы :yes: | 
| 
 Цитата: 
 | 
| 
 Цитата: 
 
var DerivedClass = function() {
    arguments.callee.$super.call(this);
}.$extends( BaseClass ).$class( function ($super) {
    this.someMethod = function () {
        return $super.someMethod.call(this);
    }
});
Что тут сложного? Вопрос серьёзный, т.к. мне интерестно узнать мнения разных людей. | 
| 
 Благополучно забыв, что в вашей статье описывается (т.е. читая вышеприведеный код без знания вашего механизма наследования), я не понимаю, что делают функции $class и самая первая. Во-вторых, определение функций в конструкторе а-ля this.someMethod = function() {} создает для каждого объекта свой набор методов, вместо ссылок на прототип. | 
| 
 Цитата: 
 Первая функция - конструктор класса DerivedClass (исправил название класса в предыдущем посте). Функция $class() - нужна для "финальной сборки" класса. Об этом, правда, подробнее можно узнать из статьи. Нет смысла здесь дублировать. Что касается "определение функций в конструкторе а-ля this.someMethod" - эту проблему тоже решает функция $class(). Функция someMethod создается только один раз для всего класса. P.S. Спасибо за отзыв :yes: | 
| 
 Вы не поняли, я не прошу объяснять код, а говорю, что если мне придется читать подобный код, уйдет слишком много времени на осмысливание обертки. Лично у меня сейчас модифицированный способ из Prototype, в Ext'е похожий по использованию. 
var A = Class.create({
    init: function() {
    },
    method1: function() {
    },
    method2: function() {
    }
});
var B = Class.extend(A, {
    init: function() {
    },
    method3: function() {
    },
    method4: function() {
    }
})
 | 
| 
 Первое, что мне лично не нравится - Prototype (как и многие другие фрэймворки) создаёт "наследование" путём копирования свойств и методов из одного класса в другой. Полагаю instanceOf тут не работает? Второе - а как насчет вызова конструктора и методов базового класса? Вот это на самом деле и есть главная задача. По крайней мере, моя. | 
| 
 Цитата: 
 Цитата: 
 
var B = Class.extend(A, {
    init: function() {
        B.superclass.init.apply(this, arguments);
    }
});
 | 
| 
 Цитата: 
 Ну, посмотрим в Prototype (version 1.6.0.3, метод Class.create()): 
...
for (var i = 0; i < properties.length; i++)
      klass.addMethods(properties[i]);
...
Хотя instanceof, надо признать, всё же работает (для вашего примера). | 
| 
 Это не наследование реализуется, а новые методы навешиваются. Наследование там стандартное klass.prototype = new subclass; Впрочем сейчас в Prototype слишком много всего непонятно для чего. | 
| 
 Цитата: 
 | 
| 
 Pavel_Volodko, Сходу скачал только финальный вариант, сразу возник вопрос - почему передаются функции, а не заранее созданный объект-прототип? В смысле, в чём фишка? ;) upd а, идею функций понял, нужен верный $className... upd2 и не хочется копировать... | 
| 
 А чем ваш способ this.method = function() {} отличается от приведенного прототайповского? (ну кроме того, что в прототайпе лучше тем, что вешается все на prototype) | 
| 
 Pavel_Volodko, И мелкий же шрифт исходников у Вас в блоге, чуть глаза не сломал :/ | 
| 
 Цитата: 
 p.s. эмулировать классическое наследование не сложнее чем использовать наследование, основанное на прототипах? Т.е. естественный вариант должен быть проще, по идее | 
| 
 Цитата: 
 Цитата: 
 Цитата: 
 | 
| 
 Цитата: 
 | 
| 
 Цитата: 
 Фишка действительно в использовании функции вместо объекта-прототипа. Хотя, на самом деле, всё гениальное просто: для создания связной иерархии классов нужно цеплять прототипы к функциям-конструкторам. Имея только объект-прототип, мы такой возможности не имеем. Приходится извращаться со всякими __proto__, копированием методов или созданием фэйковых подклассов. А вот имея функцию-конструктор для создания объекта-прототипа всё делается элементарно. upd копировать и не обязательно. Я сам очень настороженно отношусь к решениям, которым мне не до конца понятны. Я просто поделился тем, что сделал под себя. Just FYI, как говорится. Цитата: 
 На мой взгляд, достаточно заглянуть в метод Class.create() от Prototype, чтобы задаться кучей вопросов. Кроме того, выражение "вешается все на prototype" мне кажется странным (хотя, думаю, я понимаю что вы имеете ввиду) и уж тем более я не считаю это лучшим решением. | 
| 
 Цитата: 
 Цитата: 
 | 
| 
 Цитата: 
 Цитата: 
 Насчет "естественный вариант должен быть проще, по идее" - проще не значит лучше. Я и сам за простые решения. Кстати, с этой точки зрения, моё решение абсолютно естественное, т.к. построено на элементарных возможностях прототипного наследования. | 
| 
 Цитата: 
 По поводу instanceоf - по-подробнее плиз :stop: Что касается "цепляться за структуру, сохраняя суперы" - только там где это нужно, естественно. Цитата: 
 | 
| 
 Цитата: 
 
function inheritObject(object) { 
  function Dummy(){}; 
  Dummy.prototype = object; 
  return new Dummy(); 
}
Одновременно вместо Dummy для "подклассов" используется заранее подготовленная и заложенная в функцию структура. То есть, получив правильно прилинкованный объект-прототип, вы вместо: Person.prototype.x =//... Person.prototype.y =//... используете: this.x = //... this.y = //... что, в принципе, почти то же самое кроме доступа к локальным переменным функции из методов. Отсюда 2 вывода - то ли вам важны локальные переменные ($className), то ли вам не нравится "обвешивать" объект-прототип руками или через for-in... | 
| 
 Цитата: 
 | 
| Часовой пояс GMT +3, время: 15:00. |