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

«Сложно о простом». Функции-конструкторы — как объекты,(prototype). Объекты,(__proto__). constructor, ={}, как функция-конструктор new Object()

Время на прочтение 9 мин
Количество просмотров 80K
Всего голосов 57: ↑40 и ↓17 +23
Комментарии 39

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

Опять XKCD вспоминается :-)
Вот только это не xkcd. Чтобы убедиться в этом, попробуйте найти этот комикс на сайте xkcd — Вы беспременно потерпите неудачу.
Надо же, и правда.
Введите в консоль файрбага чтобы разобраться.
function A(){
console.log(«A constr»);
this.b=«asdfgh»;
}
A.prototype.b=«qwerty»;
var a=new A();
console.log(a);

console.log("-------------------");
function B(){
console.log(«B constr»);
};
B.prototype.__proto__=A.prototype;
B.prototype.c=«ololo»;
var b=new B();
console.log(b);

console.log("-------------------");

function C(){
console.log(«C constr»);
};
C.prototype.__proto__=a;
C.prototype.d=«hahahaha»;
var c=new C();
console.log©;

console.log("-------------------");

function D(){
console.log(«D constr»);
};
D.prototype.__proto__={preved:«medved»};
D.prototype.e=«trollface»;
var d=new D();
console.log(d);
Прочитав статью, понимаешь, что изучать Vanilla JS куда труднее, чем jQuery.
свойство prototype… перестаёт быть ссылкой и становится объектом

Простите, Вам бы самому подучить и, главное, понять терминологию, которую вы используете…

Свойство не может быть ссылкой или объектом. Значение свойства может быть примитивным значением (необернутые числа, строки) или ссылкой на объект.

Много текста, а из полезного только картинки, и те в JPG.

/*А при таком подходе B.prototype = {constructor:B, x:10, y:20}; добавления свойств в прототип
с экземпляром ничего не происходит*/

Здесь вообще ересь написана. Это не добавление свойств в прототип, а замещение прототипа новым объектом. И в первую очередь нужно объяснить, что {} создаёт объект, и что x = { a: 5 }; это то же самое, что x = new Object(); x.a = 5; и так далее по всему тексту такие ляпы, которые на самом деле только запутывают.

Рекомендую следующие презентации, которые коротко и четко описывают основные особенности JavaScript, которые попытался изложить автор этой статьи, и даже больше:
www.slideshare.net/Dmitry.Baranovskiy/demystifying-prototypes-6183470
www.slideshare.net/Sampetruda/advanced-javascript-closures-prototypes-and-inheritance
Свойство не может быть ссылкой или объектом. Значение свойства может быть примитивным значением (необернутые числа, строки) или ссылкой на объект.
Вам не кажется что вы придираетесь к формулировкам???

Здесь вообще ересь написана. Это не добавление свойств в прототип, а замещение прототипа новым объектом. И в первую очередь нужно объяснить, что {} создаёт объект, и что x = { a: 5 }; это то же самое, что x = new Object(); x.a = 5; и так далее по всему тексту такие ляпы, которые на самом деле только запутывают.
Цытата из статьи:«Во-вторых обычное присвоение переменной объекта, типа: var a = {}, интерпретатором на самом деле выполняется как var a = new Object(). А это значит, что свойство prototype функции-конструктора теперь содержит совершенно новый объект»
«То есть в отличии от Примера1 здесь мы не «добавили в хранилище свойство» и даже не «переписали хранилище заново», мы просто создали новое, разорвав связь с старым»
Скорее всего вы просто не дочитали статью и не потрудились понять… Дочитывайте статью полностью, а потом пишите гневные сообщения!
Да, я придираюсь к формулировкам, поскольку в них — ключ к пониманию того, что происходит на самом деле. И Вам в первую очередь следует обратить внимание на четкость формулировок и последовательность изложения текста. Можно было бы написать Вам это в личном сообщении, но мне хотелось открыть немного глаза тем, кто попытается осилить Ваш текст. Прошу прощения еще раз, если сообщение оказалось слишком гневным, но мне жаль тех, кто будет изучать JavaScript по этому тексту.

И да, я дочитал статью до конца, поэтому и посоветовал другим читателям (и вам тоже) поучиться у профессионалов JavaScript мирового уровня.
Ваши формулировки слишком неточны, поэтому к ним нельзя не придираться. Еще в первой части статьи стоило объяснить как хранятся «примитивы», как с ними производятся манипуляции (по ссылке или по значению) и что на самом деле не все «примитивы» ведут себя одинаково. Это весьма неплохо раскрыто у Дэвида Флэнагана, но как выяснятся даже там есть «упрощения».

свойство prototype… перестаёт быть ссылкой и становится объектом

