Цепочка прототипов в JavaScript - PullRequest
8 голосов
/ 09 ноября 2010

Я читаю книгу под названием «Шаблоны JavaScript», но есть одна часть, в которой, как мне кажется, парень сбивает с толку.

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

function inherit(C, P) {
C.prototype = P.prototype;
}

Он говорит:

"Это дает вам короткие и быстрые цепочки поиска прототипов, потому что все объекты на самом деле имеют один и тот же прототип. Но это также ОТДЕЛКА, потому что если один ребенок или внук где-то вниз по цепочке наследования изменяет прототип, это влияет на всех родителей и бабушка с дедушкой. "

Однако на самом деле я попытался изменить прототип say () в Child, и это никак не повлияло на Parent, и фактически Child по-прежнему указывал на Parent и полностью игнорировал свой собственный прототип с тем же именем, что имеет смысл, поскольку он указывает на другая позиция памяти. Так как парень может сказать что-то подобное? Ниже доказывается моя точка зрения:

function Parent(){}

Parent.prototype.say = function () {
return 20;
};

function Child(){
}

Child.prototype.say = function () {
return 10;
};

inherit(Child, Parent);

function inherit(C, P) {
C.prototype = P.prototype;
 } 

 var parent = new Parent();
var child = new Child();


var child2 = new Child()
alert(child.say(); //20
alert(parent.say()); //20
alert(child2.say()); //20

Ни один ребенок или внук не может изменить прототип!

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

function inherit(C, P) {
var F = function () {};
F.prototype = P.prototype;
C.prototype = new F();
}

Проблема в том, что при этом выводятся те же точные значения, что и в другом шаблоне:

function Parent(){}

Parent.prototype.say = function () {
return 20;
};

function Child(){
}

 Child.prototype.say = function () {
return 10;
};

inherit(Child, Parent);

function inherit(C, P) {
var F = function () {};
F.prototype = P.prototype;
C.prototype = new F();
}

var parent = new Parent();
var child = new Child();


var child2 = new Child()
alert(child.say(); //20
alert(parent.say()); //20
alert(child2.say()); //20

Не имеет смысла, что пустая функция как-то разрывает ссылку. Фактически, Child указывает на F, а F в свою очередь указывает на прототип Родителя. Таким образом, они все еще указывают на одну и ту же позицию памяти. Это продемонстрировано выше, где выводятся те же точные значения, что и в первом примере. Я не имею ни малейшего понятия, что этот автор пытается продемонстрировать и почему он заявляет, что мне это не нужно и что я не могу воспроизвести.

Спасибо за ответ.

Ответы [ 3 ]

8 голосов
/ 09 ноября 2010

Для вас первый пункт:

Что этот парень пытается сказать, так это то, что методы как для дочернего, так и для родительского изменятся, если вы измените прототип после созданных вами экземпляров.

Например:

function inherit(C, P) {
  C.prototype = P.prototype;
} 

function Parent(){}
function Child(){}

inherit(Child, Parent);

Parent.prototype.say = function () {
  return 20;
};

var parent = new Parent();
var child = new Child();


// will alert 20, while the method was set on the parent.
alert( child.say() );

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

// same thing happens here, 
Child.prototype.speak = function() {
  return 40;
};

// will alert 40, while the method was set on the child
alert( parent.speak() );

И околоВаша вторая точка:

function inherit(C, P) {
  var F = function () {};
  F.prototype = P.prototype;
  C.prototype = new F();
}

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

function inherit(C, P) {
  var F = function () {};
  F.prototype = P.prototype;
  C.prototype = new F();
}

function Parent(){}
function Child(){}

inherit(Child, Parent);

// same thing happens here, 
Child.prototype.speak = function() {
  return 40;
};

var parent = new Parent();

// will throw an error, because speak is undefined
alert( parent.speak() );
2 голосов
/ 05 июня 2011

То, что вы можете изменить объект-прототип, указав на него другой прототип, является нормальным поведением JavaScript. Примитивные значения JavaScript неизменны, а объекты и массивы - нет. Я объясню это на простом примере:

var person = {name: 'greg', age: 20};

>>>person.name; //prints 'greg'

>>>person.age; //prints 20

var a = person;

>>>a.name; //prints 'greg'

a.name = 'peter';

>>>a.name; //prints 'peter'

>>>person.name; //prints 'peter'

//I've changed person.name through a.name. That's why objects in JavaScript are called mutable

Массивы ведут себя одинаково:

var arr = ['first', 'second', 'third'],
    newArr = arr;

newArr.pop();

>>>newArr; //prints ['first', 'second']

>>>arr; //prints ['first', 'second']

//Frist array was also changed

Давайте посмотрим на числа строк и логические значения (примитивные типы данных):

var str = 'hello world',

    newStr = str;

>>>str; //prints 'hello world'

>>>newStr; //prints 'hello world'

>>>newStr.toUpperCase(); //prints 'HELLO WORLD'

>>>str; //prints 'hello world'

>>newStr; //prints 'hello world'

//Numbers and booleans have similiar behavior

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

function Parent(){}

Parent.prototype.say = function () {
return 20;
};

function Child(){
}



/**
*
* The area you should examine i've selected below.
*
*/

//Start BUG

//new say method will not affect the Parent.prototype beacuse it wasn't assigned yet
Child.prototype.say = function () {
return 10;
};

//rewrite Child.prototype and all it's methods with Parent.prototype
inherit(Child, Parent);

//End BUG



function inherit(C, P) {
C.prototype = P.prototype;
 } 

var parent = new Parent();
var child = new Child();


var child2 = new Child()
alert(child.say(); //20
alert(parent.say()); //20
alert(child2.say()); //20

Проблема здесь в том, что вместо копирования и изменения прототипа Parent.pro вы создаете новый метод Child.prototype.say, а сразу после него вы переписываете весь объект Child.prototype посредством назначения Parent.prototype. Просто измените их порядок, и он должен работать нормально.

1 голос
/ 09 ноября 2010

Если вы измените прототип после его наследования, вы увидите, что он также меняет прототип для родителя:

function Parent(){}

Parent.prototype.say = function () {
return 20;
};

function Child(){
}

inherit(Child, Parent);

Child.prototype.say = function () {
return 10;
};

function inherit(C, P) {
C.prototype = P.prototype;
 } 

 var parent = new Parent();
var child = new Child();


var child2 = new Child()
alert(child.say()); //10
alert(parent.say()); //10
alert(child2.say()); //10

Если вы используете модифицированную версию функции inherit, то Child иParent прототипы остаются отдельными.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...