Comments 96
Немного не в тему, но Uncle разве не «дядя», а не «дедушка»? )
Ну и по теме: Для меня особенно ценными стали последние 3 разъяснения, за них особенное «Спасибо»!
Ну и по теме: Для меня особенно ценными стали последние 3 разъяснения, за них особенное «Спасибо»!
__proto__ — не является стандартом и уже depricated. Не советую использовать.
Поддерживаю. Но множество примеров — в том числе и тут написано с ним — и в итоге многие используют несмотря на все предупреждения.
__proto__ — очень удобно для демонстрации и понимания сути прототипного наследования, тем более в браузерах что-то типа такого и используется. А какой смысл его использовать в реальных приложениях?
За this вообще авторов убить мало, назвали бы scope и сразу куча проблем бы отвалилась.
А первый пункт не очень раскрыт, на самом деле. У вас Дедушка (и прочие) означают не конкретную персону, а группу, класс персон. И Дедушка меняет не свой цвет, а цвет своей группы. Это вполне себе можно и в ООП-модели осуществить, нужно лишь изменить класс Дедушка изнутри объекта (о правомерности этого отдельно можно говорить).
А первый пункт не очень раскрыт, на самом деле. У вас Дедушка (и прочие) означают не конкретную персону, а группу, класс персон. И Дедушка меняет не свой цвет, а цвет своей группы. Это вполне себе можно и в ООП-модели осуществить, нужно лишь изменить класс Дедушка изнутри объекта (о правомерности этого отдельно можно говорить).
здесь опечатка var bar = foo.bar();
должно быть var bar = foo.bar;
должно быть var bar = foo.bar;
Может, я что-то пропустил, но почему после очередного изменения «Деда»
у потомков не изменились свойства?
Разве мы не должны получить, как в предыдущих случаях
// Смысла нет
Grandfather.prototype.color = 'blue';
console.log([u.color, f.color, s.color]); // ["blue", "green", "green"]
у потомков не изменились свойства?
Разве мы не должны получить, как в предыдущих случаях
// ["blue", "blue", "blue"]
Перед этим переопределили значение у Father на blue — поэтому Father и его потомок Son видят именно blue и поиск свойства не идёт выше по дереву.
Дело в том, что Отец изменил свой прототип (
При выполнении f.color сначала произойдет поиск в собственных свойствах (не найдено), затем алгоритм будет искать в
Если же отец удалит цвет из своего прототипа, то все опять «телепатически» посинеют, кроме сына у которого есть собственное свойство color.
Father.prototype.color = 'green';
), а поиск свойства color лежит сперва через прототип отца. Вот так выглядит объект f:f = {
__proto__: { // Прототип Father
color: 'green', // Отец решил вернуть цвет
__proto__: { // Прототип Grandfather
color: 'blue', // Дед решил поменять на синий
__proto__: Object.prototype
}
}
}
При выполнении f.color сначала произойдет поиск в собственных свойствах (не найдено), затем алгоритм будет искать в
f.__proto__
— найдено! (color: 'green'
). Вобщем до прототипа деда и не дойдет.Если же отец удалит цвет из своего прототипа, то все опять «телепатически» посинеют, кроме сына у которого есть собственное свойство color.
// Отец решил все вернуть для себя и своего потомства
Father.prototype.color = 'green';
// Хотя мог исделать и так:
// Grandfather.prototype.color = 'green';
console.log([u.color, f.color, s.color]); // ["blue", "green", "green"]
Мне не совсем понятна часть выделенная жирным. Мне кажется, что если бы была вызвана эта строчка, то на выходе было бы
["green", "green", "green"]
, даже с учётом того, что цвет деда до этого был изменён на синий Grandfather.prototype.color = 'blue';
Да, все верно, было бы
["green", "green", "green"]
.Очень познавательно! Хорошо бы ещё поподробней по поводу — что и в каких случаях получит this: «undefined, null или window(global)»?
P.S. Метафоры конечно кстати. Аля Пелевин.
P.S. Метафоры конечно кстати. Аля Пелевин.
Почему наследование работает по такой хитрой схеме?:
Что плохого может случиться, если делать например так?:
Или так:
Почему
function inherit (object, parent) {
function F(){};
F.prototype = parent.prototype;
object.prototype = new F();
return object;
}
Что плохого может случиться, если делать например так?:
object.prototype = parent.prototype;
Или так:
object.prototype = parent;
Почему
instanceOf
сравнивает obj.__proto__
и constructor.prototype
, а не obj.__proto__
и constructor
? Из каких соображений выбран такой дизайн?Потому что если вы напишите object.prototype = parent.prototype, изменения в прототипе потомка будут влиять на свойства родителя. В случае примера с дедушкой, отцом и сыном, если отец изменит свой цвет, то это отразится и на дедушке.
Потому что, создатели Javascript-а недостаточно смелы были, чтоб оставить чистое прототипное наследование и добавили классоподобие.
Поясните пожалуйста момент с:
Не понимаю, почему в этом случае this — глобальный объект.
(foo.bar = foo.bar)(); // this === global (3)
(false || foo.bar)(); // this === global (3)
(foo.bar, foo.bar)(); // this === global (3)
Не понимаю, почему в этом случае this — глобальный объект.
Потому, что правила 1 и 2 применяются если слева стоит ссылка, а не значение. Во всех этих случаях слева — значение. Возможно тут будет понятнее.
Ух! «Неделя» javascript на хабре радует!
void, конечно оператор, но void 0 и void(0) работают одинаково, и при use strict в том числе
void, конечно оператор, но void 0 и void(0) работают одинаково, и при use strict в том числе
Правильно ли я понял?
Автор, интересно также, что по поводу других способов создания объектов и наследования между ними? Речь идет о всяких фабриках, клонирований и т.д.
Для чего люди их придумали, и стоит ли овчинка выделки?
Для чего люди их придумали, и стоит ли овчинка выделки?
Область применение фабрики одинакова во всех ЯП. Обертки с Class и другие создают для упрощения работы и приближения кода JavaScript к коду других ЯП.
TheShock собирался написать статью про паттерны и их применение, не буду спойлить
Объясните, плиз, зачем функцию getProperty необходимо было вызывать именно с указанием контекста (через call)? Просто там this внутри нигде не используется. Или я что-то неправильно понимаю?
А! Там же вызывается print(), которая и содержит вывод переменных через this. То есть, нужно указывать контекст, верно. В общем, моя невнимательность. Прошу прощения )
Код
1. getProperty(p, 'print') возвратит функцию (это как сделать
2. Если функцию вызвать напрямую, то мы получим в качестве this — window и ничего не напечатается.
3. Чтобы предотвратить это мы вызываем функцию, которую возвратит getProperty с помощью явного указания this
getProperty(p, 'print').call(p)
. Разберем что тут происходит: 1. getProperty(p, 'print') возвратит функцию (это как сделать
var p_print = p.print
)2. Если функцию вызвать напрямую, то мы получим в качестве this — window и ничего не напечатается.
3. Чтобы предотвратить это мы вызываем функцию, которую возвратит getProperty с помощью явного указания this
Не совсем понял полезность этой идеи:
Вместо прямого указания, что прототипом конструктора Father, является экземпляр конструктора Grandfather, мы указываем пустой подставной объект. А прототип этого подставного объекта является прототипом конструктора Grandfather. То есть, если убрать подставной объект, получается что-то вроде
Или я что-то не так понял?
inherit(Father, Grandfather); // Это лучше
Вместо прямого указания, что прототипом конструктора Father, является экземпляр конструктора Grandfather, мы указываем пустой подставной объект. А прототип этого подставного объекта является прототипом конструктора Grandfather. То есть, если убрать подставной объект, получается что-то вроде
Father.prototype = Grandfather.prototype;
, что совсем не эквивалентно Father.prototype = new Grandfather();
, так как если Grandfather это «самый верхний» объект в дереве, то его прототипом является Object.Или я что-то не так понял?
В подтверждение следующий код:
function inherit (object, parent) {
function F(){}; // Подставной конструктор
F.prototype = parent.prototype; // Подсовываем прототип реального конструктора
object.prototype = new F(); // Теперь реальный конструктор не будет выполнен
return object; // Можно и не возвращать
};
function Grandfather() {
this.color = "red";
}
function Father() {
this.number = 1;
}
inherit(Father, Grandfather);
father = new Father();
console.log(father.number); // 1
console.log(father.color); // undefined
Если добавить в конструктор одну строку
То father получит own property — color. Обычно во всех ЯП если происходит ореррайд метода, то по умолчанию предыдущий метод «не наследуется», пока разработчик не прописал его явно.
Я про это писал
function Father() {
Grandfather.call(this); // Вот эту
this.number = 1;
}
То father получит own property — color. Обычно во всех ЯП если происходит ореррайд метода, то по умолчанию предыдущий метод «не наследуется», пока разработчик не прописал его явно.
Я про это писал
Конструктор Grandfather не будет выполнен. Если нам все-такие необходимо выполнить конструктор Grandfather, то вызываем его с помошью call или appy
var Father = function () { // Конструктор Father Grandfather.call(this); };
В вашей статье вы об этом не писали. Вы просто показали «более правильный» метод наследования, который, как я показал, не работает. Объект father не унаследовал свойство color, которое біло обїявлено в конструкторе Grandfather.
Кстати, вариант с Grandfather.call(this); в конструкторе тоже имеет недостатки, так как он скопирует методы родителя, вместо того, чтобы унаследовать их через прототип.
Кстати, вариант с Grandfather.call(this); в конструкторе тоже имеет недостатки, так как он скопирует методы родителя, вместо того, чтобы унаследовать их через прототип.
Сорри за украинские буквы, все время путаю раскладки на клавиатуре :)
Кстати, вариант с Grandfather.call(this); в конструкторе тоже имеет недостатки, так как он скопирует методы родителя, вместо того, чтобы унаследовать их через прототип.
Вы ошибаетесь. Будет только вызван конструктор родительского объекта. Методы будут унаследованы через прототип.
Вы ошибаетесь. Будет только вызван конструктор родительского объекта. Методы будут унаследованы через прототип.И снова неправда. И снова кусок кода, в качестве подтверждения моих слов:
// Объект-конструктор "Отец"
function Father() {
this.returnRed = function() {
return "red";
};
}
// Объект конструктор "Сын"
function Son() {
this.number = 1;
Father.call(this); // Делаем "наследование" через call
}
// Объект конструктор "Дочь"
function Daughter() {
this.number = 2;
}
father = new Father(); // Создаем экземпляр отца
Daughter.prototype = father; // Дочь наследует метод отца через прототип
son = new Son(); // Создаем экземпляр сына
daughter = new Daughter(); // Создаем экземпляр дочери
console.log(daughter.returnRed()); // red — метод унаследован
console.log(son.returnRed()); // red — неужели тоже унаследован?
// Изменяем метод в прототипе.
Daughter.prototype.returnRed = function() {
return "blue"
};
Son.prototype.returnRed = function() {
return "blue"
};
console.log(daughter.returnRed()); // blue — изменилось корректно
console.log(son.returnRed()); // red — опа! А метод то берется не из прототипа. Он просто скопировался в наш объект.
Вы намешали и прототипы с собственными свойствами. Не пойму чему вы удивляетесь :)
son.returnRed()
returnRed — own property, очевидно, что до прототипа не дойдет. Вы показываете несуществующие проблемы.Теперь вы себе противоречите :)
Ладно, закончим дискуссию. Начинается флейм :)
Ладно, закончим дискуссию. Начинается флейм :)
Разве это мой комментарий? Они будут унаследованы через прототип, если в конструкторе нет одноименного метода/свойства. Ваш вышеположенный код — несуществующая проблема. Или проблема для тех, кто не понимает сути прототипного делегирующего наследования.
Мне все больше начинает казаться, что вы меня тонко тролите :)
Мне все больше начинает казаться, что вы меня тонко тролите :)
Упс, почему-то подумал что ваш.
Если бы троллил, то просто написал бы, что это все херня, а я — ромашка :)
Просто интересно поспорить. Если я прав, то смогу что-то доказать. Если неправ — узнаю что-то новое.
Если бы троллил, то просто написал бы, что это все херня, а я — ромашка :)
Просто интересно поспорить. Если я прав, то смогу что-то доказать. Если неправ — узнаю что-то новое.
Ниразу не тролинг. Я тоже не понимаю этого:
Если всё так просто, то зачем такое сложное наследование через создание объекта?
А это не проще ли?
Результатом будет как раз такая последовательность:
ss.__proto__.__proto__.__proto__.__…
/* SonsSon <- Son <---- Father <- Grandfather <-- Object <-- null */
console.log(ss.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__ === null);
Если всё так просто, то зачем такое сложное наследование через создание объекта?
Father.prototype. = new Grandfather();
А это не проще ли?
Father.prototype.__proto__ = Grandfather.prototype;
Результатом будет как раз такая последовательность:
ss.__proto__.__proto__.__proto__.__…
В некоторых браузерах свойство
__proto__
скрыто. Его дописать можно, но толку от этого не будет. Поэтому создаем цепочку прототипов через new.function operatorNew (Constructor, args) {
/*1*/ var n = {'__proto__': Constructor.prototype}; // Движок JS в браузере может переписать [[proto]]
/*2*/ F.apply(n, args);
/*3*/ return n;
}
Это я понимаю. Но зачем его скрыли? Ведь с ним, наследование было бы проще пареной репы! А сейчас для нативного наследования нужно породить объект! Какая логика в такой конструкции: «для наследования необходимо породить объект»? Как надо было обкурится, что бы придумать такое?
Естественно, порождать объекты никому не нравится и появляются костыли типа:
А затем ещё тонны библиотек для не нативного ООП. Зачем было так усложнять язык закрывая свойство __proto__? При этом prototype оставили открытым и это нормально.
Естественно, порождать объекты никому не нравится и появляются костыли типа:
inherit (object, parent)
А затем ещё тонны библиотек для не нативного ООП. Зачем было так усложнять язык закрывая свойство __proto__? При этом prototype оставили открытым и это нормально.
__proto__
изначально было скрыто. Потом кто-то стал его открывать минуя стандарт. В ECMAScript [[Prototype]]
— внутреннее свойство. es5.github.com/#x8.6.2 es5.github.com/#x13.2.2 В 6-й версии ECMAScript будет сахар — class (который на самом деле делает не class-ы, а строит цепочку прототипов).Т.е. я мыслю в правильном направлении и в следующей версии будет то о чём я написал сначала?
Или class — это совсем не то?
Father.prototype.class = Grandfather.prototype;
Или class — это совсем не то?
class Monster extends Character {
constructor(x, y, name) {
super(x, y);
this.name = name;
this.health_ = 100;
}
attack(character) {
super.attack(character);
}
get isAlive() { return this.health > 0; }
get health() { return this.health_; }
set health(value) {
if (value < 0) throw new Error('Health must be non-negative.');
this.health_ = value;
}
}
harmony:classes
Фигово, это уже совсем не то. Из отличной локаничной идеи состоящей всего из трёх конструкций («function», «new», «prototype») и при этом позволяющей делать всё что угодно (классы, конструкторы, наследования, примеси и многое другое...); теперь вместо этого будет монстр со всеми этими конструкциями: class, constructor, extends, super, static, public/private, const, get/set.
Потом будут появлятся: interface, abstract, traits.
Потом будут появлятся: interface, abstract, traits.
Вы мало того, что путаетесь в своих примерах, так ещё и обвиняете меня во лжи)))
Наследование через конструктор — это какая-то чужеродная глупость, которую фиг знает кто и зачем придумал.
Это ошибка. Мы не делаем наследование, а вызываем родительский конструктор. Я просто перепишу ваш пример на PHP)
Правда странно смотрится? И теперь поняли, что это совсем не наследование? ;)
А теперь как надо было сделать:
Наследование через конструктор — это какая-то чужеродная глупость, которую фиг знает кто и зачем придумал.
Father.call(this); // Делаем "наследование" через call
Это ошибка. Мы не делаем наследование, а вызываем родительский конструктор. Я просто перепишу ваш пример на PHP)
class Father {
function __construct () {
$this->returnRed = function () {
return 'red';
};
}
}
class Son {
function __construct () {
$this->number = 1;
parent::__construct();
}
}
Правда странно смотрится? И теперь поняли, что это совсем не наследование? ;)
А теперь как надо было сделать:
class Father {
function __construct () {
// Father construct
}
function returnRed () {
return 'red';
}
}
class Son {
function __construct () {
$this->number = 1;
parent::__construct();
}
}
А я уже думал что эта ветвь обсуждения исчерпана.
Я прекрасно понимаю, что это не наследование. Это вызов конструктора другого объекта (не называю его родительским, так как наследования нет), с подменой контекста текущим объектом. При этом все свойства/методы не наследуются через прототип, а копируются.
Именно поэтому слово «наследование» я написал в кавычках.
Я прекрасно понимаю, что это не наследование. Это вызов конструктора другого объекта (не называю его родительским, так как наследования нет), с подменой контекста текущим объектом. При этом все свойства/методы не наследуются через прототип, а копируются.
Именно поэтому слово «наследование» я написал в кавычках.
Father.prototype = Grandfather.prototype;И в итоге все унаследуют от Grandfather
Или я что-то не так понял?Если в конструкторе Grandfather будет alert или подобное добро, то как вы думаете сработает ли он в
Father.prototype = new Grandfather();
? ;-)И в итоге все унаследуют от GrandfatherНет. В итоге Father унаследует все от прототипа Grandfater, в нашем случае — Object. То есть не унаследует вообще ничего. Это я показал на примере в своем комментарии выше.
Если в конструкторе Grandfather будет alert или подобное добро, то как вы думаете сработает ли он в Father.prototype = new Grandfather();? ;-)Если вы написали в конструкторе alert, то вы плохой программист. Если вы придерживаетесь следующего правила:
Я предпочитаю основываться на соглашениях и не проверяю this внутри конструктора — вызвал конструктор без new и поэтому утекло в глобалы — значит «сам дурак»к клиентскому коду, то почему оно не распространяется на код ваших объектов?
Вообще при чем тут это правило?! Алерт я показал для примера. Не важно что там будет alert ещё какой-то метод. Зачем нам их вызывать?
Father.prototype = Grandfather.prototype
и получим вот такое «прекрасное наследованние»…Father = function (){};
Grandfather = function (){};
Grandfather.prototype.pewpew = 1;
Father.prototype = Grandfather.prototype;
Father.prototype.pewpew = 2;
console.log(new Father().pewpew, new Grandfather().pewpew); // отлично унаследовал! 2, 2
В этом примере вы изменив дочерний объект, изменили и родительский (точнее не объекты-экземпляры, а объекты-прототипы).
А вызывать конструкторы очень даже полезно. Во все примерах в этой теме конструкторы не делали ничего полезного и просто устанавливали свойства, так что толку от них действительно мало.
Но что если мне нужен конструктор, который действительно что-то делает? Например, устанавливает свойства в зависимости от аргументов, сравнивая их с чем-нибудь, или устанавливает какие-то события, или делает еще что-нибудь.
А вызывать конструкторы очень даже полезно. Во все примерах в этой теме конструкторы не делали ничего полезного и просто устанавливали свойства, так что толку от них действительно мало.
Но что если мне нужен конструктор, который действительно что-то делает? Например, устанавливает свойства в зависимости от аргументов, сравнивая их с чем-нибудь, или устанавливает какие-то события, или делает еще что-нибудь.
>В этом примере вы изменив дочерний объект, изменили и родительский
Я лишь показал, то что вы предлагали.
Вызывать конструктор или нет — должен решать разработчик. Во всех ЯП при оверрайде метода старый метод убивается и только тот, кто переписывает этот метод решает стоит ли его вызывать в новом методе.
Я лишь показал, то что вы предлагали.
Вызывать конструктор или нет — должен решать разработчик. Во всех ЯП при оверрайде метода старый метод убивается и только тот, кто переписывает этот метод решает стоит ли его вызывать в новом методе.
UFO just landed and posted this here
Во всех языках, основанных на прототипах (Self, JavaScript), ссылка __proto__ скрытая, но Mozilla её отрыла. Использование __proto__ ломает всю концепцию и выглядит странно, поэтому хотят убрать.
Взамен дают Object.isPrototypeOf и Object.getPrototypeOf
Тут проблема описана подробнее: What is the difference between __proto__ and prototype
Взамен дают Object.isPrototypeOf и Object.getPrototypeOf
Тут проблема описана подробнее: What is the difference between __proto__ and prototype
А на сколько существенны недостатки классического прототипирования в программировании? Нужно побольше памяти, чтоб полностью клонировать объекты, но зато быстрее работать будет. А что касается невозможности делегирования свойств и логики – это было бы неудобным, как думаете?
но зато быстрее работать будетНапример, Хром оптимизирует рыскание по цепочке прототипов и каждый раз прозрачно создает новый объект (вобщем все свойства косвенно own). Раз это кэш, то расходуется дополнительная память, зато снижается время. Сейчас все оптимизации «по времени», так что это нормально (контр оптимизация — «по памяти»).
Плюсы прототипного наследования в том, что «создатель копии может менять её, не опасаясь побочных эффектов среди других потомков», впрочем, это является её минусом — «модификация прототипа не влечёт за собой немедленное и автоматическое изменение всех его потомков»
Извиняюсь, что не по теме Javascript. А если вручную и по необходимости выполнять обновление потомков или сделать признак у объектов, брать ли автоматом обновления предка? Например, как с обновлением программного обеспечения, хочешь — обновляйся :) Применимо ли это на уровне программного кода?
Можно все свойства «предков» перекрыть собственными свойствами, приравненными к undefined — получим фактически скрытие прототипа, разве что hasOwnProperty будет выдавать.
А на сколько существенны недостатки классического прототипирования в программировании?
Простите, а что вы в данном случае имеете в виду?
Не понятно, почему используется много раз слово «заблуждение»?
Как будто на каждом углу встречаются статьи рассказывающие о противоположном, а вы один глаза людям открываете.
Как будто на каждом углу встречаются статьи рассказывающие о противоположном, а вы один глаза людям открываете.
А вот почему все стараются избежать __proto__, в то время как это — то же самое, что .constructor.prototype? От которого не убежишь, а делает выражение только длиннее. Это вопрос. Может быть, кто-то знает ответ?
UFO just landed and posted this here
Ну вот частично ответ нашёл по ссылке выше — What is the difference between __proto__ and prototype, но не убедило. Автор говорит в самом конце, что __proto__ и prototype вместе с ним нехорошо по причине:
Видимо, при анонимных конструкторах какие-то проблемы?
Пробуем сделать анонимный конструктор.
abc = new ( function(){} )();
Сделаем ему прототип.
abc.constructor.prototype.c =115;
Смотрим прототип через объект.
console.log(abc.constructor.prototype, abc.constructor);
Всё видим, и конструктор (анонимную функцию), и прототип, присвоенный через потомка.
Или что имел автор в виду, говоря, что прототипы без конструкторов не работают? Нельзя сразу вписать abc.prototype?
> Возможно по той причине, что __proto__ можно поменять неосознанно…
--Но и .constructor.prototype можно поменять неосознанно. Нет, тут имеют какой-то смысл процитированные рассуждения автора выше.
The original idea was that you could get the kind of thing that __proto__ referred to by taking an object and getting .constructor.prototype. But .constructor itself succumbs to prototypal inheritance, so unless the object's constructor set itself up to be that value, that wouldn't work. And nobody did that, so it's mostly broken.. Что можно перевести как "[Проблема в том, что] .constructor сам становится «жертвой» прототипного наследования: пока конструкторы не будут определены, это не работает.
Видимо, при анонимных конструкторах какие-то проблемы?
Пробуем сделать анонимный конструктор.
abc = new ( function(){} )();
Сделаем ему прототип.
abc.constructor.prototype.c =115;
Смотрим прототип через объект.
console.log(abc.constructor.prototype, abc.constructor);
Всё видим, и конструктор (анонимную функцию), и прототип, присвоенный через потомка.
Или что имел автор в виду, говоря, что прототипы без конструкторов не работают? Нельзя сразу вписать abc.prototype?
> Возможно по той причине, что __proto__ можно поменять неосознанно…
--Но и .constructor.prototype можно поменять неосознанно. Нет, тут имеют какой-то смысл процитированные рассуждения автора выше.
Возможно по той причине, что __proto__ можно поменять неосознанно. Читал я где-то такое. Шанс невелик, но есть.
Все-таки разница в них есть
delete Object.prototype.pewpew1;
delete Object.prototype.pewpew2;
var a = {},
b = {};
/* 1 */ console.log(a.__proto__ === Object.prototype); // true
a.__proto__ = {"pewpew2": 1};
b.constructor.prototype = {"pewpew1": 1};
/* 2 */ console.log(b.pewpew1); // undefined - мы не переписали прототип
b.constructor.prototype.pewpew1 = 1;
/* 3 */ console.log(b.pewpew1); // 1
/* 4 */ console.log(({}).pewpew1); // 1
/* 5 */ console.log(({}).pewpew2); // undefined
/* 6 */ console.log(b.constructor.prototype === Object.prototype); // true
/* 7 */ console.log(a.__proto__ === Object.prototype); // false
Наверное, это тот случай, когда не определён конструктор объекта Object. Хотя он показывает какую-то нативную функцию, но даёт неверное первое равенство. Остальное — следствия.
Интересно, есть ли другие регулярные случаи ошибок? Например, если вместо {} ставишь new function(){this.a = 118;} или function(){}, то всё в порядке.
А var a = new Object(3), b = new Object(4); даёт ещё более интересные эффекты, однако есть сомнения, что это изъян идеологии, а не кривость реализации конкретно базового объекта Object, который давно уже советуют никогда не использовать с параметром, а теперь видно, что и для перегрузки свойств его не нужно использовать.
Интересно, есть ли другие регулярные случаи ошибок? Например, если вместо {} ставишь new function(){this.a = 118;} или function(){}, то всё в порядке.
А var a = new Object(3), b = new Object(4); даёт ещё более интересные эффекты, однако есть сомнения, что это изъян идеологии, а не кривость реализации конкретно базового объекта Object, который давно уже советуют никогда не использовать с параметром, а теперь видно, что и для перегрузки свойств его не нужно использовать.
/*1*/ — переусложнённое равенство. Чуть упростив, получаем абсолютно очевидное:
Далее, видим, где происходит сбой (не записывается прототип Qbject === Object.prototype):
А здесь — нет:
Можно догадаться, что связано с тем, что неявно вызывается конструктор того же Object и что-то мешает записать прототип на нативном уровне. Реализация через __proto__ проходит без сбоев, а через .constructor.prototype — сбоит. Что говорит об этом история? Действительно сбой или «так и должно быть»?
/* 1 */ console.log(a.constructor === Object); // true
Далее, видим, где происходит сбой (не записывается прототип Qbject === Object.prototype):
b.constructor.prototype = {f: 4};
А здесь — нет:
b.constructor.prototype.d = 5;
Можно догадаться, что связано с тем, что неявно вызывается конструктор того же Object и что-то мешает записать прототип на нативном уровне. Реализация через __proto__ проходит без сбоев, а через .constructor.prototype — сбоит. Что говорит об этом история? Действительно сбой или «так и должно быть»?
Да так и должно быть. Посмотрим что же нам говорит
Все меняет флаг writable, который запрещает перезапись (подвержены все прототипы базовых классов), но подмешать свойство мы можем, ибо оба объекта Extensible.
Object.getOwnPropertyDescriptor
:Object.getOwnPropertyDescriptor(Object, "prototype");
// {"writable":false, "enumerable":false, "configurable":false}
Object.getOwnPropertyDescriptor({}, "__proto__");
// {"writable":true, "enumerable":false, "configurable":false}
// Хотя оба они
Object.isExtensible(({}).__proto__); // true
Object.isExtensible(Object.prototype); // true
Все меняет флаг writable, который запрещает перезапись (подвержены все прототипы базовых классов), но подмешать свойство мы можем, ибо оба объекта Extensible.
Автору стоило бы начать с того или хотя бы упомянуть, что в Javascript-е есть и чистое прототипное наследование без конструкторов:
a = {x: 1}
b = Object.create(a) // a.x === 1, b.x === 1
a.x = 2 // a.x === 2, b.x === 2
b.x = 3 // a.x === 2, b.x === 3
c = Object.create(b) // a.x === 2, b.x === 3, c.x === 3
// c.__proto__ == b, b.__proto__ = a, a.__proto__ = Object.prototype
После этого видно, что конструкторы костыль
UFO just landed and posted this here
Да и до сих пор не поддерживается оперой… И создает объекты всреднем в 2 раза медленнее, чем new пруф.
@Suor, оцените потенциальную аудиторию статьи нужна ли им ещё одна заморочка?
@Suor, оцените потенциальную аудиторию статьи нужна ли им ещё одна заморочка?
Это не заморочка, это как раз простейший вариант прототипного наследования. Заморочки с конструкторами и new как раз выглядят странно на фоне такой стройной концепции. Начало статьи страдает от этого — пример с дедом, отцом и сыном непонятен и не совсем верен.
> И вдруг Дед решил: «надоело мне ходить зеленым — хочу стать сними», смутировал (изменил прототип своего класса)
Какого класса? В js нет классов. И, вообще, мы тут про прототипное наследование говорим, вроде )
Стройно так — дед меняет себя и так как он прототип отца, а отец прототип сына, то эти двое тоже меняются и т.д.
> И вдруг Дед решил: «надоело мне ходить зеленым — хочу стать сними», смутировал (изменил прототип своего класса)
Какого класса? В js нет классов. И, вообще, мы тут про прототипное наследование говорим, вроде )
Стройно так — дед меняет себя и так как он прототип отца, а отец прототип сына, то эти двое тоже меняются и т.д.
> Какого класса?
Ни я ли писал про это в самом начале статьи?! :) Да, забыл поставить «класса» в кавычки.
> И, вообще, мы тут про прототипное наследование говорим, вроде
В статье я говорю о «прототипном делегирующем наследовании». В вашей ветке обсуждения о «прототипном наследовании» две разные вещи
Ни я ли писал про это в самом начале статьи?! :) Да, забыл поставить «класса» в кавычки.
> И, вообще, мы тут про прототипное наследование говорим, вроде
В статье я говорю о «прототипном делегирующем наследовании». В вашей ветке обсуждения о «прототипном наследовании» две разные вещи
И ещё, особо смущающая вещь:
function F(){}
F.prototype = {x: 1}; // прототип F здесь не присваивается
Вторая строчка не присваивает прототип объекта F (функции). Она означает какой прототип будет у объектов созданных с помощью new F().Почему не присваивается? Проверяем:
function F(){}
F.prototype = {x: 1};
console.log("F.prototype: ", F.prototype); //F.prototype: Object(x:1) (Хром, FF)
console.log((new F()).x); //1, тоже верно
Не хватает иллюстраций, типа:
А так +100500. Наследование понял именно благодаря вам.
А так +100500. Наследование понял именно благодаря вам.
Sign up to leave a comment.
Основы и заблуждения насчет JavaScript