Let's start with an abstract example:
var a = {test: 11} b = a; b.test = 12; console.log(a.test); // Выведет 12!
This is because the objects in JS are assigned and passed by reference and not by value.
The <An Object>.prototype
is an object. When you run the code:
Bar.prototype = Foo.prototype;
you assign the Bar.prototype
property a reference to the Foo.prototype
object. As a result, any change in the Bar.prototype
property leads to a change in Foo.prototype
, which is stated in the cited quotation:
This is when you’ve changed it.
A small lyrical digression .
Generally speaking, I would recommend that you never use a construct:
Bar.prototype = new Foo();
and all those who advise you this - feel free to send teach the basics of JS . The whole point is that by calling new Foo()
you call the object's constructor. In this case, the constructor itself may, on the one hand, impose restrictions on the passed arguments, and on the other, have side effects. Let us examine each of these cases separately.
Suppose you have a constructor that imposes restrictions on its arguments:
Foo = function(a) { if (typeof a === 'undefined') { throw new Error('You have to set the first argument.'); } this.a = a; }
In this case, you can no longer just take and run:
Bar.prototype = new Foo();
because You need to explicitly pass the argument to the constructor, which is completely meaningless at the moment of describing the inheritance hierarchy. The most interesting thing is that the value of the a parameter will still be overwritten when the constructor Foo
called in the child constructor Bar
. Therefore, the construction of new Foo()
is also devoid of meaning.
Now suppose the parent constructor has side effects:
Foo = function(a) { console.log('Here I am!'); }
Using:
Bar.prototype = new Foo();
and further:
var Bar = function() { Foo.call(this); }
the string " Here I am!
" will be displayed sometime . Agree, this is not always the desired behavior of the system.
Well, another curious fact: even if at the moment the parent constructor has no side effects or restrictions on the arguments, this does not mean that he will remain so forever . It’s better to do it right away right away than to nervously debug the code in search of an error when everything breaks.
I will give, for reference, the correct implementation of inheritance in 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() { // ... };
In the case when you cannot use Object.create
(old barsers), you can either use one of the existing polyfiles, or make everything handles (via an anonymous constructor):
var inherits = function(ctor, superCtor) { // Временный конструктор, который не делает ничего и нужен // только для разрыва прямой связи между прототипами ctor // и superCtor. Его использование позволяет менять прототип // дочернего конструктора, не боясь сломать родительский. var Tmp = function() {}; Tmp.prototype = superCtor.prototype; // Обратите внимание, вызов new Tmp() не имеет АБСОЛЮТНО // никаких побочных эффектов и не накладывает ограничений // на передаваемые значения. ctor.prototype = new Tmp(); // Выставляем правильную функцию-конструктор для всех // создаваемых объектов. ctor.prototype.constructor = ctor; };
Given all the above, the universal function of inheritance can be:
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:
In the implementations above, after assigning the prototype, the Function.prototype.constructor
property is set. Although this property is rarely used in practice (I personally have never seen it in the production code), a full-fledged implementation of inheritance should expose it.