Вам все верно написали —
Свойство не может быть ссылкой или объектом. Значение свойства может быть примитивным значением (необернутые числа, строки) или ссылкой на объект.


Что бы убедить вас окончательно, выполните следующий код:
var obj = {value:'objValue'};
function operation(arg) {
    console.log('source operation() arg --------');
    console.log(arg);
    console.log('arg === obj? ------------------');
    console.log(arg === obj);
    arg = {value:'argValue'};
    console.log('new operation() arg -----------');
    console.log(arg);
}

console.log(' source obj -------------------');
console.log(obj);

operation(obj);

console.log('obj after operation() ---------');
console.log(obj);


Учитывая что в функцию аргументы передаются только по значению, можно совершенно точно говорить что переменная типа «объект» это только ссылка. Переменная типа «объект» передается в функцию по значению (все переменные передаются в функцию только по значению), но ее значением является ссылка на объект. Соответственно видно, что obj === arg, то есть они ссылаются на один и тот же объект. При этом obj и arg это разные переменные, о чем свидетельствует то, что arg = {value:'argValue'}; не изменяет исходный объект. Хотя если бы мы написали arg.value = 'argValue'; то исходный объект изменился бы.
Если бы аргументы в функцию передавались по значению, то результат был бы иным:
a={a:5}
!function(a) {a.a=4}(a);
alert(a.a);
Прежде чем закончить обсуждение темы выполнения операций над объектами и массивами по ссылке, добавим немного ясности. Фраза «передача по ссылке» может иметь несколько смыслов. Для некоторых из вас эта фраза означает такой способ вызова функции, который позволяет изменять эти значения внутри функциии наблюдать эти изменения за ее пределами. Однако данный термин трактуется в этой книге в несколько ином смысле. Здесь просто подразумевается, что функции передается ссылка на массив или объект, но не сам объект. Функция же с помощью этой ссылки получает возможность изменять свойства объекта или элементы массива, и эти изменения сохраняются по выходе из функции. Те из вас, кто знаком с другими трактовками этого термина, могут заявить, что объекты и массивы передаются по значению, правда, этим значением фактически является ссылка на объект, а не сам объект. Пример 3.3 наглядно иллюстрирует эту проблему.

Пример 3.3. Ссылки передаются по значению
// Здесь приводится другая версия функции add_to_totals(). Хотя она не работает,
// потому что вместо изменения самого массива она изменяет ссылку на этот массив.
function add_to_totals2(totals, x)
{
newtotals = new Array(3);
newtotals[0] = totals[0] + x;
newtotals[1] = totals[1] + x;
newtotals[2] = totals[2] + x;
totals = newtotals; // Эта строка не оказывает влияния
// на массив за пределами функции
}

Дэвид Флэнаган. JavaScript. Подробное руководство, пятое издание. стр. 65

С объектами то же самое.
Ну так физическими значениями вообще всех переменных в жаваскрипте являются ссылки.

Если же говорить, что аргументы передаются по значению, нужно специально дополнительно оговаривать, или иметь ввиду, что присваивание переменной осуществляется по ссылке.
Вы согласны, что в примере на PHP используется передача по ссылке?
Вы согласны что пример на JavaScript ведет себя не так как пример на PHP?
Если ответ на оба вопроса — «да», то нет смысла спорить дальше — в JavaScript передача аргументов в функцию производится по значению.
Ну так, это всё с оговорками, что значениями переменных являются ссылки.
И я таки не понимаю, чем передача по значению ссылки на объект отличается от передачи объекта по ссылке.
Это не оговорки, это реализация JavaScript.
Чем отличается передача «ссылки по значению» и «передача по ссылке», я уже несколько раз описал, и примеры написал.

Если мы говорим про передачу по ссылке, то: Если мы меняем одно значение из пары (тройки или любого другого числа ссылающихся значений) то второе значение меняется всегда, вне зависимости от того как мы меняем первое значение (само значение или только одно его свойство).

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

Эта разница очень тонкая, но она есть!
Именно этот механизм используется, когда говорят, что необходимо присвоить значение null переменной, что бы избежать утечки памяти и что бы сборщик мусора удалил объект на который нет ссылок.

То есть фактически, работая с переменной типа object вы всегда работаете со ссылкой, вы используете ее для доступа к свойствам, но присваивая ей значение, вы присваиваете значение переменной (она может изменить при этом тип), но не самому объекту. При этом если на объект есть ссылки из других переменных, то он будет использоваться дальше, если нет, то он будет удален.
Вот весьма доходчивое объяснение. Спасибо.
Это очень похоже на передачу указателя из С. Если поменяем сам указатель в функции то ничего не призойдёт, а вот если обратимся к объекту по этому указателю то сможем его поменять.
И я надеюсь что труды мои не остались напрасными и за
сфегали по значению?
вам все таки стало стыдно.
Вообще Silver_Clash абсолютно прав! Видимо просто qmax`у довольно сложно понять разницу передачи по значению между ссылочными типами и элементарными.
Пардон фигню написал(спешил). Я хотел сказать разницу между самими ссылочными и элементарными типами.
сфегали по значению?
function modify(obj) { obj.foo = 'Bar' } o = { foo: 'Foo'} o.foo == 'Foo' modify(o) o.foo == 'Bar'
Попробуйте прочесть мой комментарий еще раз, только внимательно, а не по диагонали как вы сделали это первый раз. Значением переменной содержащий объект является ссылка на этот объект (на область памяти где он хранится), соответственно переменная передается по значению, но ее значение это и есть ссылка. Обратите так же внимание, что если внутри функции изменить не свойство объекта obj.foo, а саму переменную obj=null, то внешняя переменная при этом не изменится.
ну так это называется передача по ссылке.
передача по значению — копирование значение «в» локальную переменную.
значением любой переменной является сам объект, а не ссылка.
иначе бы все переменные пришлось бы разименовывать.

если внутри функции изменить значение переменной, то при любом способе передачи никакие внешние объекты не изменятся.
потомучто параметр функции — локальная переменная, безотносительно связанного с ней значения
Вы не правы!
Приведем простой пример (на php):
<?php
function operation(&$arg) {
  $arg = array('value' => 'secondValue');
  var_dump('function val: ', $arg);
}

$arr = array('value' => 'firstValue');
operation($arr);
var_dump('last val: ', $arr);
?>


Обратите внимание, функция принимает аргументы по ссылке, при этом если внутри функции изменить переменную принимающую аргументы, то изменится и переменная вне функции. Вот это — передача по ссылке.

Теперь еще раз посмотрите на пример написанный на JavaScript, он работает не совсем так. Если внутри функции обращаться к свойству объекта, но внешняя переменная так же изменяется. Но если локальной переменной присвоить какое то значение, то локальная переменная измениться, а внешняя нет. Это как раз и наглядно показывает что передача объекта происходит не по ссылке! Иначе внешняя переменная менялась бы в любом случае.

Просто вдумайтесь еще раз: переменная типа объект не содержит сам по себе объект, она содержит ссылку на область памяти в которой содержится объект. То есть значением переменной типа Object является не сам объект а ссылка на него. Когда такая переменная передается в функцию, то в функции создается ее копия (копия ее значения, то есть как раз передача по значению), то есть еще одна ссылка на объект. Учитывая это мы можем влиять на объект, то есть эти изменения будут доступны и снаружи функции. Но при этом если локальной переменной присвоить какое то значение, то старое значение (ссылка на объект) будет утрачено и мы больше не сможем влиять на объект и изменения снаружи видны не будут.

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

Так же есть еще один нюанс — в JavaScript нет возможности организовать цепочку из ссылок типа $a = $b = $c, то есть что бы $b ссылалась на $c, а $a ссылалась на $b. В любом случае в JavaScript результатом будет $b = $c, $a = $c, то есть все ссылки будут вести прямо на один и тот же объект, а не цепочкой через все переменные. Это еще раз подтверждает тот факт что внутри функции создается копия ссылки, а не ссылка на внешнюю переменную.
Пример на php вообще некорректен, там всё через задницу.
Синтаксис символа '&' скопипастен с C++, где он сам по себе означает «взятие адреса», а в определении типа — специальный ссылочный тип, реализуемый как указатель с автоматическим разыменованием.
Хорошо, приведите корректный пример, если считаете что мой некорректен.
Похоже я определился с темой следующего своего микро-топика о JavaScript. :)
Ответ на ваш вопрос.
То есть вы имеет в виду что правильнее написать«свойство начинает ссылаться на новый объект» ??
Хватит копипастингом заниматься, есть статьи Дмитрия Сошникова который просто гениально описал все внутренностие JS, что откуда вытекает и почему. Ваша статься больше похожа на неосознанный поток слов, без структуры.

Во что хабр превращается, постоянно одно и тоже:
habrahabr.ru/post/48542/
habrahabr.ru/post/133034/
habrahabr.ru/post/40909/
habrahabr.ru/post/94070/
habrahabr.ru/post/68004/
habrahabr.ru/post/131714/
habrahabr.ru/post/108915/
habrahabr.ru/post/124327/

Можете меня заминусовать, но хватит засирать хабр, научитесь искать, научитесь писать свои мысли!

Все кому интерес javascript идем и читаем dmitrysoshnikov.com/ecmascript/ru-javascript-the-core/ читаем, читаем, читаем

+1
Поддерживаю. Почему то каждый второй (утрирую) javascript программист пишет свою статью про наследование в javascript. Лично мне тоже больше всего понравилось читать статьи Дмитрия Сошникова — в них все подробно и хорошо расписано.
В частности прямые ссылки на две статьи про ООП в javascript:
dmitrysoshnikov.com/ecmascript/ru-chapter-7-1-oop-general-theory/
dmitrysoshnikov.com/ecmascript/ru-chapter-7-2-oop-ecmascript-implementation/
Куча бреда, на явные ошибки уже указали. Не стоит писать о внутренностях и сложностях, если не понимаете их — вы лишь запутываете новичков.
Название этой серии топиков о JS вполне отражает суть.
«Сложно о простом» — сложно, с домыслами, с недомолвками и недоразумениями о том, что можно объяснить просто.

Автор пытается сделать себе имя, вполне понятно зачем.

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

Т.е., если человек хочет написать в хаб о JS, его мысли должны пройти модерацию.

Что меня раздражает больше всего: этот ужас прочтут новички.
Им станет ещё больше непонятно и невнятно, они будут бояться эксперементировать, бояться JS как огня, при том, что JS и так не самая простая штука.

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

Автор, вы в данный момент представляетесь мне или негодяем, или просто бесцеремонным и бездарным наглецом!
Всю «статью» можно уместить в одной фразе:
«Переопределяя прототип, помните об уже существующих наследниках».

Несмотря на выбранный хаб Web-разработка:
1. Контекст браузера спутан с контекстом JS как такового. У меня создаётся жесткое впечатление, что кроме Web Inspector от Google Chrome Вы больше ничего не видели. Web Inspector это «шоры», знаете, как у лошадей, чтобы не испугались. Посмотрите на Rhino, посмотрите из FireBug. Напишите свой add-on, напишите HTML Application (*.hta) приложение или windows scripting host посмотрите, вы увидите, что приставка Web в Google Chrome не заканчивается, а только начинается!

2. Меня не сильно интересуют сайты и их контекст, но даже для них:

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

— вы говорите о конструкторе, пытаетесь переопределить конструктор.
Откройте хотя бы это:
developer.mozilla.org/en-US/docs/JavaScript/Reference
вы путаете конструкторы и прототипы, даже «местами»

— если не ошибаюсь, то Call это не свойство, это метод Function, именно Finction, с большой буквы. Есть такая вещь как «new Function». Все конструкторы через неё «проходят», с неё всё и начинается. Надеюсь кто-нибудь меня поправит если я всё таки не прав.

— «пустой», поправляли же уже, не пустой, null

— не знаю зачем, вы начинаете говорить о «this». При чем это здесь вообще?
Есть область видимости переменной. О ней ещё не было ни слова, и уже сразу «this».

вот типовой пример:

var abc_call = (function(){
	return function(){
		return this;
	};
}).call("abc");


лучше бы его объяснили, уверяю, на популярность вполне хватит…

Простите, ещё, я не пытаюсь Вас убедить, надеюсь просто. что это прочтут те, кому интересно:

1. Открываем Web Inspector в Google Chrome (Ctrl+Shift+I)
2. Заходим в консоль
3. набираем
typeof []

4. жмем «ввод» (enter)
5. получаем «object»
6. делаем то же самое с
typeof {}

7. получаем «object»

Нет никакой такой специальной таблицы.
Есть null, undefined и new Function
«Все остальные» это наши: string, number, boolean, array\object.
Они наследуются от null в момент исполнения своего конструктора, явного или неявного.
Приведение типов и прочая чепуха, это лишь «наносное», которое случается по вполне определенным законам, читаем хотя бы:
bonsaiden.github.com/JavaScript-Garden/
можно и на русском
bonsaiden.github.com/JavaScript-Garden/ru/
оно короткое…

Да, всем, кто долго работает с JS советую прочесть где-нибудь про указатели в C\C++, становится многое понятно, начинает получаться то, что раньше казалось недоступным.

С уважением и наилучшими пожеланиями, ваш КО :)
Как-то вы сами сложно о простом написали. Как это «они наследуются от null», ведь null — это специальное значение переменных, как и undefined? То, что null является значением свойства prototype последнего объекта в цепочке прототипов, не значит, что все объекты от него наследуются, а значит, что этот последний объект ни от кого не наследуется.

stackoverflow.com/questions/7688902/what-is-functions-proto
stackoverflow.com/a/5982610/1346510

И всё-таки специальная таблица есть:
code.google.com/searchframe#W9JxUuHYyMg/trunk/include/v8.h&l=863
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории