Как стать автором
Обновить

Комментарии 51

Здорово написано. Спасибо! :)
Статья отличная, я как раз вникаю в глубины Javascript и это многое прояснило! Но вот «bisy» просто режет глаз…
Однозначно в закладки, спасибо!
Статья хорошая: ясное изложение, понятные примеры, удачные сравнения. Однозначный плюс.
От себя хотел бы посоветовать выносить константы или константные объекты (как в данном случае defaultOptions) за пределы функции (Timer), желательно перед ней, обернув всё это в анонимную функцию:

(function(){
	var defaultOptions = { /*константы*/ };
	window.Timer = function Timer( options ){
		//...
		//здесь есть обращение к defaultOptions
		//...
	}
	//...
})();

Такой подход хорош тем, что:
1) когда вы захотите подправить значения констант, поиск по коду defaultOptions упрощается;
2) объект не создаётся каждый раз и становится доступным для других функций, например, для какой-нибудь changeTimerOptions;
3) появляется возможность динамически менять опции таймера по умолчанию.
Ещё раз, спасибо.
Спасибо, учту.
С прототипами, учитывая уровень статьи, мне кажется, у вас не очень понятно. Для уверенности дал прочитать эту часть двум знакомым начинающим front-end-рам — они согласились.

Я, помогая в понимании JS, всегда даю читать вот это — лучшего мануала по прототипам и наследованию не встречал.

А статья написано просто замечательно. У вас, должно быть, гуманитарное образование, или явный талант к изложению.
ну вот, напишешь хорошую статью — обзовут гумном
>лучшего мануала по прототипам и наследованию не встречал.
не советовал бы я читать это, так обычно появляются клоуны, которые потом везде кричат что вы все делаете неправильное ООП в жаваскрипте и есть только уан тру вэй — это вот этот примитив с прототипами, но как правило эти люди не работали ни над чем сложным.
есть отличное видео по этой теме Class Inheritance and Composition Patterns in YUI
Клоуны появляются не от этого, а от попытки сделать полноценное ООП там, где его не было задумано. С каких это пор фундаментальные знания по языку стали губительными?

Понимать и реализовывать парадигмы так, как хочется/привыкли — личное дело каждого. Нет никаких «тру-вейев» — все ограничивается фантазией.

Но не зная основ прототипного наследования, никогда не напишешь Y.object() или Y.extend() — верно?
>Клоуны появляются не от этого
Судя по общению с людьми, появляются именно от таких статей, в которых показывают что такое прототипы, самые базовые конструкции, которые можно с их помощью получить и произносятся слова вроде «Или имитацию «классического» ООП. Ещё чаще это выглядит надругательством над возможностями JS, которое, я уверен, даёт о себе знать с усложнением проектов.»

>Но не зная основ прототипного наследования, никогда не напишешь Y.object() или Y.extend() — верно?
верно, но на звание лучшего мануала по наследованию та статья как-то совсем не тянет.
Прекрасно! Вы настаиваете на ошибочной теории и поливаете грязью тех, кто старается вернуть новичков к правильному пути. Создание методов в конструкторе — это ересь. Она нарушает идеологию ооп в общем и идеологию JS в частности. В конструкторе должно происходить конструирование экземпляра (да, такая уж тафтология), а не всего класса, как у вас. Т.Е. очень грубо говоря — чем этот экземпляр отличается от класса в целом. А у подхода с перегруженным конструктором — всё смешано в кучу. Самое отвратительное, что люди даже не понимают логики такого подхода. Посмотрите то же самое на php и почувствуйте всю отвратительность:

<?

class Timer {

  function __construct ($options) {
    $defaultOptions = array(
        'delay' => 20,
        'stopFrame' => 0,
        'loop' => true
    );
    foreach ($defaultOptions as $option => $values) $this[$option] = $option ? ($options[$option] || $defaultOptions[$option]) : $defaultOptions[$option];


    $this->start = function() {
      // code
    };
    $this->setActiveFrameElement = function( frameNumber ){
      // code
    };
    $this->toString = function() {
      // code
    }
    $this->setTask = function( new_task ) {
      // code
    }
    $this->getKeyFrames = function(){
        return $keyFrames;
    }
    $this->getTask = function(){
        return $task;
    }
  }

}

Так почему на php такое никто не напишет, а над JS все издеваются как хотят?
WUT? :)

>Создание методов в конструкторе — это ересь
Я где-то говорил про создание методов в конструкторе?

>В конструкторе должно происходить конструирование экземпляра ..., а не всего класса, как у вас.
У вас замечательные способности читать чужие мысли, но спешу вас разочаровать, ваша способность иногда обманывает вас :)

Еслиб вы потрудились взглянуть на видео, которое я привёл первым комментарием, может тогда хоть немного дошло то о чём я говорил. Я же не говорю что прототипы — это зло, я просто говорю что в сложном проекте недостаточно Object.create
К сожалению, у меня закрыт доступ к ТыТрубе сейчас.
Я так понял, что вы поддерживаете позицию автора на счёт использовния перегружённого конструктора. Если ошибся, то поясните свою позицию лично, пожалуйста, без видео. Можно в двух словах.
Моя позиция в том что я заметил как вокруг бегает много интересных людей, которые узнали про Object.create и когда они видят нечто вроде YUI Base, то начинают говорить о том какая это Java и все кто это используют — полные идиоты и ничего не понимают в жаваскрипте.

Благо разработчики YUI отлично понимают плюсы и недостатки разных подходов и используют наиболее подходящие инструменты для решения различных задач.
Y.Object() // Object.create
Y.extend()
Y.augment()
Plugins
Class Extensions
Я пока только учу JS, но такая запись
this[option] = options? (options[option] || defaultOptions[option]): defaultOptions[option];
не позволит установить значения опций как false, 0, '' (пустая строка).
Вообще, немного «корявый» способ, лучше вынести эту логику в отдельную функцию:

var extend = function(to, from) {
	for (var key in from)
		if (from.hasOwnProperty(key))
			to[key] = from[key];
	return to;
};

//........................................................................

var options = {
    foo: 1,
    bar: 2
}

// Default options
var params = extend({
	foo: 0
}, options);


params.foo //1
params.bar //2


//public
var defaultOptions



Давайте лучше рассмотрим пример:

var Foo = new function() {
	//private
	var private = 1;

	var Foo = function() {
		//...
	};

	//public
	Foo.prototype = {
		constructor: Foo,
		foo: 1,
		bar: function(param) {
			return this.foo + param + private;
		}
	};

	return Foo;
};

var object = new Foo;

object.foo;    // 1
object.bar(1); // 3

Относительно наследования:

var Extend = function(child, parent) {
    for (var key in parent) {
        if (parent.hasOwnProperty(key))
            child[key] = parent[key];
    }

    var __new__ = function() {
        this.constructor = child;
    }

    __new__.prototype = parent.prototype;
    child.prototype = new __new__();
    child.__super__ = parent.prototype;

    return child;
};

function A(value) {
   this.set = value;
};

A.prototype.get = function() {
    return this.set;
};
 
function B(value) {
    this.set = value;
};

Extend(B, A);

alert(new A(1).get()); // 1
alert(new B(2).get()); // 2

function C(value) {
    this.set = value;
};

C.prototype.get = function(value) {
    return B.__super__.get.call(this, arguments);
};

alert(new C(3).get()); // 3
Верно, ошибка моя.
Спасибо, что заметили.
исправил на
for(var option in defaultOptions) this[option] = options && options[option]!==undefined ? options[option] : defaultOptions[option];
Отличная статья! Отдельное спасибо за юмор :)
Ужасная статья! Здесь показано, как нельзя программировать. Во-первых, нарушается SRP. Во-вторых, неправильно используется наследование (впрочем, я вообще за то, чтобы избегать наследования как такового), хотя тут же можно отослать и к SRP. В-третьих, нарушается инкапсуляция на каждом шагу (например, что произойдёт, если таймер уже работает, а у него поменяют свойства stopFrame, или что если написать timer.getTask()[0] = foo). В-четвёртых, странноватые имена для некоторых сущностей (например, task там, где логичнее было бы tasks или schedule).
Статья учебная, пример в ней — тоже.
То о чем вы говорите, решается на стадии отладки. И еще миллион возможных багов…
Проблемы, о которых я говорю, не решаются на стадии отладки. Они решаются на стадии рефакторинга. Вот только рефакторинг может быть разным. Где-то намеренно оставляют технический долг, потому что здесь и сейчас времени нет. А где-то люди по незнанию городят вот такое. Если бы сразу не городили, было бы лучше. Если уж показывать, как реализовано ООП в javascript, то надо заодно и писать о том, где его уместно использовать и как это правильно делается.
Спасибо за труд. Только пример наследования и объяснение прототипов я бы делал без extend.

Например, опишем родителя:

/**
* constructor
*/
var Foo = function() {
/** type {number} */
this.param = 1;
}

/**
* return {number}
*/
Foo.prototype.getParam = function() {
return this.param;
}

Функция, реализующая наследование:

/**
* Реализует прототипное наслдование.
* Взял из Google Closure Library.
* @param {Function} childCtor Конструктор потомка
* @param {Function} parentCtor Конструктор родителя
*/
var inherits = function(childCtor, parentCtor) {
/**
* Создаем пустой конструктор
* constructor
*/
function tempCtor() {};
// Складываем прототип родителя в прототип временного
// конструктора
tempCtor.prototype = parentCtor.prototype;
// В superClass кладем ссылку на прототип родителя.
// Нужно чтобы вызывать методы родителя.
childCtor.superClass = parentCtor.prototype;
// В прототип ребенка кладем экземпляр пустого
// конструтора. Не забываем, что все методы родитля
// лежат в его прототипе, а значит передадутся
// ребенку
childCtor.prototype = new tempCtor();
// Запоминаем конструктор
childCtor.prototype.constructor = childCtor;
};

Теперь ребенка:

/**
* constructor
* @extends
*/
var Bar = function() {
// в superClass лежит ссылка на прототип родителя,
Foo.call(this);

/** type {number} */
this.otherParam = 2;
}
// Наследуемся от Foo, код наследования чуть выше.
inherits(Bar, Foo);

/**
* return {number}
*/
Bar.prototype.getOtherParam = function() {
return this.otherParam;
}

А теперь посмотрим, что получилось:

var foo = new Foo();
var fooParam = foo.getParam(); // 1

var bar = new Bar();
var barParam = bar.getParam(); // 1
var barOtherParam = bar.getOtherParam(); // 2

Объявлять методы внутри других методов или конструтора с помощью

this.myMethod = function() {/*… */};

не очень хорошо, так как они будут во всех экземплярах создаваться заново, а не ссылаться на функцию в прототипе. По возможности этого надо избегать.

Я понимаю, подход плох тем, что неудобно скрывать методы, делать их по настоящему приватными. Я в коде использую соглашеие, что приватные методы начинаются с _. Спасает только то, что на боевом сервере весь код выполняется в замыкании. Зато не нужно перекладывать метода из родителя в ребянка for..in
Простите, парсер лох. Пойду вырву себе руки
НЛО прилетело и опубликовало эту надпись здесь
даешь код из хабраюзеров!)
Убедительная просьба к автору этой статьи и к другим авторам статей по JS: используйте правильную терминологию. Не вводите новичков в заблуждение. new — это оператор, а не директива. timer — это экземпляр конструктора Timer, а не объект класса (хорошо что хоть не типа) Timer.
Ну и самый важный момент: настоящее ООП в JS есть! После этих строк читать дальше желание отпало.

Не хочу быть занудой, но видеть такое почти в каждой статье уже порядком надоело.
Во, я тоже после пассажа про «настоящее ООП» читать перестал. Раздражает вот это непреодолимое желание сделать себе велосипедные заборы и коровники java-style в яваскрипте.
НЛО прилетело и опубликовало эту надпись здесь
Не хотите быть занудой — не нудите.
Надоело не в каждой статье про «настоящее ООП» читать, а камменты к этим статьям в вашем стиле.
Моё чувство прекрасного не позволило промолчать.
Хорошо, new — это оператор, но в данном случае можно воспринимать и как директиву, предписывающую функции выполниться особым образом, это улучшает понимание.
timer — не объект класса?
Конечно. Я ведь несколько раз указал условность понятия «класс», даже в заголовке.
На мой взгляд, такие «безупречные» словесные конструкции как «экземпляр конструктора» напрочь отбивают понимание сути. Уж извините, писал, как считал более понятным.

С интересом буду ожидать и вашу статью на тему «ООП в JS есть»…
Понимание — вещь относительная.
Мне, к примеру, понятнее будет знать, что оператор new вызывает функцию, а не предписывает ей что-то. Яркий тому пример:
new functionName;

