Страницы

Поиск по вопросам

Показаны сообщения с ярлыком prototype. Показать все сообщения
Показаны сообщения с ярлыком prototype. Показать все сообщения

вторник, 25 февраля 2020 г.

Чем отличается код javascript?

#prototype #javascript


Объясните пожалуйста, кто знает, чем отличается этот код:
function Button() {
  this.cc = 0;
  this.clicked = function() {
    this.cc+=1;
    console.log('Clicked ' + this.cc);
  }
}

От вот этого:
 function Button() {
      this.cc = 0;
    }
    Button.prototype.clicked = function() {
      this.cc+=1;
      console.log('Clicked ' + this.cc);
    }
    


Ответы

Ответ 1



Внешне - ничем не отличает. Но внутренне, для случая: function Button() { this.cc = 0; this.clicked = function() { cc+=1; console.log('Clicked ' + this.cc); } } (new Button()).clicked != (new Button()).clicked И это реально разные ( почти всегда полностью ) функции, с разным контекстом исполнения, и т.д и т.п... P.S: В принципи, это можно назвать private свойствами: function Button(){ var obj_private = {}, obj_fn = [], obj = this; for( var fn in this ){ if ( typeof this[ fn ] === 'function' ){ obj_fn.push( this[ fn ] ); } } this.get_private = function (){ if ( ( this == obj ) && ( obj_fn.indexOf( arguments.caller.callee ) != -1 ) ){ return private_obj; } } } Button.prototype.clicked = function (){ var pr = this.get_private(); pr[ cc ]++; console.log( 'Clicked' + pr[ cc ] ); }

Ответ 2



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

пятница, 24 января 2020 г.

добавление функционала в Javascript (поиск в массиве), не поддерживаемого IE

#javascript #функции #internet_explorer #prototype #совместимость


Приветствую

Использую для поиска в массиве метод Array.prototype.find()

var dialogNode = xml.find(function(element){
    var objectParams = myGetTagParams(element.nodeName);

    return (objectParams.type == 'my:dialog');
});


но в IE этот функционал не поддерживается :( (а IE используют некоторые пользователи
сайта)

поэтому для них пришлось переписать код

var dialogNode = '';

for (var index in xml)
{
    var element = xml[index];

    var objectParams = myGetTagParams(element.nodeName);

    if (objectParams.type == 'my:dialog')
    {
        dialogNode = element;
        break;
    }
}


Это не очень удобно, когда в разных частях сайта используются конструкции типа "если
у пользователя один браузер - выполняй это, а если другой - выполняй это"

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

if (Array.prototype.find == undefined)
    Array.prototype.find = function(){}


?

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


Ответы

Ответ 1



Кажется разобрался! Главное - если прототипа не существовало, приходится внедрять его несколько иным путём, чем показано в приведённых в вопросе примерах, а именно использовать Object.defineProperty: if (!Array.prototype.find) { Object.defineProperty(Array.prototype, "find", { value: function(predicate) { if (this == null) { throw new TypeError('Array.prototype.find called on null or undefined'); } if (typeof predicate !== 'function') { throw new TypeError('predicate must be a function'); } var list = Object(this); var length = list.length >>> 0; var thisArg = arguments[1]; var value; for (var i = 0; i < length; i++) { value = list[i]; if (predicate.call(thisArg, value, i, list)) { return value; } } return undefined; }, }); }

понедельник, 6 января 2020 г.

Добавление метода к объекту через прототип

#javascript #prototype


Приведённый ниже код должен назначить объекту типа Image с помощью прототипа три
новых метода: protocol(), host() и pathname().
В браузере FireFox всё проходит нормально. Chrome выдаёт следующую ошибку:


  Uncaught TypeError: document.i1.protocol is not a function


Соответственно, и остальные функции document.i1.host и document.i1.path не выполняются.  

В чём заключается проблема, и как её решить?

function pr() {
    a = this.src.split(':');
    return a[0] + ':';
}

function ho() {
    a = this.src.split(':');
    path = a[1].split('/');
    return path[2];
}

function pa() {
    path = this.src.split('/');
    path[0] = '';
    path[2] = '';
    return path.join('/').split('///').join('/');
}

Image.prototype.protocol = pr;
Image.prototype.host = ho;
Image.prototype.pathname = pa;

document.write("
"); document.write(document.i1.src + "
"); document.write(document.i1.protocol() + "
"); document.write(document.i1.host() + "
"); document.write(document.i1.pathname() + "
");


Ответы

Ответ 1



Полученный (например, с помощью document.getElementById('img') или new Image()) имеет тип HTMLImageElement. Соответственно, добавлять новые методы нужно именно в его прототип. Пример: function log(info) { document.body.innerHTML += info + "
"; } HTMLImageElement.prototype.protocol = function() { log("Protocol is called"); }; var img = document.getElementById('img'); log("Got image: " + img.constructor.name); img.protocol(); var newImage = new Image(); log("New image: " + newImage.constructor.name); newImage.protocol();


Ответ 2



function pr() { a = this.src.split(':'); return a[0] + ':'; } function ho() { a = this.src.split(':'); path = a[1].split('/'); return path[2]; } function pa() { path = this.src.split('/'); path[0] = ''; path[2] = ''; return path.join('/').split('///').join('/'); } Image.prototype.protocol = pr; Image.prototype.host = ho; Image.prototype.pathname = pa; var out = document.getElementById("out"), i1, html; out.innerHTML = "
"; i1 = document.getElementById('i1'); html = out.innerHTML; html += "src: " + i1.src + "
"; html += "protocol: " + i1.protocol() + "
"; html += "host: " + i1.host() + "
"; html += "pathname: " + i1.pathname() + "
"; out.innerHTML = html;
Вероятно, вы несколько раз выполнили скрипт на странице, и появилось несколько элементов с именем "i1", поэтому в последующи разы нужно было бы обращаться к document.i1[0].protocol(). Лучше сделать так: document.write("
"); var i1 = document.getElementById('i1'); document.write(i1.src+"
"); document.write(i1.protocol()+"
"); ...

Ответ 3



Вот рабочий вариант. Спасибо @Regent и @Sergiks и @Grundy
При открытии страницы все работает корректно, выводиться вся необходимая информация, не зависимо от браузера. Got image: HTMLImageElement http: localhost /JavaScriptLearning/img1.jpg

пятница, 13 декабря 2019 г.

Стоит ли функцию как-либо называть или оставить анонимной?

#javascript #функции #dom #prototype


Есть такой код:

if (!Element.prototype.remove) {
  Element.prototype.remove = function remove() {   // <----
    if (this.parentNode) {
      this.parentNode.removeChild(this);
    }
  };
}


Это обычный полифилл для .remove()

Меня интересует вопрос - есть ли какой-либо смысл называть как-то функцию вот тут:

Element.prototype.remove = function remove() { ... }

или в этом смысла нет, и можно её оставить анонимной?

Element.prototype.remove = function() { ... }
    


Ответы

Ответ 1



Имеет. При отладке эта функция будет выводиться в консоль в том виде, в каком она была в коде, поэтому в случае функции с именем будет сразу ясно, что это за функция. А ещё в ES6 вводится свойство name, которое в случае анонимной функции оказывается пустым. function Smth() {} Smth.prototype.doSmth = function doSmth() {} Smth.prototype.doOther = function () {} var x = new Smth(); console.log(x.doSmth); console.log(x.doOther); console.log(x.doSmth.name); console.log(x.doOther.name);

Ответ 2



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

Ответ 3



Если в теле функции нет необходимости в доступе к самой себе, то смело можно использовать анонимный вариант наименования. Подробнее тут: Именованные функциональные выражения

понедельник, 25 ноября 2019 г.

Прототипное наследование


Добрый День. Изучаю способы организации наследования в JavaScript и написал небольшой пример : 

  function Foo(name) {
    this.name = name;
}

Foo.prototype.myName = function() {
    return this.name;
};

function Bar(name, label) {
    Foo.call(this, name);
    this.label = label;
}

Bar.prototype = Foo.prototype;

Bar.prototype.myLabel = function() {
    return this.label;
};

var a = new Bar("a", "obj a");

a.myName();
a.myLabel();


Вопрос возник на строке :

Bar.prototype = Foo.prototype;


Пытаясь понять разницу между 

Bar.prototype = new Foo()


и

Bar.prototype = Foo.prototype;


набрел на статью, в которой говориться 


  Bar.prototype = Foo.prototype doesn't create a new object for Bar.prototype t
be linked to. It just makes Bar.prototype be another reference to Foo.prototype, whic
effectively links Bar directly to the same object as Foo links to: Foo.prototype. This means when you start assigning, like Bar.prototype.myLabel = ..., you're modifying not a separate object but the shared Foo.prototype object itself, which would affect any objects linked to Foo.prototype.


Вопрос заключается в последнем предложении. Почему при добавлении прототипу свойств
Bar, мы автоматически меняем и прототип объекта Foo ? Если я правильно понял, то как раз при добавлении свойства или метода в объект Foo, должен измениться и объект Bar, т.к. он ссылается на прототип Foo. Помогите разобраться пожалуйста.
    


Ответы

Ответ 1



Давайте начнем с отвлеченного примера: var a = { test: 11 } b = a; b.test = 12; console.log(a.test); // Выведет 12! Это происходит потому, что объекты в JS присваиваются и передаются по ссылке а не по значению. Свойство .prototype - это объект. Когда вы выполняете код: Bar.prototype = Foo.prototype; вы присваиваете свойству Bar.prototype ссылку на объект Foo.prototype. Как следствие любое изменение свойства Bar.prototype приводит к изменению Foo.prototype, о чем и говорится в приведнной цитате: This means when you start assigning, like Bar.prototype.myLabel = ..., you're modifyin not a separate object but the shared Foo.prototype object itself, which would affect any objects linked to Foo.prototype. Небольшое лирическое отступление. Вообще говоря, я бы рекомендовал вам никогда не использовать конструкцию: Bar.prototype = new Foo(); а всех тех, кто вам это советует -- смело отправляйте учить основы JS. Вся соль том, что вызывая new Foo() вы вызываете конструктор объекта. При этом сам конструктор может с одной стороны накладывать ограничения на передаваемые аргументы, а с другой иметь побочные действия. Разберем каждый из этих случаев отдельно. Предположим, у вас есть вот такой конструктор, накладывающий ограничения на сво аргументы: Foo = function(a) { if (typeof a === 'undefined') { throw new Error('You have to set the first argument.'); } this.a = a; } В этом случае вы уже не можете просто взять и выполнить: Bar.prototype = new Foo(); т.к. вам нужно в явном виде предать аргумент в конструктор, который полностью лише смысла в момент описания иерархии наследования. Самое интересное, что значение параметра a все равно будет затерто при вызове конструктора Foo в дочернем конструкторе Bar. Поэтому конструкция new Foo() еще и лишена смысла. Теперь предположим, что родительский конструктор имеет побочные эффекты: Foo = function(a) { console.log('Here I am!'); } При использовании: Bar.prototype = new Foo(); и дальнейшем: var Bar = function() { Foo.call(this); } строка "Here I am!" будет выведена дважды. Согласитесь, это не всегда желаемое поведение системы. Ну и еще один любопытный факт: даже если сейчас родительский конструктор не имее ни побочных эффектов, ни ограничений на аргументы, это не значит, что он останется таким навсегда. Лучше уж сразу сделать все правильно, чем нервно отлаживать код в поисках ошибки, когда все сломается. Приведу, для справки, правильную реализацию наследования в JS: // Базовый конструктор var Foo = function() { // ... }; Foo.prototype.doSomething = function() { // ... }; // Дочерний конструктор var Bar = function() { // Вызываем базовый конструктор для текущего объекта. Foo.call(this); // ... }; // Устанавливаем правильное значение в цепочке прототипов. Bar.prototype = Object.create(Foo.prototype, { // Выставляем правильную функцию-конструктор для всех создаваемых // объектов. constructor: { value: Bar, enumerable: false, writable: true, configurable: true } }); // Расширяем прототип дочернего "класса". Этот шаг должен идти // СТРОГО ПОСЛЕ установки значения Bar.prototype. Bar.prototype.doAnotherAction = function() { // ... }; В случае, когда вы не можете использовать Object.create (старые браузеры) вы может либо использовать один из существующих полифилов, либо сделать все ручками(через анонимный конструктор): var inherits = function(ctor, superCtor) { // Временный конструктор, который не делает ничего и нужен // только для разрыва прямой связи между прототипами ctor // и superCtor. Его использование позволяет менять прототип // дочернего конструктора, не боясь сломать родительский. var Tmp = function() {}; Tmp.prototype = superCtor.prototype; // Обратите внимание, вызов new Tmp() не имеет АБСОЛЮТНО // никаких побочных эффектов и не накладывает ограничений // на передаваемые значения. ctor.prototype = new Tmp(); // Выставляем правильную функцию-конструктор для всех // создаваемых объектов. ctor.prototype.constructor = ctor; }; С учетом всего выше сказанного универсальная функции наследования может иметь вид: var inherits = (function() { if (typeof Object.create === 'function') { // Используем более простой вариант, если Object.create существует. return function(ctor, superCtor) { ctor.prototype = Object.create(superCtor.prototype, { constructor: { value: ctor, enumerable: false, writable: true, configurable: true } }); }; } // Используем временный конструктор для старых браузеров return function(ctor, superCtor) { var Tmp = function() {}; Tmp.prototype = superCtor.prototype; ctor.prototype = new Tmp(); ctor.prototype.constructor = ctor; }; })(); UPD: В реализациях выше, после присваивания прототипа, задается свойство Function.prototype.constructor Хотя это свойство редко используется на практике (лично я ни разу не видел в production коде), полноценная реализация наследования должна его выставлять.

Ответ 2



Если вы используете ES6, то можно использовать для наследования стандартные средства языка: // Базовый класс class Foo { doSomething() { // ... } } // Дочерний класс, наследующий все поведение базового + // методы определенные ниже. class Bar extends Foo { doAnotherAction() { // ... } }

понедельник, 15 июля 2019 г.

Как происходит перезаписывание конструктора в прототипе

Если создать новый объект с помощью Object.create и указать в нём прототип и сделать его прототипом конструктора, родной конструктор не перезапишется. А если просто объект сделать прототипом конструктора, то он перезапишется. Как это удаление происходит? Не понимаю логику перезаписи конструктора. Или так поставлю вопрос. Почему в первом случае конструктор не перезаписался, а во втором перезаписался?
function Animal() {}
function Rabbit() {} Rabbit.prototype = Object.create(Animal.prototype);
var rabbit = new Rabbit();
alert( rabbit instanceof Rabbit ); выдаст true
Rabbit.prototype = {};
alert( rabbit instanceof Rabbit ); выдаст false!


Ответ

При выполнении оператора instanceof идет последовательная проверка прототипов
И так как перед второй проверкой прототип в Rabbit изменился, следующая проверка возвращает false
Фактически вызов этого оператора можно описать следующим циклом:
function instanceOf(object, Proto) var proto = Proto.prototype; while(true){ var objectProto = Object.getPrototypeOf(object); if(objectProto == null) return false; if(Object.is(objectProto, proto)) return true; } }
Таким образом видно, что после смены прототипа у Rabbit, этот цикл продолжается пока полученный прототип не будет null и оператор не вернет false

воскресенье, 7 июля 2019 г.

Не получается изменить __proto__ у Object.prototype

В MDN говорится
Обратите внимание, что свойство __proto__ может быть переопределено даже у объекта Object.prototype, если новая цепочка заканчивается null
Однако, при попытке выполнить код из примера
var b = {}; Object.prototype.__proto__ = Object.create(null, // [[Prototype]] { hi: { value: function() { alert('hi'); } } }); b.hi();
я получаю ошибку в хроме (версия 63.0.3239.132 x64)
Uncaught TypeError: Immutable prototype object '#' cannot have their prototype set at Object.set __proto__ [as __proto__] () at js:15
и в Firefox'е (версия 58.0 x64)
TypeError: can't set prototype of this object
А вот в IE11 всё работает.
Почему так происходит и можно ли всё-таки изменить этот прототип?


Ответ

Последняя на данный момент спецификация говорит, что
The Object prototype object is the intrinsic object %ObjectPrototype%. The Object prototype object is an immutable prototype exotic object

Это нововведение ES7 (aka ES2016). Встроенный прототип объекта Object.prototype на данный момент Immutable Prototype Exotic Objects у которого внутренний слот [[prototype]] заблокирован.
Предполагается, что это предотвратит вставку посредника в глобальный механизм поиска, см. этот коммит для более детальной информации. Это реализовано в V8
перевод ответа @Bergi

Небольшое пояснение можно найти по ссылкам из issue. В примере
foo,bar,123


В этом примере был вывод: "data=foo" и "data=bar". Позволило бы изменить текущей странице значения используемые в загружаемом скрипте.

пятница, 14 июня 2019 г.

Javascript. Что происходит в этом куске кода?

Первый раз вижу такой код.. не пойму, что тут происходит?
var DemoAppModel = (function (_super) { __extends(DemoAppModel, _super);
function DemoAppModel() { _super.call(this); }
DemoAppModel.prototype.auth = function () {}
return DemoAppModel; })(observable.Observable);
exports.DemoAppModel = DemoAppModel; exports.mainViewModel = new DemoAppModel();
Где про это можно прочитать?


Ответ

TL;DR: Это полифил для реализация наследования в версиях до ES6.

До преобразования транспилятором (скорее всего речь идет о TypeScript) этот код (как верно отметил @Pavel Mayorov) имел вид:
export class DemoAppModel extends observable.Observable { auth() { } } export var mainViewModel = new DemoAppModel();
Давайте разберемся что-здесь происходит строчка за строчкой:
// Создаем IIFE(*1), возвращающую конструктор класса DemoAppModel // Аргумент "_super" - конструктор родительского класса. var DemoAppModel = (function (_super) { // Выставляем правильную цепочку прототипов для вновь созданного // конструктора(*2). // Обратите внимание, "DemoAppModel" здесь - это функция определенная // строчкой ниже, а не функция из предыдущей области видимости(*3). __extends(DemoAppModel, _super);
// Определяем нашу функцию конструктор. function DemoAppModel() { // Вызываем родительский конструктор в контексте вновь созданного // экземпляра DemoAppModel. Это необходимо для правильной // инициализации переменных экземпляра, определенных через // родительский конструктор. _super.call(this); }
// Создаем новый метод для экземпляров DemoAppModel, который ничего // не делает. DemoAppModel.prototype.auth = function () {}
// Возвращаем созданный конструктор. return DemoAppModel; // Вызываем IIFE(*1) передавая конструктор родительского класса в качестве // аргумента. })(observable.Observable);
// Экспорт функции конструктора и созданного с его помощью экземпляра // для использования в других модулях. exports.DemoAppModel = DemoAppModel; exports.mainViewModel = new DemoAppModel();
Замечания:
IIEF(Immediately-invoked function expression) - функция, которая вызывается сразу же, как только она определена. В общем случае, функция __extends имеет вид (без полифила для ES<5):
function __extends(ctor, superCtor) { ctor.prototype = Object.create(superCtor.prototype, { constructor: { value: ctor, enumerable: false, writable: true, configurable: true } }); };
Что именно здесь происходит несколько выходит за рамки вопроса и требует отдельного дополнительного обсуждения. В JavaScript мы можем использовать функции до момента их определения благодаря "Поднятию определения".
Некоторое время назад я уже рассказывал, как именно работает прототипное наследование в JavaScript вот в этом ответе. Там есть чуть больше деталей о самой логике реализации прототипного наследования.

среда, 15 мая 2019 г.

Чем отличается код javascript?

Объясните пожалуйста, кто знает, чем отличается этот код: function Button() { this.cc = 0; this.clicked = function() { this.cc+=1; console.log('Clicked ' + this.cc); } } От вот этого: function Button() { this.cc = 0; } Button.prototype.clicked = function() { this.cc+=1; console.log('Clicked ' + this.cc); }


Ответ

Внешне - ничем не отличает. Но внутренне, для случая: function Button() { this.cc = 0; this.clicked = function() { cc+=1; console.log('Clicked ' + this.cc); } } (new Button()).clicked != (new Button()).clicked И это реально разные ( почти всегда полностью ) функции, с разным контекстом исполнения, и т.д и т.п... P.S: В принципи, это можно назвать private свойствами: function Button(){ var obj_private = {}, obj_fn = [], obj = this; for( var fn in this ){ if ( typeof this[ fn ] === 'function' ){ obj_fn.push( this[ fn ] ); } }
this.get_private = function (){ if ( ( this == obj ) && ( obj_fn.indexOf( arguments.caller.callee ) != -1 ) ){ return private_obj; } } }
Button.prototype.clicked = function (){ var pr = this.get_private(); pr[ cc ]++; console.log( 'Clicked' + pr[ cc ] ); }

четверг, 14 февраля 2019 г.

Добавление метода к объекту через прототип

Приведённый ниже код должен назначить объекту типа Image с помощью прототипа три новых метода: protocol(), host() и pathname() В браузере FireFox всё проходит нормально. Chrome выдаёт следующую ошибку:
Uncaught TypeError: document.i1.protocol is not a function
Соответственно, и остальные функции document.i1.host и document.i1.path не выполняются.
В чём заключается проблема, и как её решить?
function pr() { a = this.src.split(':'); return a[0] + ':'; }
function ho() { a = this.src.split(':'); path = a[1].split('/'); return path[2]; }
function pa() { path = this.src.split('/'); path[0] = ''; path[2] = ''; return path.join('/').split('///').join('/'); }
Image.prototype.protocol = pr; Image.prototype.host = ho; Image.prototype.pathname = pa;
document.write("
"); document.write(document.i1.src + "
"); document.write(document.i1.protocol() + "
"); document.write(document.i1.host() + "
"); document.write(document.i1.pathname() + "
");


Ответ

Полученный (например, с помощью document.getElementById('img') или new Image()) имеет тип HTMLImageElement. Соответственно, добавлять новые методы нужно именно в его прототип.
Пример:
function log(info) { document.body.innerHTML += info + "
"; } HTMLImageElement.prototype.protocol = function() { log("Protocol is called"); }; var img = document.getElementById('img'); log("Got image: " + img.constructor.name); img.protocol(); var newImage = new Image(); log("New image: " + newImage.constructor.name); newImage.protocol();

пятница, 19 октября 2018 г.

Стоит ли функцию как-либо называть или оставить анонимной?

Есть такой код:
if (!Element.prototype.remove) { Element.prototype.remove = function remove() { // <---- if (this.parentNode) { this.parentNode.removeChild(this); } }; }
Это обычный полифилл для .remove()
Меня интересует вопрос - есть ли какой-либо смысл называть как-то функцию вот тут:
Element.prototype.remove = function remove() { ... }
или в этом смысла нет, и можно её оставить анонимной?
Element.prototype.remove = function() { ... }


Ответ

Имеет. При отладке эта функция будет выводиться в консоль в том виде, в каком она была в коде, поэтому в случае функции с именем будет сразу ясно, что это за функция. А ещё в ES6 вводится свойство name, которое в случае анонимной функции оказывается пустым.
function Smth() {} Smth.prototype.doSmth = function doSmth() {} Smth.prototype.doOther = function () {} var x = new Smth(); console.log(x.doSmth); console.log(x.doOther); console.log(x.doSmth.name); console.log(x.doOther.name);

вторник, 2 октября 2018 г.

Прототипное наследование

Добрый День. Изучаю способы организации наследования в JavaScript и написал небольшой пример :
function Foo(name) { this.name = name; }
Foo.prototype.myName = function() { return this.name; };
function Bar(name, label) { Foo.call(this, name); this.label = label; }
Bar.prototype = Foo.prototype;
Bar.prototype.myLabel = function() { return this.label; };
var a = new Bar("a", "obj a");
a.myName(); a.myLabel();
Вопрос возник на строке :
Bar.prototype = Foo.prototype;
Пытаясь понять разницу между
Bar.prototype = new Foo()
и
Bar.prototype = Foo.prototype;
набрел на статью, в которой говориться
Bar.prototype = Foo.prototype doesn't create a new object for Bar.prototype to be linked to. It just makes Bar.prototype be another reference to Foo.prototype, which effectively links Bar directly to the same object as Foo links to: Foo.prototype. This means when you start assigning, like Bar.prototype.myLabel = ..., you're modifying not a separate object but the shared Foo.prototype object itself, which would affect any objects linked to Foo.prototype.
Вопрос заключается в последнем предложении. Почему при добавлении прототипу свойства Bar, мы автоматически меняем и прототип объекта Foo ? Если я правильно понял, то как раз при добавлении свойства или метода в объект Foo, должен измениться и объект Bar, т.к. он ссылается на прототип Foo. Помогите разобраться пожалуйста.


Ответ

Давайте начнем с отвлеченного примера:
var a = {test: 11} b = a;
b.test = 12; console.log(a.test); // Выведет 12!
Это происходит потому, что объекты в JS присваиваются и передаются по ссылке а не по значению.
Свойство .prototype - это объект. Когда вы выполняете код:
Bar.prototype = Foo.prototype;
вы присваиваете свойству Bar.prototype ссылку на объект Foo.prototype. Как следствие, любое изменение свойства Bar.prototype приводит к изменению Foo.prototype, о чем и говорится в приведнной цитате:
This means when you start assigning, like Bar.prototype.myLabel = ..., you're modifying not a separate object but the shared Foo.prototype object itself, which would affect any objects linked to Foo.prototype.

Небольшое лирическое отступление
Вообще говоря, я бы рекомендовал вам никогда не использовать конструкцию:
Bar.prototype = new Foo();
а всех тех, кто вам это советует -- смело отправляйте учить основы JS. Вся соль в том, что вызывая new Foo() вы вызываете конструктор объекта. При этом сам конструктор может с одной стороны накладывать ограничения на передаваемые аргументы, а с другой иметь побочные действия. Разберем каждый из этих случаев отдельно.
Предположим, у вас есть вот такой конструктор, накладывающий ограничения на свои аргументы:
Foo = function(a) { if (typeof a === 'undefined') { throw new Error('You have to set the first argument.'); }
this.a = a; }
В этом случае вы уже не можете просто взять и выполнить:
Bar.prototype = new Foo();
т.к. вам нужно в явном виде предать аргумент в конструктор, который полностью лишен смысла в момент описания иерархии наследования. Самое интересное, что значение параметра a все равно будет затерто при вызове конструктора Foo в дочернем конструкторе Bar. Поэтому конструкция new Foo() еще и лишена смысла.
Теперь предположим, что родительский конструктор имеет побочные эффекты:
Foo = function(a) { console.log('Here I am!'); }
При использовании:
Bar.prototype = new Foo();
и дальнейшем:
var Bar = function() { Foo.call(this); }
строка "Here I am!" будет выведена даважды. Согласитесь, это не всегда желаемое поведение системы.
Ну и еще один любопытный факт: даже если в сейчас родительский конструктор не имеет ни побочных эффектов ни ограничений на аргументы, это не значит, что он останется таким навсегда. Лучше уж сразу сделать все правильно, чем нервно отлаживать код в поисках ошибки, когда все сломается.

Приведу, для справки, правильную реализацию наследования в JS:
// Базовый конструктор var Foo = function() { // ... };
Foo.prototype.doSomething = function() { // ... };
// Дочерний конструктор var Bar = function() { // Вызываем базовый конструктор для текущего объекта. Foo.call(this); // ... };
// Устанавливаем правильное значение в цепочке прототипов. Bar.prototype = Object.create(Foo.prototype, { // Выставляем правильную функцию-конструктор для всех создаваемых // объектов. constructor: { value: Bar, enumerable: false, writable: true, configurable: true } });
// Расширяем прототип дочернего "класса". Этот шаг должен идти // СТРОГО ПОСЛЕ установки значения Bar.prototype. Bar.prototype.doAnotherAction = function() { // ... };
В случае, когда вы не можете использовать Object.create (старые барузеры) вы можете либо использовать один из существующих полифилов, либо сделать все ручками(через анонимный конструктор):
var inherits = function(ctor, superCtor) { // Временный конструктор, который не делает ничего и нужен // только для разрыва прямой связи между прототипами ctor // и superCtor. Его использование позволяет менять прототип // дочернего конструктора, не боясь сломать родительский. var Tmp = function() {}; Tmp.prototype = superCtor.prototype;
// Обратите внимание, вызов new Tmp() не имеет АБСОЛЮТНО // никаких побочных эффектов и не накладывает ограничений // на передаваемые значения. ctor.prototype = new Tmp(); // Выставляем правильную функцию-конструктор для всех // создаваемых объектов. ctor.prototype.constructor = ctor; };

С учетом всего выше сказанного универсальная функции наследования может иметь вид:
var inherits = (function() { if (typeof Object.create === 'function') { // Используем более простой вариант, если Object.create существует. return function(ctor, superCtor) { ctor.prototype = Object.create(superCtor.prototype, { constructor: { value: ctor, enumerable: false, writable: true, configurable: true } }); }; }
// Используем временный конструктор для старых браузеров return function(ctor, superCtor) { var Tmp = function() {}; Tmp.prototype = superCtor.prototype; ctor.prototype = new Tmp(); ctor.prototype.constructor = ctor; }; })();
UPD:
В реализациях выше, после присваивания прототипа, задается свойство Function.prototype.constructor. Хотя это свойство редко используется на практике (лично я ни разу не видел в production коде), полноценная реализация наследования должна его выставлять.