Парижское наследование в JavaScript

76
7

Наблюдая за лекцией Дугласа Крокфорда по продвинутому JavaScript и он поднимает идею паразитного наследования, которая по существу имеет конструкторы, вызывает другие конструкторы для модификации рассматриваемого объекта. Вот его код:


function gizmo(id, secret) {
secret = secret || {};
secret.id = id;
return {
toString: function () {
return "gizmo " + secret.id;
}
};
}

function hoozit(id) {
var secret = {},
that = gizmo(id, secret);
that.test = function (testid) {
return testid === secret.id;
};
return that;
}

var myHoozit = hoozit(20);
console.log(myHoozit.test(20)); //returns true


Я понимаю код, и здесь нет ничего сложного. Путаница происходит в функции hoozit. Если вы не установили secret = {}, вы не получите возвращаемого a true.


Это непонятно, потому что в функции gizmo вы видите secret = secret || {}, который должен позаботиться об этом для нас... но это не так.


Почему это короткое замыкание работает некорректно (secret = secret || {}) в функции gizmo, когда не передается второй параметр в функции hoozit (прерывается как в Chrome, так и в Firefox)?

спросил(а) 2013-08-04T00:33:00+04:00 6 лет, 11 месяцев назад
1
Решение
93

Почему это короткое замыкание работает некорректно (secret = secret || {}) в функции gizmo, когда не передается второй параметр в функции hoozit (перерывы как в Chrome, так и в Firefox)?



Просто потому, что вы не можете получить доступ к secret внутри that.test, потому что он не существует в этой области:


function hoozit(id) {
var that = gizmo(id);
that.test = function (testid) {
// secret is not defined in this or in any higher scope
// hence you get a refernece error
return testid === secret.id;
};
return that;
}

Единственный существующий объект secret является локальным для функции gizmo.

Если вы определяете его и просто не передаете его на gizmo, то secret = secret || {} будет оцениваться как secret = {}, то есть новый объект создается внутри функции gizmo. Это значение доступно только в функции gizmo и вообще не связано с переменной secret в функции hoozit. Объект secret внутри gizmo отличается от объекта hoozit.


function hoozit(id) {
var secret = {}, // secret object is created here
that = gizmo(id);
that.test = function (testid) {
// you never set `secret.id`, hence the comparison results in `false`
return testid === secret.id;
};
return that;
}

В secret = secret || {} нет ничего плохого, он работает как ожидалось.

ответил(а) 2013-08-04T00:42:00+04:00 6 лет, 11 месяцев назад
66

Посмотрите на более простой пример.


function Foo(baz){
baz = baz || {};
baz.boo = 1;
}

function Bar(baz){
baz = baz || {};
Foo(baz);
return baz;
}


Если мы назовем Bar(), мы передаем объект в Foo. Затем он обманывает объект, устанавливая свойство boo. Классно!


Теперь скажем, что наша функция Bar выглядит так:


function Bar(baz){
baz = baz || {};
Foo();
return baz;
}

Единственное отличие состоит в том, что мы не передаем объект Foo. Из-за этого Foo создает объект внутри своей области. Мы устанавливаем свойство на этом новом объекте, а затем заканчиваем работу. Объект не является родительской областью Bar, поэтому Bar никогда не знает, что этот объект создан и не имеет доступа к нему. В течение следующих нескольких миллисекунд объект удаляется из ОЗУ, поскольку ссылки на него отсутствуют.

Это не совсем так с вашим вопросом. Функция toString ссылается на нее. Поскольку внешний объем завершен, теперь он является локальной переменной для этой функции toString. Если мы не передали объект secret, он никогда не покидает эту область. Его нужно каким-то образом экспортировать.


Более логичной тактикой было бы просто создать ее как свойство нашего исходного объекта. Наш секрет может быть легко доступен пользователю, который знает, что такое JavaScript, поэтому мы должны сохранить некоторые головные боли и использовать разумный метод наследования.


Если вы не знаете, что SomeFunction.call принимает любое количество аргументов. Первый - это то, что вы хотите быть this в функции, а остальная часть - только обычные аргументы.


function gizmo() {
this.toString = function () {
return "gizmo " + this.id;
};
};

function hoozit(id) {
this.id = id;
gizmo.call(this); // allow gizmo to manipulate this object

this.test = function (is) {
return this.id === is;
};
};

h = new hoozit(1);
console.log(h.test(1)); // true
console.log(h.toString()); // "gizmo 1"

ответил(а) 2013-08-04T01:13:00+04:00 6 лет, 11 месяцев назад
55

Вам нужно secret = {}.


Ошибка при return testid === secret.id;, поскольку secret должен существовать.


Основная магия, которую вы, вероятно, ищете, где заполняется secret.id, так как все операции происходят в gizmo(). Ответ такой строки: that = gizmo(id, secret);


secret передается в gizmo и в JavaScript объекты передаются по ссылке. Это означает, что если у вас есть локальный объект и передать этот объект в качестве аргумента другой функции, любые операции с этим объектом будут отображаться локально.


Если вы не хотите, чтобы это произошло, вам понадобится какой-то экземпляр/клон (термин, клон которого неправильно использовался библиотеками, чтобы предложить глубокую копию) аргумента. Но в этом примере вы хотите изменить secret в gizmo, чтобы обновить secret в hoozit, так что все работает так, как должно.


Вот еще один способ его написания:


function gizmo(secret) {              // only receive secret, which already has an "id"
secret = secret || {'id':null}; // if secret not passed create it with a default "id"
return {
toString: function () {
return "gizmo " + secret.id; // the reason why we needed a default "id"
}
};
}

function hoozit(id) {
var secret = {'id':id}, // create a object and set key "id" to hoozit argument
that = gizmo(secret); // only pass secret

that.test = function (testid) {
return testid === secret.id;
};

return that;
}

var myHoozit = hoozit(20);
console.log( myHoozit.test(20) ); //returns true

ответил(а) 2013-08-04T01:15:00+04:00 6 лет, 11 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема