Пользователь
0,0
рейтинг
7 января 2009 в 23:56

Разработка → Работа с объектами в JavaScript: теория и практика

В этой статье я хочу по возможности полно и последовательно рассказать о том, что такое объект в JavaScript, каковы его возможности, какие взаимоотношения могут строиться между объектами и какие способы «родного» наследования из этого вытекают, как это все влияет на производительность и что вообще со всем этим делать :)

В статье НЕ будет ни слова про: эмуляцию традиционной класс-объектной парадигмы, синтаксический сахар, обертки и фреймворки.

Сложность материала будет нарастать от начала к концу статьи, так что для профи первые части могут показаться скучными и банальными, но дальше будет намного интереснее :)


Объекты в JavaScript


Во многих статьях встречается фраза «В JavaScript — всё объект». Технически это не совсем верно, однако производит должное впечатление на новичков :)

Действительно, многое в языке является объектом, и даже то, что объектом не является, может обладать некоторыми его возможностями.

Важно понимать, что слово «объект» употребляется здесь не в смысле «объект некоторого класса». Объект в JavaScript — это в первую очередь просто коллекция свойств (если кому проще, может называть это ассоциативным массивом или списком), состоящая из пар ключ-значение. Причем ключом может быть только строка (даже у элементов массива), а вот значением — любой тип данных из перечисленных ниже.


Итак, в JavaScript есть 6 базовых типов данных — это Undefined (обозначающий отсутствие значения), Null, Boolean (булев тип), String (строка), Number (число) и Object (объект).
При этом первые 5 являются примитивными типами данных, а Object — нет. Кроме того, условно можно считать, что у типа Object есть «подтипы»: массив (Array), функция (Function), регулярное выражение (RegExp) и другие.
Это несколько упрощенное описание, но на практике обычно достаточное.

Кроме того, примитивные типы String, Number и Boolean определенным образом связаны с не-примитивными «подтипами» Object: String, Number и Boolean соответственно.
Это означает, что строку 'Hello, world', например, можно создать и как примитивное значение, и как объект типа String.
Если вкратце, то это сделано для того, чтобы программист мог и в работе с примитивными значениями использовать методы и свойства, как будто это объекты. А подробнее об этом можно будет прочитать в соответствующем разделе данной статьи.

Работа по ссылке


Ссылка — это средство доступа к объекту под различными именами. Работа с любыми объектами ведется исключительно по ссылке.
Продемонстрируем это на примере:

test=function() {alert('Hello!')} //Создадим функцию {alert('Hello!')} (а функция, как мы помним, является полноправным объектом) и сделаем переменную test ссылкой на нее
test_link=test; //test_link теперь тоже ссылается на нашу функцию
test(); //Hello!
test_link(); //Hello!

* This source code was highlighted with Source Code Highlighter.

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

Наша функция, как и любой другой объект — просто область в памяти, и все ссылки на эту область абсолютно равнозначны. Более того, объект может вообще не иметь ссылок — в таком случае он называется анонимным, и может быть использован только непосредственно сразу после создания (например, передан в функцию), иначе доступ к нему получить будет невозможно и в скором времени он будет уничтожен сборщиком мусора (garbage collection), который и занимается тем, что удаляет объекты без ссылок.

Посмотрим, почему так важно это понимать:
test={prop: 'sometext'} //Создаем объект со свойством prop
test_link=test; //Создаем еще одну ссылку на этот объект

alert(test.prop); //sometext
alert(test_link.prop); //sometext

//Изменяем свойство объекта
test_link.prop='newtext';

alert(test.prop); //newtext
alert(test_link.prop); //newtext
/*Можно было бы сказать, что свойство изменилось и там и тут - но это не так.
Объект-то один. Так что свойство изменилось в нем один раз, а ссылки просто продолжают указывать туда, куда и указывают. */

//Добавляем новое свойство и удаляем старое
test.new_prop='hello';
delete test.prop;

alert(test_link.prop); //undefined - такого свойства больше нет
alert(test_link.new_prop); //hello - что и следовало ожидать

//Удаляем ссылку
delete test;
alert(test.new_prop);
/*В этом месте скрипт выкинет ошибку, потому что test уже не существует, и test.new_prop не существует тем более */
alert(test_link.new_prop); //hello
/* а вот тут все в порядке, ведь мы удалили не сам объект, а лишь ссылку на него. Теперь на наш объект указывает единственная ссылка test_link */

//Создаем новый объект
test=test_link; //Сперва снова создадим ссылку test
test_link={prop: 'sometext'} //А вот и новый объект

alert(test_link.prop); //sometext
alert(test.prop); //undefined
/* Cоздание нового объекта разрывает ссылочную связь, и теперь test и test_link указывают на разные объекты.
Фактически, это равносильно удалению ссылки test_link и созданию ее заново, но уже указывающей на другой объект */
alert(test.new_prop); //hello - теперь test содержит ссылку на наш самый первый объект

* This source code was highlighted with Source Code Highlighter.

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

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

Примитивные значения


Как я упоминал выше, типы данных String и Number могут быть как объектами, так и примитивными значениями.
obj=new String('hello'); //Создаем строку как объект
simple='hello'; //Создаем примитивное значение

alert(obj); //hello
alert(simple); //hello - пока все предсказуемо

alert(obj.length); //6 - у объекта типа String есть свойство length, хранящее длину строки
alert(simple.length); //6
/* Хотя simple - не объект, мы можем обращаться к тому же набору свойств, что и у объекта типа String. Это довольно удобно */

obj.prop='text';
simple.prop='text';

alert(obj.prop); //text - раз obj обычный объект, то мы можем запросто придать ему еще одно свойство
alert(simple.prop); //undefined - а вот simple не объект, и этот номер у нас не пройдет


* This source code was highlighted with Source Code Highlighter.

Все то же самое справедливо и для типа Number, и для Boolean (ну, кроме того, что в них нет свойства length, а есть ряд других замечательных свойств).
Использование строк и чисел как объектов не несет в себе никакой практической пользы, т.к. примитивные значения удобнее в работе, но сохраняют при этом весь необходимый функционал. Тем не менее, для полноты картины необходимо понимать этот механизм.

Не стоит путать использование примитивных значений с использованием литералов — например, независимо от того, создаем мы массив как «test=new Array()» или как «test=[]», в результате все равно будет один и тот же объект. Никаких примитивных значений мы не получим.

Создание и использование объектов


Итак, в отличии от языков, где реализована класс-объектная парадигма, нам не нужно создавать сначала класс, чтобы потом создать объект класса. Мы можем сразу создать объект, что и сделаем в следующем примере:
test={
 simple_property: 'Hello',
 object_property: {
  user_1: 'Петя',
  user_2: 'Вася'
 },
 function_property: function(user) {
  alert(this.simple_property + ', ' + this.object_property[user]);
 }
}

test.function_property('user_1'); //Hello, Петя.


* This source code was highlighted with Source Code Highlighter.

Перед нами объект test, имеющий 3 свойства, названия которых, как я надеюсь, говорят сами за себя. Больше всего нас в нем интересует свойство function_property, содержащее функцию. Такую функцию можно назвать методом объекта.

В нашей функции дважды используется ключевое слово this, которое является указателем (т.е. ссылкой) на объект, из которого вызывается функция. Таким образом, this.simple_property=test.simple_property='Hello', а this.object_property[user]=test.object_property[user]='Петя'.

Необходимо четко осознавать, this всегда указывает именно на объект, из которого вызвана функция, а не на объект, к которому она принадлежит. Хотя в данном примере это один и тот же объект, это не всегда так.
test.function_property('user_1'); //Hello, Петя.

test2=new Object(); //Еще одна форма создания нового объекта, аналогичная test2={}

test.function_property.call(test2, 'user_1'); //ошибка
/* Метод call позволяет вызвать функцию от имени другого объекта. В данном случае, мы вызываем метод function_property объекта test, и его this указывает уже не на объект test, а на объект test2. А т.к. в нем нет свойства object_property, то при попытке получить this.object_property[user]скрипт выдаст ошибку */

//попробуем исправить ситуацию
test2.simple_property='Good day';
test2.object_property=test.object_property; //В данном случае воспользуемся указанием объекта по ссылке, чтобы не дублировать код

test.function_property.call(test2, 'user_1'); //Good day, Петя.


* This source code was highlighted with Source Code Highlighter.

Из примера также должно быть видно, что нет четких этапов создания и использования объекта. Объект может быть как угодно модифицирован в любое время — до, после и даже во время использования. Это тоже важное отличие от «традиционного» ООП.

Конструктор


В примере выше мы создавали 2 объекта, обладающих некой схожестью. И там и там имелись свойства simple_property и object_property. Очевидно, что при написании реального кода также нередко встает задача создания одинаковых или просто похожих объектов. И разумеется, мы не должны каждый такой объект создавать вручную.

На помощь нам придет конструктор. Конструктор в JavaScript — это не часть класса (потому что здесь нет классов), а просто самостоятельная функция. Самая обычная функция.

make_me=function(_name) {
alert('меня запустили');
this.name=_name;
this.show_name=function() {alert(this.name);}
}

child=new make_me('Вася'); //меня запустили
/* Давайте разберемся, что здесь происходит. Интерпретатор видит оператор new и проверяет, что находится справа от него. Т.к. make_me - это функция, и она может быть использована в качестве контруктора, то создается новый объект в памяти и запускается на выполнение функция make_me, причем ее this указывает как раз на этот новый объект. Далее этому объекту добавляется свойство name, которому присваивается значение из аргумента _name, и метод show_name. Также (не знаю в какой именно момент, но это и не важно) переменная child начинает указывать на наш новенький, только что рожденный объект */

alert(child.name); //Вася
child.show_name(); //Вася

child2=new make_me('Петя');
child2.show_name(); //Петя

child2.show_name=function() {alert('Не буду говорить свое имя');} //Не забываем, что можем изменять наши объекты в любой момент
child2.show_name(); //Не буду говорить свое имя

child.show_name(); //Вася - дети никак не влияют друг на друга


* This source code was highlighted with Source Code Highlighter.

Также можно сравнить конструктора с отцом — он порождает ребенка, наделяя его определенными качествами, но сразу после создания ребенок становится полностью независим от родителя и может стать очень непохожим на своих братьев.
Если мы вспомним про описание типов данных в начале статьи, то становится понятно, что Object и его подтипы (Function, Array и другие) — это на самом деле конструкторы, придающие создаваемому объекту возможности функции, массива и т.д.

Итак, это уже намного лучше. У нас теперь есть возможность создавать объекты по некоторому образцу. Однако, не все еще хорошо. Во-первых, каждый созданный нами объект и все его свойства и методы занимают отдельное место в памяти, хотя во многом они повторяются. Во-вторых, как быть, если мы хотим сохранить связь между родителем и ребенком, и иметь возможность менять все дочерние объекты разом. На помощь нам придет прототип.

Прототип


Как у каждого ребенка есть отец и мать (хотя бы в биологическом смысле), также они есть и у каждого объекта в JavaScript. И если отец, как мы определелись, работает конструктором, то мать — это как раз прототип. Посмотрим, как это происходит:
make_me=function(_name) {
alert('меня запустили');
this.name=_name;
this.show_name=function() {alert(this.name);}
}
/*
Видя ключевое слово function, интерпретатор проверяет код справа от него, и т.к. все ок - создает новый объект в памяти, который одновременно является нашей функцией. Затем, автоматически (без участия программиста) для этой функции создается свойство prototype, ссылающееся на пустой объект. Если бы мы это делали вручную, это выглядело бы как make_me.prototype=new Object();

Затем, данному объекту (на который указывает свойство prototype) также автоматически добавляется свойство constructor, указывающее обратно на функцию. Получается такая вот циклическая ссылка.

Теперь этот объект, который можно описать как {constructor: ...здесь ссылка на фунцию...} - и есть прототип функции.
*/

alert(typeof make_me.prototype); //Object - действительно, объект
alert(typeof make_me.prototype.constructor); //Function - это наша функция
alert(make_me.prototype.constructor === make_me); //true

make_me.prototype.set_name=function(_name) {this.name=_name;} //Добавляем в прототип функции make_me новый метод

child=new make_me('Вася'); //меня запустили
/* Теперь помимо всего того, что описано в предыдущем примере, дополнительно в объекте child создается скрытое свойство [[Prototype]], которое указывает на тот же объект, что и make_me.prototype. Т.к. свойство скрыто, мы не можем ни просмотреть его значение, ни изменить его - однако оно играет важную роль в дальнейшей работе */

alert(child.name); //Вася
child.show_name(); //Вася

child.set_name('Коля');
/* Сначала, интерпретатор ищет метод set_name в объекте child. Так как его там нет, он продолжает поиск в свойстве child.[[Prototype]], находит его там и запускает. */
child.show_name(); //Коля - теперь Васю зовут Коля :)

make_me.prototype.show_name2=function() {alert('Привет, ' + this.name;} //Т.к. прототип - это обычный объект, мы точно также можем его менять на лету

child2=new make_me('Петя');
child2.show_name2(); //Привет, Петя
child.show_name2(); //Привет, Коля - изменения в прототипе влияют не только на вновь созданные объекты, но и на все старые

child2.show_name2=function() {alert('Не буду говорить свое имя');} //Мы по прежнему можем изменить сам объект, при этом новый метод show_name2 в данном объекте (и только в нем) как бы "затрет" старый метод из прототипа
child2.show_name2(); //Не буду говорить свое имя - т.к. у нас теперь есть собственный метод show_name2, то он и вызывается, и поиск в прототипе не происходит

child.show_name2(); //Привет, Коля - здесь все по прежнему

make_me.prototype={prop: 'hello'} //Попробуем пересоздать прототип заново

alert(child.prop); //undefined
child.show_name2(); //Привет, Коля
/* Если вспомнить, что такое работа по ссылке, то все понятно. Пересоздание прототипа рвет связь, и теперь свойство [[Prototype]] у объектов child и child2 указывают на один объект (который раньше был прототипом функции make_me), а свойство make_me.prototype - на другой объект, который является новым прототипом функции make_me */

child3=new make_me('Олег');
alert(child3.prop); //hello - что и следовало ожидать

* This source code was highlighted with Source Code Highlighter.

Как видно из примера, пока отец сохраняет верность матери (т.е. пока протип функции остается прежним), все дети зависят от матери и чутко реагируют на все изменения в ней. Однако, стоит только родителям развестись (конструктор меняет прототип на другой) — дети тут же разбегаются кто куда и больше связи с ними нет.

Немного о терминологии
До тех пор, пока первичная связь между конструктором и прототипом не разорвана, мы можем наблюдать следующую картину:
make_me=function(_name) {
alert('меня запустили');
this.name=_name;
this.show_name=function() {alert(this.name);}
}

make_me.prototype.set_name=function(_name) {this.name=_name;}
child=new make_me('Вася');

alert(typeof make_me.prototype); //object - у функции есть свойство prototype
alert(typeof child.prototype); //undefined - у созданного объекта НЕТ свойства prototype
alert(child.constructor.prototype === make_me.prototype); //true - зато у объекта есть свойство constructor, которое указывает на функцию-конструктор make_me, у которой, в свою очередь, есть свойство prototype

* This source code was highlighted with Source Code Highlighter.

Как я заметил после чтения многочисленных форумов на эту тему, основные проблемы возникают у людей, когда они путают свойство prototype у функции и скрытое свойство [[Prototype]] у объекта, созданного с помощью этой функции.
Оба этих свойства являются ссылкой на один и тот же объект (до тех пор, пока первичная связь прототипа с конструктором не нарушена), но это тем не менее разные свойства, с разными именами, одно из них доступно для программиста, а другое нет.

Необходимо всегда четко понимать, что если речь идет о прототипе конструктора — то это всегда свойство prototype, а если о прототипе созданного объекта — то это скрытое свойство [[Prototype]].

Наследование


Теперь мы знаем, что у каждого объекта есть скрытая ссылка на прототип, а каждый прототип — это обычный объект.
Наиболее чуткие читатели уже уловили запах рекурсии :)
Действительно, т.к. прототип — это обычный объект, то и он в свою очередь имеет ссылку на свой прототип, и так далее. Таким образом реализуется иерархия прототипов.
bird=function() {} //Это конструктор птички
bird.prototype.cry=function(){alert('Кри!');} //Птичка умеет кричать
bird.prototype.fly=function(){alert('Я лечу!');} //и летать

duck=function() {}
duck.prototype=new bird();
duck.prototype.cry=function(){alert('Кря-кря!');} //Утка кричит по другому
duck.prototype.constructor=duck; //Принудительно устанавливаем свойство prototype.constructor в duck, т.к. иначе оно будет ссылаться на bird

billy = new duck(); //Билли - это наша утка
billy.fly(); //Я лечу! - Билли может летать, потому что он птица
billy.cry(); //Кря-кря! - Билли кричит кря-кря, потому что он утка

* This source code was highlighted with Source Code Highlighter.

Так можно реализовывать иерархию любого уровня вложенности.

Задача на звездочку


Теперь, раз уж мы столько знаем обо всем этом, давайте попробуем разобраться, сколько всего происходит в этих трех строчках
make_me=function() {}
child=new make_me();
alert(child.toString()); //выводит [object]

* This source code was highlighted with Source Code Highlighter.

В первой строке мы создаем новую функцию и переменную make_me, которая указывает на эту функцию. При этом создается прототип функции, make_me.prototype, в котором содержится свойство constructor, указывающее на make_me.
Но это далеко не все :)
Т.к. функция make_me — это тоже объект, то он в свою очередь имеет папу и маму, т.е. конструктор и прототип. Его конструктор — это родная функция языка Function(), а прототип — объект, содержащий в себе методы call, apply и т.д. — именно благодаря этому прототипу мы и можем пользоваться этими методами в любой функции. Таким образом, у функции make_me появляется свойство [[Prototype]], указывающее на Function.prototype.

В свою очередь, прототип конструктора Function — тоже объект, конструктором которого является (сюрприз!) Object (т.е. Function.prototype.[[Prototype]].constructor===Object), а прототипом — объект, содержащий стандартные свойства и методы объекта, такие как toString, hasOwnProperty и другие (другими словами — Function.prototype.[[Prototype]]['hasOwnProperty'] — это как раз тот самый метод, которым мы можем пользоваться во всех производных объектах — причем это именно собственной метод данного объекта, а не наследованный). Вот таким вот интересным образом мы обнаруживаем, что все виды объектов являются производными от Object.

Можем ли мы продолжить дальше? Оказывается, нет. Object.prototype именно потому и содержит базовые свойства объекта, что не имеет собственного прототипа. Object.prototype.[[Prototype]]=null; В этом месте путешествие по цепочке прототипов в поиске свойства или метода прекращается.

Еще один интересный факт — конструктором Object является Function. Т.е. Object.[[Prototype]].constructor===Function.
Налицо еще одна циклическая ссылка — конструктор Object это Function, а конструктор Function.prototype — это Object.

Вернемся к нашему примеру. Как создается функция мы уже поняли, теперь перейдем ко второй строке. Там мы создаем объект child, конструктором которого является функция make_me, а прототипом — make_me.prototype.

Ну и в третей строчке мы видим, как интепретатор поднимается по цепочке, от child к child.[[Prototype]] (он же make_me.prototype), затем к child.[[Prototype]].[[Prototype]] (он же Object.prototype), и уже там находит метод toString, который и запускает на выполнение.

Примеси


Может показаться, что наследование через прототипы — единственный способ, возможный в JavaScript. Это не так.
Мы имеем дело с очень гибким языком, который предоставляет не столько правила, сколько возможности.

Например, при желании мы можем вообще не использовать прототипы, а программировать с помощью концепции примесей. Для этого нам пригодятся наши старые добрые друзья — конструкторы.
//Это конструктор человека
man=function() {
this.live=function(){alert('Я живу');} //Человек умеет жить
this.walk=function(){alert('Я иду');} //Человек умеет ходить
}

//Это конструктор поэта
poet=function(){
this.kill=function(){alert('Поэт убил человека');} //Поэт может убить человека
this.live=function(){alert('Я мертв');} //От этого человек умрет
}

vladimir=new man(); //Владимир - человек
vladimir.live(); //Я живу - он жив
vladimir.walk(); //Я иду - он ходит

poet.call(vladimir); //Выполняем конструктор poet для объекта vladimir
vladimir.kill(); //Поэт убил человека
vladimir.live(); //Я мертв

//А теперь фокус
man.call(vladimir);
vladimir.live(); //Я живу

* This source code was highlighted with Source Code Highlighter.


Что мы видим в данном примере? Во-первых, это возможность наследования от нескольких объектов, не находящихся в одной иерархии. В примере их 2, но может быть сколько угодно.
Во-вторых, это отсутствие какой-либо иерархии вообще. Переопределение свойств и методов определяется исключительно поряком вызова конструкторов.
В-третьих, это возможность еще более динамически менять объект, причем именно отдельный объект, а не всех потомков, как при изменении прототипа.

Upd: Замыкания и приватные свойства


Чтобы не раздувать эту и без того немаленькую статью, даю ссылку на пост Замыкания в JavaScript, где про это довольно подробно написано.

Что теперь со всем этим делать


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

Причем вопрос о цене довольно нетривиален, особенно если мы говорим о разработке под браузер Internet Explorer 6 и 7 версий.
1. Память — тут все просто. Во всех браузерах наследование на прототипах отнимает в разы меньше памяти, чем при создании методов через конструкторы. Причем, чем больше методов и свойств у нас есть, тем больше разница. Однако, стоит помнить, что если у нас не тысяча одинаковых объектов а всего лишь один, то расходы памяти в любом случае будут небольшими, т.к. здесь стоит учитывать другие факторы.
2. Процессорное время — здесь основные тонкости связанны именно с браузерами от Microsoft.
С одной стороны, объекты, где методы и свойства создаются через конструктор — могут создаваться в разы (в некоторых случаях в десятки и сотни раз) медленнее, чем через прототип. Чем больше методов — тем медленнее. Так что если у вас в IE замирает на несколько секунд во время инициализации скрипта — есть повод копать в эту сторону.

С другой стороны, собственные методы объекта (созданные через конструктор) могут выполняется немного быстрее, чем прототипные. В случае, если позарез необходимо ускорить именно выполнение какого-то метода в этом браузере, то нужно это учесть. Имейте ввиду, ускоряется именно вызов метода (т.е. поиск его в объекте), а не его выполнение. Так что если сам метод у вас выполняется секунду, то особого увеличения быстродействия вы не заметите.

В других браузерах подобных проблем наблюдается, там время создания объектов и вызова их методов примерно одинаково для обоих подходов.

P.S. Обычно в статьях подобного рода автор предлагает некую обертку, либо пытающуюся реализовать класс-объектное наследование на базе прототипного, либо просто синтаксический сахар для прототипного наследования. Я не делаю этого намеренно, т.к. считаю, что человек, понявший смысл данной статьи, способен сам для себя написать любую обертку, и еще много интересных вещей :)
Дмитрий @depp
карма
69,7
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (216)

  • +4
    > языке есть 3 типа данных, которые действительно являются полноправными объектами

    а как же RegExp, Node, Arguments..?

    > ключом, как обычно, может быть только число или строка

    нет, только строка

    но в целом — хорошо. правда про замыкания — ни слова…
    • 0
      Вообще всё, что приходит снаружи (какой-нибудь XMLHttpRequest) объктом JavaScript не является… Так что кроме JS-объектов в реальных браузерах есть и «классические» объекты… С классами и прочей атрибутикой…
      • +1
        RegExp и Arguments — самые что ни на есть родные.
    • 0
      > > языке есть 3 типа данных, которые действительно являются полноправными объектами
      > а как же RegExp, Node, Arguments..?
      Имхо, всё, что имеет свойство constructor и не является скалярным значением — полноправный объект.
      Например, DOM-элемент в IE — это не-полноправный объект =)
    • 0
      И так статья получилась огромная, а замыкания — это все-таки несколько другая тема. А вы думаете, в контексте данной статьи стоило о них упомянуть?
      • +1
        ага… с их же помощью создаются приватные поля.
        • +3
          А разве приватные поля не являются тем самым притягиванием за уши класс-объектной модели?
          Понятно, что их можно сделать, и понятно, что многие привыкли их использовать в других языках, но стоит ли этим заниматься в JavaScript, где изначально все прозрачно и открыто?
          • +1
            нет. какое отношение инкапсуляция имеет к классам? =)

            приватные поля короче в записи и не засоряют внешний интерфейс.
            • 0
              Ну, тут как бы дело в том, что в класс-ориентированных языках мы определяем приватное поле (или метод) ключевым словом private, и это — родной способ языка, предназначенный именно для этого.

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

              Нам придется либо менять везде prop на this.prop (или наоборот), либо, если мы изначально работали с приватным свойством через геттеры и сеттеры — у нас может получиться избыточность, когда в геттере берется свойство, которое теперь и так публично (типа this.get_private() {return this.old_private_now_public})

              Собственно в том и вопрос — стоит ли оно того.
              • 0
                ну да, в яваскрипте то же самое реализуется по другому — с помощью ключевого слова var =)

                и часто тебе приходится делать приватные свойства публичными? особенно при наличие акцессоров… ;-)
                • 0
                  Да не то чтобы часто, но на JavaScript не один же я пишу :) Кто его знает, что там у людей происходит в процессе разработки.

                  В общем да, пожалуй стоит об этом написать, как об одной из возможностей, но даже пока не знаю, стоит ли апдейтить данную статью или заводить новую.
                  • 0
                    ну написал же про примеси, которые нужно безконца перемешивать ;-)
                    кстати, вместо перепримешивания лучше создавать лёгкие прокси-объекты: groups.google.com/group/jfix-javascript-framework/browse_thread/thread/25e9a9420e6697b6?hl=ru
                    кстати, подключайся ;-)
    • 0
      Про типы данных исправил.
      А вот с ключами немного непонятный для меня момент:
      Если мы создадим массив ar=['test', 'test2'], то получим новый объект.
      При этом ar[0]=='test', однако ar.0 — выдает ошибку.
      Но delete ar[0] удаляет элемент массива так же, как если бы это было свойство объекта, причем ar.length при этом остается равно 2.
      Как это все можно объяснить?
      • +1
        alert([777][{toString:function(){return '0'}}]

        магия приведения типов
        • 0
          Чего-то я все равно недопонял :)
          [777][{toString:function(){return '0'}}] по идее то же самое, что [777]['0'].
          Причем и [777]['0'] и [777][0] дают один и тот же результат.

          Т.е. вы хотите сказать, что у массива ключи — это строки на самом деле? И [777][0] как бы преобразуется в [777]['0'] на лету?
          • +3
            ага
            for( var key in [1] ) alert( typeof key );
            • 0
              Ух, шайтан )
              Интересно, зачем это так реализовано.
              Сейчас исправлю этот момент в статье, спасибо за подсказку.
              • +1
                сам удивляюсь. чем больше узнаю яваскрипт, тем больше убеждаюсь, что придумал его шизофреник…
                • 0
                  Причем брат того, кто придумал Перл…
                • –4
                  сам удивляюсь. чем больше узнаю яваскрипт, тем больше убеждаюсь, что придумал его шизофреник…
                  Вывод неверный. JavaScript никто не придумывал! Он постепенно создавался путём добавления средств, которых больше всего не хватало программистам, пишущим интерпретатор. Причём они добавлялись туда, куда было проще всего их добавить программистам. Тот же путь — у языков Perl (ужас), PHP (ужас, ужас, ужас), etc…
              • –3
                Вы неправильный вопрос задаёте: не «зачем», а «почему»! Никто не придумывал JavaScript! Он делался в большой спешке и бо́льшая часть маразма в нём возникла не потому что его кто-то придумал, а потому что «так получилось»… Представьте что вам нужно сделать интерпретатор простенького скриптового язычка, у вас очень мало времени, о будущем вам думать некогда — и вы всё поймёте.

                Ну проще просто язык реализовывать если «не заморачиваться» и всё в строки преобразовывать. Но числами индексировать хочется — так давайте их автоматом преобразовывать! Где-то сделали так, где-то иначе, в конце-концов зафиксировали то, что получилось в стандарте… и, ужаснувшись, успокоились.
                • +1
                  > о будущем вам думать некогда

                  Если б он заглянул в будущее Нетскейп Навигатора, то быстренько бы уволился, какой там язык… ;)

                  > Но числами индексировать хочется — так давайте их автоматом преобразовывать!

                  Массивов изначально не было, зато с каждым свойством объекта дополнительно корреспондировал индекс того же объекта.
    • +1
      Про замыкания я вот тут относительно подробно написал: habrahabr.ru/blogs/webdev/38642/
      • +2
        Ну вот кстати да, одна из причин, по которой я не писал про замыкания — это как раз то, что про них и так довольно много написано. Если вы не против, я могу дать из своей статьи ссылку на вашу, чтобы не раздувать ее сверх меры.
        • +1
          Я только за. :)
          А за статью спасибо, новичкам будет очень полезна.
  • 0
    Примеси
    … при желании мы можем вообще не использовать прототипы
    На выполнение конструкций вида this.property = value; требуется время. Поэтому, чем больше объектов в системе и чем сложнее функции-конструкторы этих объектов, тем медленнее будет работать наш код.
    Лучше всё таки использовать прототипы, либо говорить про подобные «примеси» с оговоркой на скорость.
    • +2
      Вы статью-то читали? Там внизу все оговорки приведены.

      Вообще статья мне понравилась: одна из немногих рассказывающая про ООП в JavaScript и не притягивающая за уши понятие «класс», которого там нет и никогда не было…
      • 0
        Читал и мне тоже понравилось.
        Метод «примесей» подан как «очень положительный». Я же считаю, что его наоборот следует применять по минимуму.

        > не притягивающая за уши понятие «класс»
        Раз уж вы заговорили про уши:
        bird=function() {}
        // ...
        duck=function() {}
        duck.prototype=new bird();
        Подобный код всегда вызывал у меня непонимание: с одной стороны функция-конструктор является как-бы инициализатором свойств нового объекта, а с другой — может выступать в роли конструктора прототипа другой функции. Напонимание в следующем: как, используя данный метод, передавать параметры для инициализации нового объекта? Ответа я так и не нашёл (вариант проверки параметров на undefined в функции-конструкторе не подходит, потому что код становится не красивым =)
        • –3
          — Доктор, когда я сгибаю руку вот так у меня стреляет вот здесь. Что мне делать
          — Не сгибать так руку.

          Зачем вам потребовалось смешивать в кучу разные сущности? То что JavaScript позволяет такой финт ушами не обозначает что его нужно использовать!
          • 0
            — Доктор, а мне удобнее всего чесать за ухом именно с согнутой рукой! Что мне делать?

            Затем, что это интуитивно понятно и удобно. А из того, что вы так называете передачу параметров в функцию-инициализатор, совсем не обозначает, что этой замечательной возможностью пользоваться запрещено.
            • +2
              — А вам обязательно при этом руку между ног пропускать?

              Что конкретно вам не нравится в примере? Вы хотите создавать не только уток, орлов и пеликанов, но и просто абстрактных птичек? Сделайте фабрику:
              bird=function() {} //Это конструктор птички
              bird.prototype.cry=function(){alert(this.crytext);} //Птичка умеет кричать
              bird.prototype.fly=function(){alert('Я лечу!');} //и летать
              make_a_bird=function(crytext) {
                var nb=new bird();
                nb.crytext=crytext;
                return nb;
              }

              В чём проблема-то? Если вам нужна птичка - используете make_e_bird, а простой конструктор - оставьте для прототипов.
              • –2
                — Гы =) Доктор, ну уж вы совсем какую-то ерунду говорите.

                Предлагаю голосование: если кто-то выступает за метод make_a_bird, то [+1] вам в комент и [-1] в мой, а если против — то наоборот.
                • 0
                  подсыпь кармы — рассужу =)
              • +1
                > В чём проблема-то?

                А зачем нам «конструктор» и «сделай_на_основе_конструктора»? Ладно бы Вы штамповали каскадно объекты через фабрику, но это здесь — уже создана «порождающая сущность» (конструктор), но потом еще и «порождающая-порождающая сущность».

                Другой вопрос, когда конструктор обертывают — там нет никаких привязок к именам типа «bird», «make_a_bird» и т.д. + еще и связать прототипы можно внутри этой обертки.

                Создавать же wrapper для каждого конструктора вручную — это лишнее.
                • 0
                  А зачем нам «конструктор» и «сделай_на_основе_конструктора»?
                  А это уж у Covex'а нужно спросить. Если мы хотим по разному создавать объекты, являющиеся прототипами и не являющиеся таковыми — но нам нужны две разные функции. В большинстве случаев это действительно не нужно.

                  Ладно бы Вы штамповали каскадно объекты через фабрику, но это здесь — уже создана «порождающая сущность» (конструктор), но потом еще и «порождающая-порождающая сущность».
                  Эта так же раница, которая в C++ имеется между operator new класса и конструктором, а в Java — между конструктором и фабрикой. Порождать это дело руками или автоматом — дело вкуса.
                  • +1
                    > Порождать это дело руками или автоматом — дело вкуса.

                    Дело генерации. Или Вы на счетах считаете, а не на компьютере?

                    Один из лозунгов «какой-то» философии:
                    Правило генерации: избегайте ручного ввода кода, при любом удачном случае, пишите программы, которые бы писали программы.
            • +1
              Для прототипа wrapper можно создать, таким образом, и «холостые выстрелы» исчезнут. Прототип wrapper'a будет указывать на прототип родительского конструктора.

              function A() { alerr('A'); }
              A.prototype.fn = function () {};
              
              function B() {
                B.parent.apply(this, arguments);
                alert('B');
              }
              
              // связка прототипов для делегирования
              var __wrapper = function() {};
              __wrapper.prototype = A.prototype;
              B.prototype = new __wripper();
              B.prototype.constructor = B;
              B.parent = A;
              
              var obj = new B(); // alert('A'), alert('B')


              В то время, как, если бы не было wrapper'a — alert('A') «выстрелил бы в холостую» когда создавалась цепь наследования.
              • 0
                А смысл городить такие конструкции, если можно просто не делать в конструкторах ничего, кроме инициализации свойств дефолтными значениями?

                Ну и соответственно для каких-то реальных действий выделить отдельный метод init.

                Ведь если цель — сократить объем кода и избежать лишних сущностей, то метод с wrapper все равно не позволяет ее достигнуть.
                • 0
                  > А смысл городить такие конструкции, если можно просто не делать в конструкторах ничего, кроме инициализации свойств дефолтными значениями?

                  Ну компромисс — есть компромисс. И подобных компромиссов «а смысл?» можно тогда в любом месте напихать (тогда вообще, смысл может отпасть и достаточно будет линейного (даже не процедурного)) программирования (безо всяких классов и прототипов).

                  К тому же, иногда, кто-то в конструкторе (или методе init (если еще и для конструктора будет враппер) — не важно) может выдать системное сообщение.

                  > Ведь если цель — сократить объем кода и избежать лишних сущностей, то метод с wrapper все равно не позволяет ее достигнуть.

                  Ну вот в эти моменты люди и начинают писать функции-обертки (и часто называют их «классами»), где эти манипуляции с __wrapper'ами скрыты, но обеспечивают наследование. Естественно, никто не заставляет каждый раз писать этот кусок кода — вынесете его в обертку и создавайте объекты через нее (это всего лишь улучшение code reuse).
                  • 0
                    Тут дело даже не в обертке.
                    Конструктор то все равно ручками пишем. Вот и спрашивается, чем B.parent.apply(this, arguments) лучше по сравнению с this.init_A(arguments);
                    • 0
                      > Вот и спрашивается, чем B.parent.apply(this, arguments) лучше по сравнению с this.init_A(arguments);

                      Или я что-то упустил, или изначально не про это спрашивалось? :) Изначально спрашивалось, как передавать параметры в конструктор и не делать никаких проверок в этом одном и том же конструкторе, который используется и для порождения и для обеспечения наследования.

                      Касаемо Вашего текущего вопроса про «init_A»: в случае с B.parent — мы не привязываемся к именам родительских сущностей («А», «Bla», «SuperBla»). В принципе, можно даже и к «B» не привязываться, если общаться через «this.constructor.parent».
              • 0
                Прикольно!
                Правда сразу же напрашивается «обёртка» для всего этого =)
        • 0
          pastebin.ru/300297
          • +1
            Смущают две вещи:
            var klass= arguments.callee; // Говорят, что классы в JS - от лукавого !
            // и
            eval( 'var Cat= ' + Animal ); // А eval тем более =)
            • 0
              если единственный аргумент — «кто-то когда-то где-то что-то сказал-то», то меня он слабо колышет.

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

              как клонировать функцию без эвала?
              • 0
                > если единственный аргумент
                Мне самому нравится называть Animal и Cat — классами (я, типа, пошутить попытался =)

                > как клонировать функцию без эвала?
                Никак =( Мне всегда хотелось создать новый объект-функцию (но как-нибудь не через eval — это ж некрасиво =)
                • 0
                  не, ну можно, конечно, написать что-то вроде:

                  Animal= function(){
                  var func= function(){
                  // init
                  }
                  func.superconstructor= arguments.callee;
                  return func;
                  }()

                  Cat= Animal.superconstructor();

                  но это изврат ;-)
                • 0
                  Как это никак нельзя клонировать функцию без eval?
                  А binding`и уже отменили?

                  function GBind(oContext, fFunctor) {
                      return function () {
                          return fFunctor.apply(oContext, arguments);
                      }
                  }

                  или что-то другое имелось под клонированием?
                  • 0
                    Я имел ввиду немного другое. Приведу пример:
                    function Func() {}
                    var Obj = new Func();
                    alert(Func.constructor == Function);
                    Мне же хотелось иметь объект-функцию Func2, у которой был бы другой фактический конструктор — не Function, а какая-то другая моя функция.
                    (2All: я и сам приравниваю это своё желание к бреду — можно мне это не напоминать =)
                  • 0
                    arguments.callee будет всегда указывать на функтор => «клонировать» можно будет только специально подготовленные для этого функции => нет смысла использовать делегирование, если можно фабрику (см выше).
                • 0
                  Хм, может как-то так?
                  function class( methods ){
                  return function(){
                  if( ! this.constructor ) return null;
                  ....
                  }
                  }
                  var animal = class({...})
                  var cat = new animal( args )
              • 0
                > тандем из функции и её прототипа вполне можно называть классом ибо удовлетворяет всем признакам класса.

                только ты уточняй, — «всем признакам динамического класса» (потому что те, кто не знаком с динамическими объектами и классами, могут не сразу понять). Ну и, естественно, лучше уточнять (для избежания пустых холиворов), что стандарт понятия «класс», как «отдельная сущность для порождения объектов» — не описывает.
        • 0
          > Подобный код всегда вызывал у меня непонимание: с одной стороны функция-конструктор является
          > как-бы инициализатором свойств нового объекта, а с другой — может выступать в роли
          > конструктора прототипа другой функции.

          Тут нет никакого противоречия. Функция в любом случае является конструктором объекта. А прототип другой функции — это тоже объект. Ничуть не хуже какого-нибудь другого.
          • 0
            Согласен со всем сказаным, но вы вырвали фразу из контекста.
            Непонимание не в теоретической, а в практической части — там в моём комментарии начиная со слов «Напонимание в следующем» всё написано =)
  • +4
    Да — стоит заметить что во всех современных браузерах (кроме Opera и MS IE) «скрытое» свойство [[Prototype]] является вполне «открытым», имеет название __proto__ и его можно менять!

    Если вы пишете программну для Intranet'а и вас недобраузеры не волнуют — это можно с успехом иногда использовать, если же вы делаете публичный web-сайт, то этот можно использовать для отладки…
  • –1
    /* независимо от того, создаем мы массив как «test=new Array()» или как «test=[]», в результате все равно будет один и тот же объект */

    Объект будет не совсем «один и тот же»:

    Array.prototype.randomize = function() {}
    var a1 = new Array()
    a1.randomize() // ok
    var a2 = []
    a2.randomize() // ошипка


    (Firefox 3)
    • +3
      пишешь из параллельного мира, где развитие яваскрипта пошло по совсем другому пути развития? =)
    • +2
      Тысяча чертей! Прошу прощения за дезинформацию. Не зла ради!

      Недавно в своём проекте поменял дефиницю с [] на Array чтобы заработало прототипирование. И думал что это помогло ;-) А сейчас пробую — всё едино.
  • +1
    Ошибка в первом примере:
    test=new Function (alert('Hello!')}

    Неправильные скобки
    • 0
      Ага, спасибо, исправил.
      Вроде отлавливал опечатки, но видимо не все.
      • +1
        не забудь написать:
        upd: frizz подсказывает в комментах, что я наврал со скобочками.
        • 0
          И превратим этот пост в пост благодарности? )
          Насколько я помню даже Дональд Кнут, известный своей щедростью человек, выписывал чеки только за фактические поправки, а не за обнаружение опечаток.
          • 0
            я к тому, что не приятно читать сначала дезинфу и сразу за ней в скобочках исправления, исправления исправлений и тп. всё-таки статья должна оставаться статьёй, не превращаясь в дифф версий.

            а если очень хочется поблагодарить — я принимаю чеки, вебмани, яндекстаньга, пластиковые карточки и даже крышки от бутылок ;-)
  • 0
    Хорошая статья. Правда, о производительности ни слова :) А прототипы ой как тормозят…
    • +1
      Там вообще то есть о производительности.
      Под заголовком «Что теперь со всем этим делать».

      А можно поподробнее про торможение прототипов? Насколько я понял из тестов, которые проводил, создание объектов на прототипах наоборот происходит значительно быстрее в IE. А вот вызов методов из таких объектов — лишь немного медленнее. И опять же только в IE.
      • 0
        ну, я по опыту могу сказать вот что:

        Вариант 1
        var a = function (){
            this.set = function(a){this.var = a};
        }
        var b = new a();
        b.set(1);

        Вариант 2
        var a = function (){}
        a.prototype.set = function(a){this.var = a};
        var b = new a();
        b.set(1);

        Вариант 3
        var b = function (){}
        b.set = function(a){this.var = a};
        b.set(1);

        Если вариант 2 работает чуть (10-20%) медленнее, чем вариант 1 (обращение через прототип функции идет). То вариант 3 (без прототипов объектов) работает в несколько (у меня в Fx в 2 раза) быстрее.

        Причем, если нам метод set не нужен снаружи, то его можно объявить только внутри области видимости функции, и будет еще быстрее.
        • 0
          А в каких соотношениях в ваших тестах на производительность были «Объявление объекта-функции a», «расширение объекта a функцией set» и «вызов функции set»?
          • 0
            я привел полные варианты кода. Их можно запустить в цикле и замерить время.
            Если объекты сложнее, чем пара строк, то нужно мерить в каждом конкретном случае. Пропорции сохраняются (я больше про разницу между 1 и 3 случаями).

            Это я просто к тому, что если нам нужен синглетон — то использовать прототипы совсем не нужно.
            • 0
              > А прототипы ой как тормозят…
              >… если нам нужен синглетон
              А! Теперь всё ясно =)
              Для синглтона new — действительно лишняя операция + на поиск функции set в прототипе объекта тратится время.
              В остальных же случаях — вариант 2 работает быстрее, чем 1 и 3. Быстрее за счёт отсутствия необходимости создавать новые объекты-функции set при создании нового объекта.
              • 0
                нужны конкретные случаи. Т.е. хорошо бы обзорную статью со этими самыми случаями. В том коде, который я привел, вариант 2 — самый медленный.
                • +1
                  Имхо, на статью не тянет =( Кода гораздо больше, чем текста:
                  var a = function() { this.c = 0; }
                  a.prototype.set = function() { this.c++; }
                  var b = function() {
                   this.c = 0;
                   this.set = function() { this.c++; }
                  }
                  function T(T1) {
                   var T2 = new Date().getTime();
                   if (T1) alert(T2-T1);
                   return T2;
                  }

                  var i, T1, cnt = 100000, obj;
                  /* Первый тест: много созданий объекта и вызовов функции */
                  T1 = T();
                  for (i=0; i<cnt; i++) {
                   obj = new a();
                   obj.set();
                  }
                  T1 = T(T1);
                  for (i=0; i<cnt; i++) {
                   obj = new b();
                   obj.set();
                  }
                  /* На первый тест тратится на много меньше времени, чем на второй т.к. в первом случае на создание нового объекта set не тратится время */

                  /* Второй тест: одно создание объекта и много вызовов функции */
                  T1 = T(T1);
                  obj = new a();
                  for (i=0; i<cnt; i++)
                   obj.set();
                  T1 = T(T1);
                  obj = new b();
                  for (i=0; i<cnt; i++)
                   obj.set();
                  T1 = T(T1);
                  /* На первый тест тратится немного больше времени т.к. во втором случае не нужно искать функцию в прототипе объекта */

                  * This source code was highlighted with Source Code Highlighter.
                  • 0
                    пример не очень корректен: есть же третий случай, когда мы напрямую меняем без создания промежуточных объектов. Это будет еще быстрее.

                    Я про это и писал, что хорошо бы разобрать 3-5 характерных примеров использования прототипов / синглетонов. Ибо сейчас лично у меня все на уровне спинного мозга запомнилось :)
                    • –2
                      «Прототипы тормозят» — это Вы, батенька, переоптимизировали. Прототипы не являются медленной альтернативой доступа к свойству, а вносят дополнительную, недоступную другими путями, функциональность, на что и тратится время.

                      Синглтоны же вообще костыли из мира классического ооп, который что-то сильно любят притягивать за уши к JS, где средства-то другие.
                      • +2
                        > Синглтоны же вообще костыли из мира классического ооп, который что-то сильно любят притягивать за уши к JS, где средства-то другие.

                        Что Вы имеете в виду? Можно подробней, мне интересно.
                        • 0
                          Понадобиться «синглтон» в js может только для создания объекта, зависимого от возможно неподгруженных сущностей. Эдак можно вообще все сложные объекты так создавать «для подстраховки». ИМХО, если такая ситуация возникла, скорее всего, нужно пересмотреть архитектуру приложения.
                          Считаете иначе — приведите пример, если не трудно.
                          • 0
                            > Понадобиться «синглтон» в js может только для создания объекта, зависимого от возможно неподгруженных сущностей.

                            Почему? Почему, если мне нужен просто единичный объект:

                            var obj = {a: 10, b: function () {}, c: 20};

                            > Эдак можно вообще все сложные объекты так создавать «для подстраховки».

                            Не ясно.

                            > ИМХО, если такая ситуация возникла, скорее всего, нужно пересмотреть архитектуру приложения.

                            Почему? Почему, если мне нужен просто единичный объект:

                            var obj = {a: 10, b: function () {}, c: 20};

                            > Считаете иначе — приведите пример, если не трудно.

                            Да можно по-разному считать и если разные мнения технологически обоснованы — можно использовать все эти мнения и подходы.

                            Я хотел узнать, что там за «костыли», «уши» и «другие средства»?
                            • 0
                              var obj = {a: 10, b: function () {}, c: 20};
                              это не синглтон в понимании клооп.
                              Отсюда и непонимание про костыли и уши.
                              • 0
                                > это не синглтон в понимании клооп.

                                Что значит «не синглтон»? Причем здесь классовое ООП (я правильно понял «клооп»?). Где четкое определение синглтона (настолько четко, что дальше ни шагу)?

                                > Отсюда и непонимание про костыли и уши.

                                Ну, я все-таки снова попрошу объяснить. Ну а иначе — какой смысл просто так что-то говорить?
                                • 0
                                  Паттерн синглтон: ru.wikipedia.org/wiki/%D0%A1%D0%B8%D0%BD%D0%B3%D0%BB%D1%82%D0%BE%D0%BD
                                  www.rsdn.ru/article/patterns/singleton.xml

                                  «Применение cинглтона в js»: www.google.ru/search?q=javascript+singleton&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:ru:official&client=firefox-a
                                  и другие подобные извращения — притягивание за уши клооп к js.

                                  Как правильно, Вы уже сами привели пример:
                                  var obj = {a: 10, b: function () {}, c: 20};
                                  Всё, уникальный объект некоторого «класса» в нужной видимости создан — обращайтесь.

                                  Но это не применение синглтона :)
                                  Применить его подобие может понадобиться в случае habrahabr.ru/blogs/javascript/48542/#comment_1259671, т.к. одна из фич паттерна — отложенное создание объекта, но по этому поводу я своё мнение уже высказал.
                                  • 0
                                    > и другие подобные извращения — притягивание за уши клооп к js.

                                    Конкретно, пожалуйста, конкретно. Где, в каком месте и почему что-то там является извращением? Это Ваше ИМХО? Или кто-то Вам сказал?

                                    По поводу «клооп». Про динамические классовые организации слышали? Сравнения с моделью JS делали? Сходство видели? А разницу? И кто там кого и куда «за уши тянет»?

                                    > Но это не применение синглтона :)

                                    Смотря, что такое синглтон. Если для использования этого термина обязательно нужна сущность «класс» (а кто Вам это сказал?) — тогда можно не употреблять этот термин вообще. Тем, не менее, можно {} назвать, например, Singleinstance (и создать ее хоть отложено, хоть переотложено). Правда, для пущей наглядности, можно обернуть в функцию со «статическим» свойством — тогда уж точно отложено можно создавать/возращать нужный объект.

                                    «Синглтон» — всего лишь термин, для чье-то красивой реализации одиночности объекта. И «клооп» здесь особой роли не играет. А в JS нет никаких синглтонов, но можно называть одиночные объекты «Одиночками».
                                    • 0
                                      >Конкретно, пожалуйста, конкретно. Где, в каком месте и почему что-то там является извращением?

                                      Какие проблемы решает настоящий синглтон?
                                      В клооп если я хочу создать 1 объект, чтобы он был виден всем, я делаю его внешним статическим. Тогда я, во-первых, не могу подготовить что-то необходимое ДО его создания, во-вторых, рискую насоздавать несколько объектов. Могу создавать его динамически и передавать по ссылке нуждающимся, тогда я описываю лишние свойства, родитель должен быть обработан до других объектов. Выход — синглтон.

                                      В js если я хочу создать такой объект, я просто создаю обычный объект — тогда, когда он мне нужен. При этом, он сразу виден из любого места, случайно пересоздать его при правильной архитектуре приложения невозможно. Т.е. проблем, решаемых настоящим синглтоном, в js нет.
                                      Извращенем является первый же пример по моей ссылке — человек «решает» несуществующие проблемы.

                                      >А в JS нет никаких синглтонов, но можно называть одиночные объекты «Одиночками».

                                      В том-то и дело, что «в JS нет никаких синглтонов», потому, что эти объекты ничем не отличаются от остальных.
                                      Но это, конечно, не мешает Вам городить сущности и называть вещи так, как привыкли в клооп.

                                      >И кто там кого и куда «за уши тянет»?

                                      Всякие попытки имитировать клооп в js, принесение паттернов из клооп — притягивание того, к чему просто привыкли, в другую среду, потому, что не понимают её и не хотят обучаться. «За уши» — потому, что не нужно, не лезет и выглядит нелепо.

                                      • 0
                                        > В том-то и дело, что «в JS нет никаких синглтонов», потому, что эти объекты ничем не отличаются от остальных.

                                        Естественно, нет. Я больше интересовался об «ушах». При этом, о «клооп» заговорили Вы. Возможно, человек под «синглтоном» имел в виду порождение объекта не от «конструктора-штамповщика». Естественно, никаких синглтонов в роли паттернов из клооп в JS нет.

                                        > Всякие попытки имитировать клооп в js, принесение паттернов из клооп — притягивание того, к чему просто привыкли, в другую среду, потому, что не понимают её и не хотят обучаться. «За уши» — потому, что не нужно, не лезет и выглядит нелепо.

                                        О, поверьте, я не привык (ни к той, ни к другой, ни к третьей технологии). Паттерны, которые есть в «клооп», и порожденные именно благодаря статике и классам, естественно, не нужны в динамических языках (это я уже отметил). Насчет «ушей» — я лишь хотел сказать, что динамические классовые реализации (не касаемо никаких паттернов) — мало чем отличаются от динамических прототипных.
                                        • 0
                                          Значит мы просто изначально друг друга не правильно поняли.

                                          Синглтон, конечно, отчасти фабрика, но для меня он всё-таки конкретный паттерн именно из клооп, потому и заговорил про клооп. Тем более, на фоне общей тенденции имитировать клооп в js.
        • 0
          > Вариант 3… var b = function (){}

          А почему именно function, а не просто var b = {};? Коль скоро, это «синглтон»? {} по действиям алгоритма быстрее, чем создание объекта function.
          • 0
            это просто ради чистоты эксперимента — везде в качестве объектов функции. Можно и хэш, никто не спорит — это еще быстрее :)
            • 0
              > везде в качестве объектов функции

              да нет, в первых двух случаях, как раз-таки получаются не «объект типа функция», а «объект типа объект».
  • +1
    Еще можно порекомендовать «JavaScript: подробное руководство» Флэнагана.
    • +1
      Это та, что с бегемотом на обложке, отличная книга.
      • +1
        носорогом ;)
        • 0
          Ну да.
  • 0
    Отличная статья. Продолжайте в том же духе пожалуйста =)
  • 0
    obj=new String('hello'); //Создаем строку как объект
    simple='hello'; //Создаем примитивное значение


    А если говорить правильно, то оба обьявления ничем не отличаются, и обе строки будут совершенно одинаковыми.
    • 0
      Оговорка: первая конструкция работает медленнее, чем вторая
      • 0
        Ну я как бы не о скорости речь веду :)
    • 0
      Вообще-то они ни разу не одинаковые. Одна — объект, другая — строка. Одной можно добавить свойства, другой — нельзя. Это собственно и в статье написано.
      • 0
        Одной можно добавить свойства, другой — нельзя
        Я, видимо, неправильно добавляю свойства. :)

        >>> test = 'test';
        "test"
        >>> String.prototype.some_property = 12
        12
        >>> test.some_property
        12
        • 0
          test = 'test'
          String.prototype.A = 12
          test.B = 45
          console.log(test.A, test.B)
          • 0
            Свойство A присутствует, B — отсутствует.
            Т.е. свойства через прототип добавить можно, а индивидуальные нельзя.
            Возможно, это имел ввиду автор статьи
            • 0
              Да, речь именно о собственных свойствах объекта. Кроме того, с obj=new String() можно работать по ссылке, а с примитивной строкой — нет.
          • 0
            См. ниже.
    • 0
      alert([ Boolean(''), Boolean( new String('') ), Boolean( String( '' ) ) ])
      • 0
        Разобрался:

        String objects are created by calling the constructor new String(). The String object wraps Javascript's string primitive data type with the methods described below. The global function String() can also be called without new in front to create a primitive string. String literals in JavaScript are primitive strings.
        • 0
          wrapper'ы создаются для каждого из примитивных типов.

          alert(1.toString()); // создался wrapper, еще и наследование показано
  • –1
    Заметил ещё одну интересную особьенность конструкторов Яваскрипта, о которой, возможно, не всем известно.

    Объект можно наследовать от «стандартных» классов, таких как DOMElement и XMLHttpRequest.
    Для этого нужно вернуть из конструктора экземпляр «стандартного» объекта.

    Пример 1:
    var Element = function(tag, html, className, parent) {
      var e = document.createElement(tag || 'div');
      e.innerHTML = html;
      e.className = className;
      (parent || document.body).appendChild(e);
      e.customMethod = function() { /*...*/ };
      return e;
    };
    var test = new Element('span', 'Test', 'warning');
    


    Пример 2:
    var Request = function(url, method) {
      var req = new XMLHttpRequest();
      req.open(method || 'GET', url, true);
      /* other initialization */
      req.customMethod = function() { /*...*/ };
      return req;
    };
    var test = new Request('page.php');
    


    Варианты использования ограничиваются только вашим воображением.
    Например, я использую класс (наследник от XMLHttpRequest) который генерирует ошибку по таймауту (т.е. если запрос не был завершён в течение 10 секунд).
    • 0
      Имхо, это не совсем то, что вы ожидаете: return e; и return req; как бы «замещают собой» действие оператора new. Уберите его — и всё будет точно так же.
      • 0
        В примере выше:

        var test = new Request('page.php');
        test.send(); // вызов метода встроенного класса XMLHttpRequest
        test.customMethod(); // вызов своего метода
        


        Без return в конструкторе наследование от встроенного класса не получится.
        • 0
          Не. Всё будет работать без new.
          • 0
            Понял, что вы имели ввиду.
            Зато можно сделать вот так:

            var Request = function(url, method) {
              var req = new XMLHttpRequest();
              return req;
            };
            Request.prototype = {
              method1: function() {},
              method2: function() {}
            };
            var test = new Request('page.php');
            


            Т.е. кроме того, что расширять стандартный класс своими методами, можно ещё и реализовать множественное наследование.
            • 0
              Не работает этот метод
              alert(test.method1); // --> undefined
              • 0
                Ага, потому что test — это объект, созданный конструктором XMLHttpRequest, и прототип у него совершенно другой, нежели Request.prototype.

                Такая конструкция ни в каком виде не будет работать, потому что мы не можем изменить свойство [[Prototype]] у уже созданного объекта.
              • 0
                Хм, действительно, странно.
                (пошёл читать документацию и разбираться в своём коде)
  • +3
    > Как я упоминал выше, типы данных String и Number могут быть как объектами, так и примитивными значениями.

    Немного неточно про типы. Встроенные типы — это Undefined, Null, Boolean, Number, String, Object.

    'текст' — примитивное значение типа String;
    new String('text') — не-примитивное значение типа Object.
    • 0
      Т.е. Undefined, Null, Boolean, Number, String — это все примитивные типы, а Object — единственный не примитивный тип, от которого производными являются всякие Array, Function, RegExp и т.д.?
      • +1
        Объектный тип один (Object Type), но объекты разные. Очень условно можно считать, что у объектного типа есть «подтип» или «тип объекта»: Array, String, Number… Если принять к сведению, что стандарт избыточно использует одни и те же имена для разных дел, то получаем:

        String — встроенный тип;
        String — конструктор, характеризующий подтип String встроенного типа Object.

        С примитивным значением встроенного типа (String, Number, Boolean) корреспондирует не-примитивное значение соответствующего подтипа (String, Number, Boolean) встроенного типа Object.

        ;)
        • 0
          Теперь более менее понятно. А Function точно стоит в одном ряду с какбы «подтипами» Array, String и т.д.?

          Просто typeof Array, new String, new RegExp и прочие всегда выдает object, а вот typeof new Function выдает function.

          Может функция это шестой, самостоятельный тип данных?
          • +2
            Согласно стандарту всего 9 типов данных, 3 из них не конечные, о них можно не вспоминать:

            A value is an entity that takes on one of nine types. There are nine types (Undefined, Null, Boolean, String, Number, Object, Reference, List, and Completion). Values of type Reference, List, and Completion are used only as intermediate results of expression evaluation and cannot be stored as properties of objects.

            Оператор typeof (и не он один) не отражает своим названием суть происходящего:

            null -> 'object', хотя это тип Null;
            function(){} -> 'function', хотя это тип Object, типа Function нет вообще;
            нет свойства вообще -> 'undefined', хотя значения нет вовсе, как и типа;
            объект хоста -> любая строка, хотя тип Object.

            Функция определенно важный объект среди прочих, отсюда и свой typeof на случай приблизительного определения, callable объект или не callable, это важно. Но если рассматривать «подтип» типа Object так, как это упорядочено в стандартe, то есть с точки зрения каждого конструктора, свойств прототипа, свойств порожденного объекта и проч., то «подтип» Function стоит в одном ряду с «подтипом» Array, String… и т.п.
            • 0
              Да что ж такое, ни на что в этом языке нельзя понадеяться )
              Пофиксил это место в статье, спасибо за полезную инфу.
            • 0
              афайк тип function в языке есть, ибо нельзя вместо функции подставить специально сформированный объект и не обрести при этом эксепшен «not a function». то, что его нет в спецификации, которую кое-как сляпали уже после формирования языка — проблемы спецификации.
              • 0
                После формирования какого языка кое-как сляпали?
                • 0
                  сабжевого
                  • 0
                    Я просто пытаюсь понять, что есть в этом 'афайк', наличие эксепшна не убедило… ;)
                    • 0
                      о да, спецификация другого языка — куда убедительнее ;-)

                      функция — это кортеж из обычного объекта и ассоциированного с ним исполняемого кода, то есть является совершенно другой структурой данных нежели просто объект и как следствие является отдельным типом. а в спецификации написано, что луна из сыра…
                      • 0
                        Понятно. Думал, вы знаете какие-то исторические факты, почему появились «проблемы спецификации», почему функции не выделены в отдельный тип.
  • 0
    Наверно из серии «хочешь понять как это работает — попробуй объяснить другому»
    Интересно, когда начнут появляться статьи о JS 1.8…
    • 0
      наверное, когда он будет доступен большинству пользователей
  • +1
    > Таким образом реализуется иерархия прототипов, или каскадное наследование.

    Делегирующее. Каскадное — это, когда точная копия создается от прототипа, и новые объекты имеют свои собственные свойства (при этом прототип уже не нужен). Поэтому, в JS — делегирующее прототипное наследование.

    > Примеси

    Используя эту терминологию, лучше уточнить, что она не «стандартная» для JavaScript.
    • 0
      Делегирующее — это общая характеристика наследования в JavaScript. Я пытался подобрать термин именно для обозначения того, что образуется иерархическое наследование, когда прототип имеет собственный прототип и так далее. Видимо, термин подобрал неудачно. Лучше, пожалуй, эту часть фразы убрать вообще.

      > Используя эту терминологию, лучше уточнить, что она не «стандартная» для JavaScript.
      А чем она нестандартная, если для нее нужны лишь родные средства языка, без оберток, и сама идеология соответствует идеологии JS (динамическое изменение объектов)?
      • 0
        > Видимо, термин подобрал неудачно

        Да, просто термин «каскадирование» уже использован для описания прототипирования (http://ru.wikipedia.org/wiki/Прототипно_программирование). В принципе, частный случай в JS есть — когда свойства создаются прямо в объекте, либо, когда объекты создаются через «фабрику». А частный — потому что цепь прототипов все-таки присутствует.

        > А чем она нестандартная, если для нее нужны лишь родные средства языка…?

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

        Главное, что понимать под ней и как будет выглядеть реализация. В Ruby, например, подмешивание не создает родные слоты в объекте — там создается хидден-класс, который вклинивается на первое место в цепи классов (т.е. свойство не найдется в самом объекте, затем будет искаться в подмешенном хидден-классе, затем в классе объекта, затем в родителе класса объекта и т.д.). Т.е. снова — делегация, а не каскадирование. С другой стороны, «примесь» можно рассматривать как расширение самого объекта, создавая родные слоты. В принципе, оба подхода можно реализовать в JS. Просто более точным было бы — «стандарт не описывает понятия примесь, но, этот теоретический механизм вполне реализуем в JS» (это не цепляние к словам, просто уточнение :)).
  • +1
    В этом коде, каждая строчка выполняет одно и то же действие: создает функцию (а функция, как мы помним, это полноправный объект) и делает переменную test ссылкой на нее. С точки зрения интерпретатора нет никакой разницы, каким именно способом мы воспользуемся — результат будет один.


    Это не соответствует действительности.
    • 0
      точнее, не «баг с замыканиями», а «особенности реализации FunctionExpression» в разных движках
      • 0
        В комментариях у меня там всё описано. В любом случае, утверждение из статьи действительности не соответствует.
        • 0
          То, что в статье под одну гребенку подогнаны и и FunctionDeclaration и FunctionExpression — это, конечно, недочет. А то, что там какая-та реализация обрабатывает «по-своему» поведение FunctionExpression — это дело десятое.
    • +1
      Да уж, сложна была судьба языка JavaScript, и пути его создателей неисповедимы )
      • 0
        Точнее, не JavaScript, а реализаций Java(ECMA)Script.
    • 0
      В JavaScript полно таких несуразностей — а всё потому что язык не был спроектирован, а был слеплен из разных кусков, которые довольно странно взаимодействуют между собой. Хуже него — только PHP, даже Perl — и тот не так ужасен.
      • 0
        > а всё потому что язык не был спроектирован, а был слеплен из разных кусков, которые довольно странно взаимодействуют между собой. Хуже него — только PHP, даже Perl — и тот не так ужасен.

        Ой, с удовольствием послушаю-ю :) Подождите, попкорн только возьму и поудобней усядусь =) Шутка, не обижайтесь :)

        А, если серьезно, то — расскажите, почему Вы так думаете, откуда, на основе чего Вы пришли к таким выводам?
        • 0
          А, если серьезно, то — расскажите, почему Вы так думаете, откуда, на основе чего Вы пришли к таким выводам?
          Я просто наблюдал за этими языками в то время как они создавались и за попытками сделать из дерьма конфетку. Результат… тот ещё. Самые ужасные ляпы удалось убрать (по-моему PHP4 был чуть ли не единственным языком в котором объекты передавались по значению, а не по ссылке), но чудес всё ещё хватает и там и там. Например пресловутая модель на основе протипов: штука простая и красивая но кому и зачем пришла в голову идея что прямой доступ к полу __proto__ людям не нужен? В результа вместо простых и понятных манипуляций приходится изобретать кучу странных приёмов. Но, конечно, переплюнуть PHP где до сих пор оператор == — не отношение эквивалентности, а операторы < и > — не отношения порядка весьма сложно.
          • +1
            приходится изобретать кучу странных приёмов
            Так это же самое интересное! =)
            Имхо, изобретения — это искусство, а постоянное использование «понятных манипуляций» — ремесленничество.
          • +1
            > Я просто наблюдал за этими языками в то время как они создавались и за попытками сделать из дерьма конфетку. Результат… тот ещё.

            А с JavaScript'ом-то где дерьмо было? Черт с ними, со старыми версиями PHP, где объекты по значению передавались (кстати, какая такая священная книга написала, что они обязательно должны передаваться по ссылке? Ну — во многих языка — по ссылке, значит по ссылке — ок, они «исправились» в новых версиях).

            > Например пресловутая модель на основе протипов: штука простая и красивая но кому и зачем пришла в голову идея что прямой доступ к полу __proto__ людям не нужен? В результа вместо простых и понятных манипуляций приходится изобретать кучу странных приёмов.

            Моделей куча, организаций еще больше. Разве, во всех языках порожденный объект имеет доступ к своему конструктору (классу)? И прототипы тут не при чем. Например, в Python, объекты имеют доступ к классу через аналогичное свойство __class__. К тому же — лазейку оставили — через .constructor.prototype (это «куча странных приемов»?)

            > Но, конечно, переплюнуть PHP где до сих пор оператор == — не отношение эквивалентности

            А в JS тоже. Поскольку ни там, ни там строгой типизации нет. Но дерьмо ли это? Для эквивалентности в обоих языках есть ===.

            Чем еще эти языки ужасны?
            • 0
              А с JavaScript'ом-то где дерьмо было?
              В ранних версиях нельзя было даже надёжно сравнить два объекта! Операторы === и !== появились в версии 1.3! В ранних версиях объекты функций прямо так и принадлежали к объекту «функция» и к ним был доступ снаружи (это отменили не то в версии 1.4, не то в 1.5). И вообще там было много чудес. Которые, в частности, до сих пор не позволяют сделать нормальный многопоточный движок…

              Разве, во всех языках порожденный объект имеет доступ к своему конструктору (классу)?
              Нет, но зачем нам создавая динамический язык создавать какое-то дурацкое ограничение на ровном месте? Чтобы устроить всем программистам «кузькину мать»?

              К тому же — лазейку оставили — через .constructor.prototype (это «куча странных приемов»?)
              Эта лазейка не позволяет изменять поле [[Prototype]] (которое, кстати, можно было бы разрешить делать не только указателем на объект, но и, скажем, массивом — на скорость это особо не повлияло бы, но зато сделало бы mix-in'ы возможными).

              Или можно было бы пойти в другом направлении: большей строгости (см. проект JavaScript 2.0). А сегодня JavaScript — это ни рыба, ни мясо.

              А в JS тоже. Поскольку ни там, ни там строгой типизации нет. Но дерьмо ли это? Для эквивалентности в обоих языках есть ===.
              Я боюсь вы даже не поняли о чём я :-( Отношение эквивалентности не имеет никакого отношения к оператору эквивалентности и наличие === отнюдь не означает что == не должно быть отношением эквивалентности. Скажем в Lisp'е тоже есть несколько операторов сравнения: «eq?» «eqv?» «equal?» «=» и прочие — и все они являются отношениями эквивалентности. Как и должно быть в нормальном языке — независимо от того динамически он типизирован или статически. Конечно в тех языках где операторы можно расширять (многие диалекты Lisp'а) можно расширить эти операторы так, что они перестанут быть отнешениями эквивалентности — но это уже от программиста зависит.
              • +1
                > В ранних версиях нельзя было даже надёжно сравнить два объекта! Операторы === и !== появились в версии 1.3!

                а, т.е. был один ==, который не учитывал типы? и 1 == '1' было false, да? Или что? Вообще, не известно что было? Что значит «нельзя было надежно сравнить»? А как в других («не дерьмовых») языка дело обстояло в то время?

                > В ранних версиях объекты функций прямо так и принадлежали к объекту «функция» и к ним был доступ снаружи (это отменили не то в версии 1.4, не то в 1.5).

                Что имеется в виду? Я не совсем понял. Переменные в скопе функции и к ним был доступ? Или что за «объекты функции, к которым был доступ снаружи»? Уточните.

                > И вообще там было много чудес.

                Как и в любой разрботке, которая эволюционирует. А какие Вы видели «недерьмовые» эталонные разработки?

                > Которые, в частности, до сих пор не позволяют сделать нормальный многопоточный движок…

                А какие «чудеса» не позволяют?

                > Эта лазейка не позволяет изменять поле [[Prototype]] (которое, кстати, можно было бы разрешить делать не только указателем на объект, но и, скажем, массивом — на скорость это особо не повлияло бы, но зато сделало бы mix-in'ы возможными).

                В смысле? Несколько объектов (массив) в одном звене цепи наследования? А какая разница:

                obj -> obj1 -> [obj2, obj3] -> obj4

                или

                obj -> obj1 -> obj2 -> obj3 -> obj4

                Или я не правильно понял? И почему «тогда бы были доступы примеси»? Где точный алгоритм реализации примеси? Примесь может быть реализована и вклиниванием хидден-класса в цепь наследования и, так же, расширением самого объекта родными слотами.

                > Я боюсь вы даже не поняли о чём я

                О, пожалуйста, не переживайте, я Вас успокою — я понял. :)

                > не имеет никакого отношения к оператору эквивалентности и наличие === отнюдь не означает что == не должно быть отношением эквивалентности

                Почему не означает? Реализаций — много, идей — еще больше. Как реализовали, ==, так, вероятно, и задумывалось. А покажите пример, эквивалентности объектов в разных языках. Мне интересно.

                > Или можно было бы пойти в другом направлении: большей строгости (см. проект JavaScript 2.0). А сегодня JavaScript — это ни рыба, ни мясо.

                Если будет строго — это будет «рыба»? Или «мясо»? Почему? Откуда Вы взяли это утверждение?
                • 0
                  а, т.е. был один ==, который не учитывал типы? и 1 == '1' было false, да?
                  Наоброт 'popa' == 0 было true. NaN появился позже.
                  Что значит «нельзя было надежно сравнить»? А как в других («не дерьмовых») языка дело обстояло в то время?
                  Как и должно быть, собственно: если типы разные — то объекты разные, если справа и слева один объект — то они уж точно равны :-) Вот если типы одинаковые и объекты разные — можно двигаться дальше. Ту заплатку, которую в результате придумали сложно назвать вменяемой: в JavaScript a==a может быть false и даже a===a может быть false — что как бы несколько дико...function surprise() {
                    test.arguments.i = 2;
                  }
                  function test() {
                    var i=1;
                    surprise();
                    alert(i); // Ой какой surprise!
                  }
                  test();
                  Как и в любой разрботке, которая эволюционирует. А какие Вы видели «недерьмовые» эталонные разработки?
                  Pascal, Java, даже где-то C или Python. Да, там есть чудеса и недоработки — но такого «коврового бомбометания» несуразностей как в PHP или JavaScript там никогда не было.
                  А какие «чудеса» не позволяют?
                  Ну тут беда не в JavaScript'е, а в DOM'е — но на практике от этого не легче. Много чего доступно через глобальные переменные.
                  В смысле? Несколько объектов (массив) в одном звене цепи наследования? А какая разница.
                  Большая разница: посмотрите на «классику жанра» — ios, iostream, ostream и iostream в C++.
                  И почему «тогда бы были доступы примеси»? Где точный алгоритм реализации примеси? Примесь может быть реализована и вклиниванием хидден-класса в цепь наследования и, так же, расширением самого объекта родными слотами.
                  Про примеси даже Wikipedia неплохо пишет. Хидден-классы их не реализуют ни разу (ещё раз на пример с ios/istream/ostream/iostream, пожалуйста), множественное наследование и множественные прототипы — реализуют запросто. Через расширение объекта родными слотами можно сделать вообще что угодно, но там придётся много наворотов (понятно что в динамических языках можно менять и istream и ostream и эти изменения сразу и непосредственно влияют на iostream).
                  Почему не означает? Реализаций — много, идей — еще больше. Как реализовали, ==, так, вероятно, и задумывалось.
                  Нет, увы. Реализовали — как левая пятка возжелала. Потом добавили NaN чтобы дыру заткнуть. Получился дурдом.
                  А покажите пример, эквивалентности объектов в разных языках. Мне интересно.
                  Классическая тройка эквивалентностей из Lisp'а не подойдёт? Все три оператора «eq?», «eqv?», «equal?» — полноценные операторы эквивалентности; «=», «char=?» и прочие — специализированные (в смысле: могут кинуть исключение, но на тех данных, на которых не кидают — являются операторами эквивалентности). «eq?», «eqv?», «equal?» как бы охватывают всё более и более общие случаи:
                  (eq? 1 1) => #t
                  (eq? 1234567890123456789012345678901234567890 1234567890123456789012345678901234567890) => #f
                  (eqv? 1234567890123456789012345678901234567890 1234567890123456789012345678901234567890) => #t
                  (eqv? '(1 2) '(1 2)) => #f
                  (equal? '(1 2) '(1 2)) => #t
                  (= 1 1) => #t
                  (= 1234567890123456789012345678901234567890 1234567890123456789012345678901234567890) => #t
                  (= '(1 2) '(1 2)) => ERROR
                  (char=? #\a #\a) => #t
                  (char=? #\a #\b) => #f
                  (char=? #\a '(1 2)) => ERROR

                  Всё просто и предсказуемо не нужно гадать на кофейной гуще (по модулю чисел с плавающей точкой — но они тоже на ровном месте не возникают, строка не может вдруг сама неожиданно преобразоваться куда-нибудь).
                  • 0
                    > Наоброт 'popa' == 0 было true

                    А я вот не в курсе. Это как-то оговаривалось? Где-то описывался алгоритм ==?

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

                    Почему «если типы разные»? Почему 1 == '1' при разных типах не может быть true, если типизация динамическая? Более того, можно ввести оператор like и выдавать true на {a: 1, b: 2, c: 3} like {a: 1, b: 2}. При этом может быть одинаковый тип, но объекты разные. {a: 1} == {a: 1} — с какой стати должно быть true?

                    > и даже a===a может быть false — что как бы несколько дико

                    NaN == NaN // false (и это оговорено), а еще кто?

                    > Хидден-классы их не реализуют ни разу

                    А почему Вы так категорично утверждаете? В Ruby, например, так примеси и реализованы. По include'у создается хидден-класс, который вклинивается первым в цепь поиска.

                    > ещё раз на пример с ios/istream/ostream/iostream, пожалуйста

                    Подробней расскажите, я не понял, что Вы имеете в виду.

                    > Классическая тройка эквивалентностей из Lisp'а не подойдёт?

                    Я с Lisp'ом знаком поверхностно, но сейчас почитал про эти три оператора. И что? Конкретная реализация конкретной системы. Кто сказал, что во всех остальных должна быть такая же? А еще, кроме Лиспа?
                    • 0
                      А я вот не в курсе. Это как-то оговаривалось? Где-то описывался алгоритм ==?
                      Ну какое-то описание там было. Но вот насколько оно соответствовало действительности? Если судить по описанию и функция sort() должна была работать — а она это начала делать на всех платформах только в JavaScript 1.2… Я же говорю — сильно быстро делали, не до того было. Самый разгар «браузерных войн», Netscape 2.0, 3.0, 4.0, MS IE 3.0, 4.0, etc — где уж тут думать о красоте языка? Да и не ожидал тогда никто, похоже, что на JavaScript кто-то будет большие и сложные программы писать...
                      Почему 1 == '1' при разных типах не может быть true, если типизация динамическая?
                      Потому что если программа у вас не совсем тривиальна то сравнение переменных разных типов обозначает что вы что-то перепутали и нужно либо кинуть исключение, либо уж на худой конец вернуть ложь. У скриптов в 10 строк (под которые JavaScript и затачивался) ситуация другая, но сейчас — от такого поведения намного больше вреда, чем пользы.

                      Подробней расскажите, я не понял, что Вы имеете в виду.
                      ios содержит базовую информацию (скажем режимы вывода: hex/oct/dec, etc), istream реализует функцию read(), ostream — функцию write(), iostream — просто умеет делать и то и другое. Конечно если руками копировать методы туда-сюда, то можно сделать некоторое подобие, но в динамическом языке хочется чтобы после доваление метода readline() в istream и writeln() в ostream они появились автоматом и в iostream()!

                      Кто сказал, что во всех остальных должна быть такая же? А еще, кроме Лиспа?
                      Не обязательно. С учётом того, что стандарт IEEE прямо требует чтобы NaN не подчинялся логике я бы признал ситуацию с == удовлетворительной если бы аномалии касались бы только этого проклятого числа. Так она ведёт себя, скажем, в Python'е (objects of different types always compare unequal, and are ordered consistently but arbitrarily). Но нет же! Вполне может быть так что a==b, b==c, но a!=c. Что за @!@^*#)(*!? А когда у вас a<b, b<c, но c<a — то что можно о таком языке сказать? Правильно: ничего хорошего. Два языка для которых такие примеры можно придумать за рамками операций с плавающей точкой (которые всегда опасны и требуют особой квалификации от программиста) это JavaScript и PHP. Именно потому что они пытаются «помочь». Ровно этими благими намерениями вымощена дорога к бесконечным проблемам с безопасностью в PHP и JavaScript'е.

                      Правда моё отношение к PHP и JavaScript радикально отличается: PHP можно (и нужно) по возможности игнорировать, а JavaScript игнорировать не получится, так что с ним приходится мириться…
                      • 0
                        > где уж тут думать о красоте языка?

                        Да ладно, любой язык (любое творение) эволюционирует и развивается. Сейчас 1.5 / 1.6 / 1.7.

                        > если программа у вас не совсем тривиальна то сравнение переменных разных типов обозначает что вы что-то перепутали

                        Я не пойму, Вы как-то за меня, зачем-то, говорите, пытаясь приписать мне то, чего нет :) Говорите за себя. С чего бы это значило (да еще и в таком утвердительном ключе :)), что я что-то перепутал? Если Вы что-то перепутали (и привыкли к строгой типизации), это же не означает, что все остальные тоже перепутали, так? Или не так? :)

                        > iostream — просто умеет делать и то и другое.… хочется чтобы после доваление метода readline() в istream и writeln() в ostream они появились автоматом и в iostream()!

                        ах, вы об этом; все завесит от реализаций, и, смотря, в каком отношении изначально был iostream по отношению к istream и ostream. И что, что он «умеет делать и то и другое»? Главный вопрос в том, как это реализовано. Может там и было накопировано из обоих сущностей (так тогда и копируйте), если is и os были подмешены (пронаследованы) к ios, то это уже другая реализация.

                        > Но нет же! Вполне может быть так что a==b, b==c, но a!=c. Что за @!@^*#)(*!? А когда у вас a<b, b<c, но c<a — то что можно о таком языке сказать? Правильно: ничего хорошего.

                        Покажите мне примеры без NaN'ов, без сравнений {} и {} в JavaScript. Вероятно, все будет объяснимо, можно будет и в стандарте уточнить.
                        • 0
                          Я не пойму, Вы как-то за меня, зачем-то, говорите, пытаясь приписать мне то, чего нет :) Говорите за себя. С чего бы это значило (да еще и в таком утвердительном ключе :)), что я что-то перепутал? Если Вы что-то перепутали (и привыкли к строгой типизации), это же не означает, что все остальные тоже перепутали, так? Или не так? :)
                          Это простая статистика. Всё легко проверяемо. У вас есть пример большой (скажем от 1000 строк) программы на JavaScript где бы больше половины сравнений при нормальной работе могли бы сравнивать объекты разных типов? Покажите — интересно. А если большая часть сравнений сравнивает объекты одинаковых типов, то с точки зрения безопасности очевидно что лучше бы чтобы объекты разных типов были бы и вообще несравнимы — меньше вероятность сделать случайную ошибку.
                          Может там и было накопировано из обоих сущностей (так тогда и копируйте), если is и os были подмешены (пронаследованы) к ios, то это уже другая реализация.
                          Дык эта, мы, в общем-то, про примеси говорим.
                          Покажите мне примеры без NaN'ов, без сравнений {} и {} в JavaScript. Вероятно, все будет объяснимо, можно будет и в стандарте уточнить.
                          Да легко:var a1="1";
                          var b1=1;
                          var c1="01";
                          alert(a1==b1);
                          alert(b1==c1);
                          alert(a1==c1);
                          var a2=7;
                          var b2="13";
                          var c2="5";
                          alert(a2<b2);
                          alert(b2<c2);
                          alert(a2>c2);

                          Если с первым вариантом ещё можно что-то сделать (допускать одно и только одно «каноническое» написание чисел, то есть, скажем, что "-0" это не число и начинать числа с нуля тоже нельзя), то второе — это, похоже, неисправимое следствие идиотской попытки «всё улучшать». Можно, конечно, сравнивать строки «по-числовому» (алгоритм из RPM/DPKG: разбить строку на «числовые» и «нечисловые» части, после чего сравнивать «числовые» подстроки как числа, а «нечисловые» — лексикографически), но это приведёт уже к такому маразму с существующими скриптами, что все люди, кое-как свыкшиеся с ужасом под названием JavaScript взвоют и проклянут таких улучшателей. Увы — в этом месте JavaScript дерьмом родился — дерьмом и умрёт (если умрёт). Ничего уже не поделаешь… Разве что отказаться от идиотской идеи автоматического преобразования типов…
                          • +2
                            > Всё легко проверяемо.

                            И пример не приводите.

                            > при нормальной работе

                            Что такое? Это полный субъективизм, выработанный привычкой к конкретной технологии. То, что не получается увидеть знакомое и калькировать в другой технологии, не дает Вам права называть что-то ненормальной работой.

                            > Дык эта, мы, в общем-то, про примеси говорим.

                            Дык, эта… :) Я Вам про них и говорю. Покажите мне точные алгоритмы примесей (и бумагу, где с печатью Всевышним написано «примеси реализуются так и не иначе» :)). Я Вам привел примеры реализаций.

                            > Да легко

                            И что легко? Не интуитивно? И кто Вас строки в ожидании чисел просит сравнивать? Приводите все к числам и сравнивайте. Перед этим почитайте об алгоритмах приведения и о том, как в данной технологии работаю операторы ==, ===, <, >.

                            Я понимаю, что Вы привыкли к строгой типизации. Здесь не так. Здесь не строгая динамическая. И работа вышеприведенных операторов — плата за это.

                            > с ужасом под названием JavaScript взвоют и проклянут таких улучшателей. Увы — в этом месте JavaScript дерьмом родился — дерьмом и умрёт (если умрёт). Ничего уже не поделаешь…

                            Демагогия. Фальшь.

                            > Разве что отказаться от идиотской идеи автоматического преобразования типов…

                            Локальная привычка. Мысль в локальной идеологии.
                            • 0
                              И пример не приводите.
                              А причём тут пример? Речь идёт от статистике — тут нужно брать реальный код и смотреть. Хотите чтобы я тут вставил несколько тысяч строк jQuery или Prototype?
                              Что такое? Это полный субъективизм, выработанный привычкой к конкретной технологии.
                              Совершенно верно. Но вот именно этот субъективизм и определяет практически качество языка. Язык может быть безумно хорош со всех теоретических точек зрения, но если он не годится для реальных программистов — то язык дерьмо.
                              И что легко? Не интуитивно?
                              Причём тут интуиция? Речь идёт о последствиях ошибок. Куча алгоритмов завязана на то, что оператор равенства является отношением эквивалентности, а операторы сравнения — отношением порядка. Нарушая эти фундаментальные посылки вы автоматически усугубляете последствия ошибок: несгибаемые алгоритмы могут согнуться (скажем отсортированный массив может стать неотсортированным после удаления одного элемента из него) и обнаружение ошибок превратится в лотерею.
                              И кто Вас строки в ожидании чисел просит сравнивать?
                              Errare humanum est. Если вы в дизайне языка не учитываете тот факт, что человек делает ошибки — то вас следуют переквалифицировать в дворники.
                              Приводите все к числам и сравнивайте. Перед этим почитайте об алгоритмах приведения и о том, как в данной технологии работаю операторы ==, ===, <, >.
                              Разумеется так и делается. Но тот факт что если этого не сделать, то получаешь не вменяемое сообщение об ошибке, а сошедшую с ума программу язык аж никак не красит.
                              Я понимаю, что Вы привыкли к строгой типизации. Здесь не так. Здесь не строгая динамическая. И работа вышеприведенных операторов — плата за это.
                              Нифига подобного. Ерунда. Чем типизация в Python'е, Comon Lisp'е или Scheme так принципиально отличается от того, что имеется в недоязыках типа PHP или JavaScript? Однако же это не мешает им иметь вменяемые операторы сравнения и порядка (по модулю маразма с IEEE 754 — но это отдельная и, слава богу, небольшая песня).
                              Локальная привычка. Мысль в локальной идеологии.
                              Ну если считать что половина математики и почти вся информатика — это «локальная привычка», и нужно всего-то навсего разработать специальную «JS/PHP информатику», то да. Извините — но к таким жертвам я не готов.

                              А в рамках существующей науки и существующих программистов (а не уникально-сферически-непогрешных) JavaScript и PHP — дерьмо. И это не демагогия, а медицинский факт — подтверждается и теорией и практикой.
                              • 0
                                > Если вы в дизайне языка не учитываете тот факт, что человек делает ошибки — то вас следуют переквалифицировать в дворники.

                                Следите за речью. Иначе мы закончим разговор. Еще раз — говорите по существу.

                                > Хотите чтобы я тут вставил несколько тысяч строк jQuery или Prototype?

                                Опять демагогия.

                                > Ерунда.

                                Мне кажется, Вы не цените ни мое, ни свое время. Следите за речью, исключите демагогию, говорите по существу. Я много Вашей ерунды опроверг, — что-то Вы просто затыкаетесь в этих моментах, но, тем не менее, позволяете себе попусту кидаться словами.

                                > Errare humanum est

                                Никто за это Вас не винит. Надо просто глубже разбираться в теории, далее, в теории конкретных технологий и подкреплять практикой.

                                > Чем типизация в Python'е, Comon Lisp'е или Scheme так принципиально отличается от того, что имеется в недоязыках типа PHP или JavaScript?

                                Я, как отмечал, поверхностно знаю Comon Lisp'е, со Scheme — не знаком. С Python'ом знаком, но сейчас его под рукой нет. Напишите примеры (те же самые, со строками, числами, и т.д.) — посмотрим, что там происходит.

                                > А в рамках существующей науки и существующих программистов (а не уникально-сферически-непогрешных)

                                Спасибо, я «оценил» :)

                                > JavaScript и PHP — дерьмо.

                                Демагогия. Фальшь.

                                > И это не демагогия, а медицинский факт — подтверждается и теорией и практикой.

                                Усиленная демагогия, рассчитанная на то, что особо никто разбираться в этом трепе не будет.

                                > Ну если считать что половина математики и почти вся информатика

                                Туда же относится. Какая «половина математики» какая «вся информатика»? Хотите оперировать фундаментальными понятиями — учитывайте все факторы (на каких множествах происходят операции, есть ли оговорки и т.д.). Если технология делает оговорки и однозначность (типологическая) множества может варьировать — соответственно и отклонения в операциях так же будут присутствовать. А то, что их кто-то не понимает (или не желает понимать? :)) — дело десятое.

                                Не технологии дерьмо :)
                                • 0
                                  Я, как отмечал, поверхностно знаю Comon Lisp'е, со Scheme — не знаком. С Python'ом знаком, но сейчас его под рукой нет. Напишите примеры (те же самые, со строками, числами, и т.д.) — посмотрим, что там происходит.
                                  Смотрите:
                                  >>> print 7 < "13"
                                  True
                                  >>> print "13" < "5"
                                  True
                                  >>> print 7 > "5"
                                  False
                                  >>> print "1" == 1
                                  False
                                  >>> print 1 == "01"
                                  False
                                  >>> print "1" == "01"
                                  False
                                  Если технология делает оговорки и однозначность (типологическая) множества может варьировать — соответственно и отклонения в операциях так же будут присутствовать. А то, что их кто-то не понимает (или не желает понимать? :)) — дело десятое.
                                  Хмм. Интересная точка зрения. А сейчас замутим опрос :-) Посмотрим что скажет народ…
                                  • 0
                                    javascript.ru/ecmascript/part11#a-11.8.5

                                    Почитайте особенно пункт 3 и ниже, и все станет ясно.

                                    > А сейчас замутим опрос :-) Посмотрим что скажет народ…

                                    Только вы народу предисловие потом не забудьте показать, а не просто абстрактную фразу напишите :) Да, и пример, конечно.

                                    P.S> я через 40 минут уезжаю, до инета доберусь через день, так что, если что — отвечу позже.
                                    • 0
                                      Почитайте особенно пункт 3 и ниже, и все станет ясно.
                                      Что именно станет ясно? Что проблема не в конкретной реализации языка, а в самом языке? Так это и так ясно было.

                                      Понимаете — есть такой фундаментальный принцип: POLS. Он выходит далеко за рамки языков программирования и касается всех сторон человеческой деятельности. Это — принцип, не закон, при наличии достаточных оснований он может быть нарушен — но только если его нарушение окупится каким-нибудь большим удобством в будущем. JavaScript же (как и PHP) его нарушают «не думая». В частности проблемы с операторами сравнения настолько фундаментальны, что у меня просто в голове не укладывается что их нарушение может быть вообще оправдано хоть чем-нибудь — и уж совершенно точно возможность не преобразовывать строки введённые фурму в числа не оправдывает того ужаса, который разработчики этого кошмара сотворили с программистами…
                                    • 0
                                      21 пункт + замечание, а могло бы быть только 4:
                                      1. типы разные — фолс
                                      2. строки сравниваем как строки
                                      3. числа — как числа
                                      4. объекты — по ссылкам
                                      • 0
                                        > а могло бы быть только 4:

                                        В разных реализациях — как захотелось, так и сделали.

                                        > 1. типы разные — фолс

                                        не факт, лишь твое желание. Есть другие реализации.

                                        > 2. строки сравниваем как строки

                                        вот и приводи явно.

                                        > 3. числа — как числа

                                        вот и приводи явно.

                                        > 4. объекты — по ссылкам

                                        не факт, лишь твое желание. Есть другие реализации (например, можно сравнивать хэши объектов)
                                        • 0
                                          ну и нафига тогда мягкая типизация, если нужно безконца приводить типы? типа, мы избавим вас от необходимости приводить типы вручную! но код будет вести себя непредсказуемо… зачем такое счастье нужно?

                                          сравнивать объекты по хэшам — глупая затея.

                                          а захотелось, как мы видим, через жопу…
                                          • 0
                                            > ну и нафига тогда мягкая типизация, если нужно безконца приводить типы?

                                            И в Руби так и в Пайтоне (в новой версии). Где надо — приводи руками. Где не надо (и понимаешь, почитав условия, как это работает) — можешь не приводить (избавишься от лишних действий).

                                            Оператор like тогда вообще можно рассматривать, как «ослабленное слабого сравнения».
                                            • 0
                                              > И в Руби так и в Пайтоне

                                              Я имею в виду, что руками надо приводить.
                                              • 0
                                                — ты почему не здороваешься?
                                                — и вася, и петя тоже не здороваются. а я что, хуже?
                                                • 0
                                                  ой, только вот не надо еще одной демагогии, ни про «Вась», ни про «Петь» :)

                                                  Я говорю, что в Руби и в Питоне (относительно > и < стало) — динамическая строгая типизация. А в JS — динамическая нестрогая (слабая). И где нужно — приводи сам, где не надо (а таких случае, может быть, и будет больше) — не приводи (за тебя приведут).
                                  • 0
                                    все, с версии 3.0. Питона — надо руками приводить типы. Примеры выше будут выдавать ошибку.

                                    А в JS (ввиду динамической нестрогой типизации) — все по правилам.
                              • 0
                                Ну, то, что Вы начали минусовать мои комменты — это совсем не серьезно. Слабо просто по-человечески и по существу общаться? :) Вероятно, да.
                                • +1
                                  Комментария я не минусую — смысл какой? Всё равно дискуссия эта никому не интересна.
                                  • 0
                                    А я почитал =)
                  • 0
                    > Ой какой surprise!

                    А что Вы хотели показать и что ожидали увидеть? И формальные параметры и локальный var'ы (и function declaration) попадают в Variable (в новой редакции Environment) object. При этом одноименные var'ы и цифровые обращения к arguments взаимозаменяемы.
                    • 0
                      В ранних версиях JavaScript (кажется до версии 1.3) surprise() легко и непринуждённо менял переменную «i» внутри функции test — что, разумеется, ни в какие ворота не лезет. alert, понятно, выдавал 2. Сейчас это исправлено, но это ж кем надо быть чтобы такие чудеса в язык вообще засунуть? Правильно: человеком, который разрабатывает не язык программирования, а так — поделку на скорую руку и у которого сроки поджимают…
                      • 0
                        > но это ж кем надо быть чтобы такие чудеса в язык вообще засунуть? Правильно: человеком, который разрабатывает не язык программирования, а так — поделку на скорую руку и у которого сроки поджимают…

                        Опять — почему? Возможно, в той версии, объект arguments был связующим звеном (я не в курсе, и спецификацию для 1.2 не читал).

                        > Правильно:

                        Да не правильно. Как реализовали, так — задумали. То, что Вы где-то увидели другое, вовсе не означает, что должно быть так же здесь.

                        Потом поправили, поскольку — снова, решили, что идеологически и технологически — так вернее. Это есть эволюция системы.
                        • 0
                          Опять — почему? Возможно, в той версии, объект arguments был связующим звеном (я не в курсе, и спецификацию для 1.2 не читал).
                          Это задокументировано только в версиях в которых эта фича была отменена. Очевидно потому что появилась она как артефакт от «реализации на скорую руку» и никто особо не думал к каким последствиям это может привести.

                          Найти язык с худшим дизайном чем ранний JavaScript не так-то просто… конечно если забыть про PHP.
                          • 0
                            > Найти язык с худшим дизайном чем ранний JavaScript не так-то просто…

                            Демагогия.

                            К черту ранний JavaScript. Я Вам говорю о вполне адекватном понятии — «эволюция систем».

                            Что в нынешних версиях JS «дерьмового» кроме Ваших локальный привычек к строго типизации, кроме того, что Вы запутались в ==, ===, < и > и кроме того, что в каких-то реализациях вы не имеете __proto__ (хотя, стандарт говорит, что доступ к прототипу может быть дан в реализации)?
                            • 0
                              А я говорю об основных принципах. Они одинаковы и чудовищны в PHP и Javascript: результат любой ценой. Интерпретатор всячески пытается угадать что же такое на самом деле имел в виду программист и как бы всё-таки выдать какой-нибудь разумный результат.

                              Это — в корне порочная практика. Чем раньше диагностируется ошибка тем меньше ею последствия. Динамические языки, в принципе, отказываются от ранней диагностики ошибок — но они получают что-то взамен! В случае же языков JavaScript и PHP разработчики языка изначально выбрали в качестве добродетели ровно противоположное: неверная программа может иногда (если луна в нужном квадранте) выдать верный результат. Поведение операторов сравнения — лишь один пример, но, конечно, очень показательный. Наверное самый выдающийся остаток того ужаса, который был в этих языках изначально. При этом, что ужасно, полноценной отдачи от нарушения принципа «ранняя диагностика — благо» нет: языки гораздо менее динамические, чем могли бы быть!

                              Грубо говоря Lisp платит 100 рублей и получает отдачи на 150рублей — отлично, Python платит 50рублей и получает отдачи на 70рублей — тоже ничего, а JavaScript платит 1000 и получает отдачи на 100 — ну кто так торгует? Хуже только PHP платящий 2000 и получаюший отдачи на 50…

                              Сейчас с этим «первородным грехом» пытаются бороться — но это часто приводит в бешенство горе-программистов, использующих эти языки! Они-то уже привыкли к тому, что можно программировать небрежно и это «сойдёт вам с рук»!
                              • 0
                                > результат любой ценой

                                Т.е. все-таки неинтутивный для Вас результат каких-то вычислений заставляет Вас записывать что-то в «дерьмо», так? При этом завязано это лишь на Ваши привычки/непривычки, как Вы сами соглашаетесь, так?

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

                              • +1
                                столько бреда…
                                PHP — один из самых популярных языков в вебе. Наверно, все любят переплачивать…
                                Между делом, на мой взгляд js — один самых красивых языков. Может это дело вкуса?
                                • 0
                                  миллионы леммингов не могут ошибаться…
                  • 0
                    function surprise()
                    {
                    test.arguments[0] = 'world!';
                    }
                    function test©
                    {
                    alert©;
                    surprise();
                    alert©;
                    }
                    test('hi');
          • 0
            Передавать объекты по значению? Каждая передача объекта по значению это копирование всех свойств объекта. Это сильное замедление производительности, а для скрипт языка тем более.

            Вы наверное не имели опыта работы с низкоуровневыми языками. Просто многие вещи становятся очевидными почему реализовано так, а не так.
            • 0
              в пхп применяется ленивое копирование.
              • 0
                Передача по значению нужна редко, по умолчанию должна быть передача по ссылке (а если кому и потребовалась передача по значению никто не машет сделать метод клонирования, и даже указывать насколько далеко уходить в объект для копирования свойств). То что в js сделали передачу по ссылке — правильное решение.
                • 0
                  То что в js сделали передачу по ссылке — правильное решение.

                  По ссылке происходит работа непосредственно с объектом (его свойствами), не передача. «Переменные» передаются всегда по значению, даже если значение объектного типа.
                  • 0
                    ну я вообще то это знаю, речь шла об объектах.
                    • 0
                      а об объектах и говорится.
            • 0
              Передача объектов по значению плоха отнюдь не только и не столько с точки зрения производительности. Просто вся парадигма ООП построена на том что у объекта сохраняется некоторый набор инвариантов (иначе чем он отличается от простого ассоциативного массива?). Когда в этот механизм «грубо вмешиваются» создавая копию — получается маразм. Из распространённых языков такая возможность имелась только в C++ в PHP4 — причём в C++ это необходимо из-за совместимости с C и эффективности (когда объекты маленькие можно ускорить программу за счёт отказа от одного уровня индирекции). Разработчики PHP4 умудрились же в своей реализации ООП объединить все возможные недостатки разных систем! Ну да ладно — дело в прошлом, ООП в PHP5 сильно изменён и он уже даже на что-то вменяемое похож… что перевело PHP из категории «санитаров мне! срочно!» в категорию «ну и дерьмо же эта ваша заливаня рыба»… Прогресс… Посмотрим что в PHP6 будет :-) И сравним с Perl6 заодно…
              • 0
                > Просто вся парадигма ООП построена на том что у объекта сохраняется некоторый набор инвариантов (иначе чем он отличается от простого ассоциативного массива?). Когда в этот механизм «грубо вмешиваются» создавая копию — получается маразм.

                Почему? И почему обязательно объект обязательно должен иметь инваринты (да еще и для того, как Вы пишите, чтобы подпадать под парадигму ООП)?
                • 0
                  И почему обязательно объект обязательно должен иметь инваринты?
                  Что его тогда отличает от ассоциативного массива, если у него никаких инвариантов нет?
                  • 0
                    > Что его тогда отличает от ассоциативного массива, если у него никаких инвариантов нет?

                    Вы интересуетесь? Или даете мне помыслить, чтобы я пришел к какому-то ответу, который Вы, якобы, знаете?

                    Я отвечу: все эти понятия сверх-абстрактные и теоретические. Физически может вообще ничего не отличаться. Четких рамок вообще нет. Терминология — размыта, реализации — еще больше.

                    Поэтому объект от ассоциативного массива (который является лишь теоретической структурой данных; как, собственно, и объект) может ничем не отличаться.
                    • 0
                      Поэтому объект от ассоциативного массива (который является лишь теоретической структурой данных; как, собственно, и объект) может ничем не отличаться.
                      Зачем тогда умножать сущности без необходимости?

                      Вы интересуетесь? Или даете мне помыслить, чтобы я пришел к какому-то ответу, который Вы, якобы, знаете?
                      Нет. Я уточняю терминологию. Я всегда считал что объект — это как минимум набор инвариантов, связывающих биты, хранящиеся в этом объекте. Если для вас объект что-то другое — скажите что. Если вы не можете даже сформулировать что такое объект — то о чём мы тут говорим?
                      • 0
                        > Я всегда считал что объект — это как минимум набор инвариантов, связывающих биты, хранящиеся в этом объекте.

                        Примеры в студию. Инварианты — лишь часть (малая часть!) объектов и классов, чтобы ограничить объекты (да, я понял, инвариация Вас притягивает все из-за той же строгой типизации, вернее, вытекает, как следствие ).

                        > Если для вас объект что-то другое — скажите что.

                        Объетк — абстрактное представление (возможно физическое), что обладает своими свойствами. Применительно к программированию — всего лишь абстрактный тип данных, не более. (ага, инвариация — основа ООП :) простите, но это не так.)

                        > Если вы не можете даже сформулировать что такое объект — то о чём мы тут говорим?

                        Демагогия. Я в который раз отмечаю, что большая часть Вашего текста, демагогически направлена на личность (в данном случае мою). При этом, в реале — это не так. Говорите о предмете.
                        • 0
                          Примеры в студию.
                          Классический пример объекта в JavaScript — это массив. Инвариантом в нём является соотношение между длиной массива (тем, что возвращает .length) и содержимым (количеством хранящихся в массиве элементов). Если эти фундаментальные величины начинают расходиться — пользоваться массивом нельзя.
                          Инварианты — лишь часть (малая часть!) объектов и классов, чтобы ограничить объекты (да, я понял, инвариация Вас притягивает все из-за той же строгой типизации, вернее, вытекает, как следствие ).
                          Инварианты — это то, что остаётся если выбросить всё наносное.
                          Применительно к программированию — всего лишь абстрактный тип данных, не более.
                          Окей — абстрактный тип данных, так абстрактный тип данных. А что такое абстрактный тип данных? Это набор действий сохраняющий набор инвариантов. В примере с массивом выше: у нас может быть предусмотрено действие, вставляющие новый элемент в середину массива… а может не быть. Но ежели такое действие есть и оно не меняет длину массива (или, наоборот, увеличивающее его на два) — у нас в программе ошибка.
                          (ага, инвариация — основа ООП :) простите, но это не так.)
                          Простите, но если вы уберёте из массива кучу операций (pop, push, reverse, sort) — он останется массивом. А если уберёте его инварианты — не останется вообще ничего.
                          • +2
                            Ой, если я скажу, что и массивов в JS нет (ну, или более точно, что он мало чем отличается от любого другого объекта) и что .length (не зависимо от количества и сущности ключей и элементов) может возвращать не то, что Вы ожидаете, это только подкрепит Вашу «веру»? :) А если и в Ruby, например, так?

                            > А что такое абстрактный тип данных? Это набор действий сохраняющий набор инвариантов.

                            Это просто совокупность свойств для обособления. При этом, действия — тоже свойства. Возможно, сами свойства будут понятны не всем (не всем языкам мира), тогда делаются действия (описанные на понятном, общедоговореном языке), которые возвращают свойства.

                            Вы, пожалуйста, добавляйте к своим утверждениям фразы «ИМХО», «мне кажется», «я думаю», «я в большом сомнении», «я фантазирую», «я предполагаю» и т.д. (выберите потом).

                            Чтобы говорить на языке утверждений — надо: 1) — знать фундаментальную теорию, 2) обязательно знать локальную теорию конкретной технологии и 3) быть готовым подкрепить все это практикой по первой просьбе. При этом Вы пытаетесь (кидаетесь фразами, демагогируете) оперировать 1), не думая о 2), но говоря и о 3), поскольку иначе — «дерьмо» :)

                            Инварианты можно рассматривать лишь, как усиление типизации. Но не основу ООП.
                            • 0
                              Ой, если я скажу, что и массивов в JS нет (ну, или более точно, что он мало чем отличается от любого другого объекта) и что .length (не зависимо от количества и сущности ключей и элементов) может возвращать не то, что Вы ожидаете, это только подкрепит Вашу «веру»? :)
                              Я собственно потому и выбрал этот пример. То, что абстракции в JavaScript «протекают» — не делает этому языку чести.

                              Это просто совокупность свойств для обособления. При этом, действия — тоже свойства.
                              А вот это как раз самая ужасная кривизна в большинстве современных языков программирования. С какой стати действия имеют отношения к объектам? Они должны объединять объекты, а не принадлежать им! Рассмотрим четыре объекта: плоттер, монитор, круг и квадрат. Где должна жить функция draw? Разумный ответ нормального человека: нигде. У нас четыре функции draw: для вывода круга на плоттер, квадрата на плоттер, круга на монитор и квадрата на монитор. То, что эта простая естественная парадигма не реализуется в C/C++ или Java я ещё понять могу: ну там эффективность, всё такое. Но почему в скриптовых языках, где об эффективности и наносекундах говорить глупо — те же самые дурацкие ограничения??? В Common Lisp'е этих ограничений нет, ещё в парочке языков — тоже, но в большинстве языков снова и снова действия пихаются в объекты — где им совсем не место.
                              • 0
                                > Я собственно потому и выбрал этот пример. То, что абстракции в JavaScript «протекают» — не делает этому языку чести.

                                Да «нет» там массивов, это абстрактное выделение. Здесь инварианты не уместны вообще (а уж тем более в ключе — «основа ООП»), поскольку все объекты mutable, и каждый объект может перегруппироваться в «массив».

                                > С какой стати действия имеют отношения к объектам? Они должны объединять объекты, а не принадлежать им! Рассмотрим четыре объекта: плоттер, монитор, круг и квадрат.

                                Опять утверждение. Ок, давайте тогда мне ссылку на «бумагу с печатью», где Вы подобные утверждения выискиваете. Насчет draw — что мешает сделать 4 своих метода для каждого объекта?
                                • 0
                                  Да «нет» там массивов, это абстрактное выделение.
                                  С тем что массивов нет — соглашусь.
                                  Здесь инварианты не уместны вообще (а уж тем более в ключе — «основа ООП»), поскольку все объекты mutable, и каждый объект может перегруппироваться в «массив».
                                  О какое сильное утвеждение! Извините — если вы написали программу, то вам разве не интересно чтобы она делала то, что требуется? А если «инварианты не уместны», а выяснив что в массиве у вас три элемента вы сможете из него вытащить только два — то как вы будете доказывать что работа вашей программы вообще имеет хоть малейшее отношение к тому, что требуется?
                                  Ок, давайте тогда мне ссылку на «бумагу с печатью», где Вы подобные утверждения выискиваете.
                                  Из разных мест — и бумага с печатью не нужна если у вас есть голова и некоторые базовые знания CS.
                                  Насчет draw — что мешает сделать 4 своих метода для каждого объекта?
                                  И как тогда будет выглядеть реализация вывода векторной картинки (суть списка векторных фигур) на какое-нибудь устройство вывода — на плоттер или монитор (по выбору пользователя вашей библиотеки)? А если нам ещё в PDF, SVG и PostScript это потребуется перегонять? А если кроме квадрата и круга у нас будет ещё с десяток примитивов? Да, можно обойтись и вообще без ООП — но по-моему нежелание признать что в данном примере классический ООП неудобен — просто говорит о том, что вы не хотите думать, а хотите на всё получить «бумагу с печатью».
                                  • 0
                                    > О какое сильное утвеждение! Извините — если вы написали программу, то вам разве не интересно чтобы она делала то, что требуется? А если «инварианты не уместны», а выяснив что в массиве у вас три элемента вы сможете из него вытащить только два — то как вы будете доказывать что работа вашей программы вообще имеет хоть малейшее отношение к тому, что требуется?

                                    Я не допущу подобного, зная, какой инструменту у меня в руках, и что я могу с ним делать.

                                    > если у вас есть голова и некоторые базовые знания CS

                                    Еще один переход на личности, который никак не красит Вас в желании казаться тем, кем Вы не являетесь.

                                    > Да, можно обойтись и вообще без ООП — но по-моему нежелание признать что в данном примере классический ООП неудобен — просто говорит о том, что вы не хотите думать, а хотите на всё получить «бумагу с печатью».

                                    Кроме очередного пустого перехода на личности, я сути в Вашем вопросе не вижу. Расскажите подробней, что Вас волнует, посмотрим, что можно сделать.
                                    • 0
                                      самоуверенность в своей непогрешимости — страшнейший из пороков в программировании.
                                      • 0
                                        > самоуверенность в своей непогрешимости

                                        Это где это я «самоуверен в своей непогрешимости»?
                                        • 0
                                          «Я не допущу подобного, зная, какой инструменту у меня в руках, и что я могу с ним делать»
                                          • 0
                                            О, этим я всего лишь хотел сказать, что нужно изучать теорию конкретных технологий.

                                            Когда были представлены полностью «мутирующие» объекты (когда система состоит только лишь из них, и каждый из таких объектов может видоизменяться, перегруппировываться и т.д., образуя новые системы) относительно статических сущностей — они тоже были в штыки приняты. Тем не менее, это была новая идеология (просто кому-то непривычная).
                                            • 0
                                              о, ну давай, изложи нам эту великую теорию слабой типизации.
                                              • 0
                                                Да нет, давай так не будем неверный ход дискуссии задавать — типа ты отстаиваешь что-то одно, а я что-то другое. Я повторю — я не привязан ни к той, ни к другой идеологии, не я их создавал. Я лишь изучаю их. И видеть особенности разных технологий независимо от своих привычек — мне это интересней. Поэтому — «изложи эту великую идею» — не ко мне. Касательно же меня — мне удобно — я могу проверить просто на if (!obj) (при этом и '', и "", и undefined и 0 туда попадут — и это мне и нужно будет), вместо if (bla && bla && bla && т.д.). Так же — оператор like в некоторых языках (например, в не вышедшем ES4), который, подобно duck typing'у проверяет на «типа является тем, что мне нужно, но конкретика в данный момент не важна».
                                                • 0
                                                  а в качестве obj тебе придёт new Number(0) и будешь ты искать ошибку в стоге кода…
                                                  • 0
                                                    ну, если твоя программа неуправляема, и тебе не известно, что, где и когда приходит — это уже проблемы твоей программы.
                                                  • 0
                                                    >придёт new Number(0)

                                                    Бывает и так, нужно или исключить такую возможность (что правильно), или допроверить. Контрпример из философии языка — расширили прототип каким-нибудь методом, вызвали его — 66..method(), внутри метода чего-нибудь делаем с this, складываем, умножаем, не суть… Можно конвертировать сразу в примитив или можно (если операция позволяет) работать непосредственно с объектом, без эксепшнов и с тем же результатом. Используйте плюсы, избегайте минусов. Всё для вас.
                                • 0
                                  var pseudoArray= function(){ this.length= 0 };
                                  pseudoArray.prototype= new Array;
                                  pseudoArray.apply( this, new pseudoArray );

                                  // second argument to Function.prototype.apply must be an array
                                  • 0
                                    Об изменяемости объектов я говорил в общем (что может иметь «ключи» и «инвариантую» .length). То, что там на уровне реализации существуют жестокие проверки — это особенность системы (в принципе, да — обоснованные; потому — и выделение отдельной сущности).
            • 0
              > Вы наверное не имели опыта работы с низкоуровневыми языками. Просто многие вещи становятся очевидными почему реализовано так, а не так.

              Хорошо, хоть вы слово «наверное» вставляете. Я в курсе, почему передаются объекты по ссылке (кстати, понятие «ссылка» может означать в разных реализациях разное). Я лишь саркастически спрашиваю, «кто сказал, что должно быть так и не иначе»? Если какая-то реализация захочет сделать новую вещь, и это технологически и идеологически будет обосновано — там ресурсы будут второстепенны. Вы думаете Self в штыки не восприняли поначалу (под предлогом «Вы что, с ума сошли?! Столько памяти жрать?!»). Тем не менее.

              Отвечая Вам: я знаю, почему объекты передаются по ссылке (при этом, повторю, понятие «ссылка» физически может содержать не то, что есть в Си или Асме; к примеру, JavaScript. Какая вам там ссылка из Си? Там внутренний тип Reference, который является объектом (в свою очередь, этот объект содержит базу и имя слота)). И — у меня был опыт с низкоуровневыми языками (смотря, что еще вы называете низкоуровневым, Асм — тоже высокуровневый, относительно hex-кодов).
              • 0
                Физически не важно как ссылка реализована. А вот задача ссылки одинаковая что в C что в Js — указать на объект в памяти.

                p.s. ну у меня есть опыт написания программ и в hex кодах, и программирования под защищенный режим x86 на asm.
                • 0
                  > p.s.

                  а Вы решили, что я буду понтоваться чем-то? :)
                  • 0
                    это я уточнил что подразумевалось под низкоуровневым опытом :)
    • 0
      Пофиксил (точнее вообще убрал этот кусок, как не имеющий прямого отношения к тексту).
  • 0
    > зато у объекта есть свойство constructor, которое указывает на функцию-конструктор

    Тоже (опечатка, наверное) не точно в этом моменте. Сначала правильно пишите, что свойство constructor — это свойство прототипа конструктора. А затем говорите, что это свойство порожденного объекта (тогда как это свойство не родное и будет найдено в прототипе).
    • 0
      Если бы я хотел сказать, что это свойство порожденного объекта, то я бы сказал «у объекта есть собственное свойство constructor». Поскольку в данном контексте нам важно лишь то, что в этом свойстве лежит, то не не имеет значения, находится оно фактически в объекте или его прототипе — главное что оно доступно для чтения.
      Вообще, видимо, это все вопрос терминологии и «разжевывания объяснений» )
  • +1
    >Таким образом, this.simple_property=test.property='Hello', а this.object_property[user]=test.object_property[user]='Петя'.

    Скорее всего, так:
    this.simple_property=test.simple_property='Hello'
    • 0
      Угу, спасибо, пофиксил.
  • 0
    Автору большое спасибо! Статья очень хорошая и полезная. Почерпнул много нового для себя, правда не все осили с первого раза, так что придется перечитать повторно.

    Автор — продолжай!
  • НЛО прилетело и опубликовало эту надпись здесь
  • 0
    Пожалуй, нигде не встречал более простого объяснения наследования в Javascript.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.