Скобки необязательны так как оператор сам всё сделает.

По поводу термина «Класс». Если вам удобнее использовать этот термин — никто не против, но необходимо явно оговориться что вы понимаете под этим, что бы дальнейшее чтение никого не сбивало с толку. Необходимо — потому что сам термин пришел из других языков. Кавычки каждый может понять по-своему.

«Экземпляр конструктора» — это и есть суть. Если вы считаете такое определение непонятным — расскажите о нем более развернуто, а не ищите ему замену.

Писать ещё одну статью про ООП в JS не вижу смысла, уже всё написано.
Статья очень плохая. Просто сборник дурных практик.
Я понимаю, мы 10 лет назад долго писали такой пи**ец-код, пока не разобрались — просто потому что нормальной документации по теме вообще не было.
Но сейчас-то?

Почитайте MDN, загляните в реализацию наследования в node.js и в Google Closure Library.
Не городите ужасов, пожалуйста.
Пожелание к 3-м последним комментаторам:

С интересом буду ожидать ВАШУ статью на тему "ООП в JS есть!"…
Да, пожалуй, придется )))

Денис, без обид, но Вы действительно не слишком хорошо разобрались в теме.

А здесь очень много новичков, которые этого просто не видят.
И мало JS-разработчиков с серьезным опытом в IT за пределами создания прикольной анимации и плагинов к jQuery. Таких вообще мало, к сожалению.

Так что не принимайте восторженные отзывы слишком серьезно.
Лучше поговорите с кем-нибудь, кто в теме давно, всерьез и надолго.
Я и не обижаюсь. И на безупречное знание темы не претендую.
А Вы, в теме давно? Вам и слово.

По сути то Вы правы: есть ООП! Потому, что в первую очередь оно есть… в душе программиста, а значит и везде где он пожелает ему следовать. Об этом же и статья.
А вот в спецификации нет.

Но душа то нам важнее. ;-)
А вот в спецификации нет.

Как это нет?

Each constructor is a function that has a property named “prototype” that is used to implement prototype-based inheritance and shared properties. Objects are created by using constructors in new expressions...

4.2.1 Objects
Похоже, для Вас это вопрос веры, а не точности тех. реализации.
В священные войны я не вступаю. :)
Вы не просто вступаете в священные войны. Вы их развязываете!
Так это видят только те, кто хочет воевать. :)
То есть вы хотите воевать и потому вам кажется, что monolithed холиварит? Интересная логика.
Не я, вы так видите, ведь вы это утверждаете:
Вы не просто вступаете в священные войны. Вы их развязываете!
Денис, ну что за детский сад: «С интересом буду ожидать ВАШУ статью на тему «ООП в JS есть!»»? Не обязательно быть поваром, чтоб понять, что блюдо — не вкусное. Это критика, причем обоснованная, не нужно скатываться до «сперва добейся сам».
На момент публикации статьи очень не кстати выяснилось, что на сайте denis-or-love.narod.ru где пример, засел хулиганский js скрипт.
Скрипт я «выпилил», но плашка с предупреждением от яндекс пока осталась.
Ужасная статья, к третьему абзацу понятно, что ООП в JavaScript мне не нужен и лучше пойти какао попить и не тратить время. Очень много воды, вернее весь текст — вода с кусочками кода, чтобы показать что статья техническая.

Настоящего ООП в JS нет! Как выше заметили местами код ужасен.

Писать свою статью не буду, ибо логика сначала сам сделай, а потом критикуй не для меня :)
Разумеется.
Критикуют одни, а созидают другие. :)

Статья написана не ради плюсиков или пустого тролле-трепа, а ради реальной пользы, которую она уже принесла и продолжает нести.

Спасибо, что читаете.
Какую пользу она приносит? Практическую? Да, после вашей статьи появится на еще одного говнокодера больше, который не знает, что ООП в JS есть. Он будет держаться молодцом, зарабатывать деньги, но будет оставаться безграмотным, не знающим элементарнейших вещей из своей области. Не обольщайтесь хорошими оценками к посту, я так же, как и вы могу писать сатьи на тему квантовой механики, имея знания только «по верхам» и доставляя радость людям, которые в ней разбираются немногим хуже меня.
Пардон, вы уже отметились в комментариях к своей бывшей статье о том, что на ноль делить нельзя.
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории