Использование супер методов в Javascript на основе функционального наследования Крокфорда

Я читал главу о функциональном наследовании в Крокфорде "Хорошие части". В примере с млекопитающим он дает мне немного смущение относительно того, почему он использует метод superior для изменения функции get_name. Вот пример, о котором идет речь:

Function.prototype.method = function (name, func) {
 this.prototype[name] = func;
 return this;
};
var mammal = function (spec) {
 var that = {};
 that.get_name = function () {
 return spec.name;
 };
 that.says = function () {
 return spec.saying || '';
 };
 return that;
};
var myMammal = mammal({
 name: 'Herb'
});
var cat = function (spec) {
 spec.saying = spec.saying || 'meow';
 var that = mammal(spec);
 that.purr = function (n) {
 var i, s = '';
 for (i = 0; i < n; i += 1) {
 if (s) {
 s += '-';
 }
 s += 'r';
 }
 return s;
 };
 that.get_name = function () {
 return that.says() + ' ' + spec.name + ' ' + that.says();
 };
 return that;
};
Object.method('superior', function (name) {
 var that = this,
 method = that[name];
 return function () {
 return method.apply(that, arguments);
 };
});
var coolcat = function (spec) {
 var that = cat(spec);
 var super_get_name = that.superior('get_name');
 that.get_name = function (n) {
 return 'like ' + super_get_name() + ' baby';
 };
 return that;
};
var myCoolCat = coolcat({
 name: 'Bix'
});
var name = myCoolCat.get_name(); // 'like meow Bix meow baby'

Я смущен этим, потому что я могу реплицировать одно и то же, удалив метод superior и просто изменив coolcat следующим образом:

var coolcat = function(spec) {
 var that = cat(spec);
 var super_get_name = that.get_name();
 that.get_name = function(n) {
 return 'like ' + super_get_name + ' baby';
 };
 return that;
};

Итак, я не понимаю, почему Крокфорд выбирает метод superior. Кто-нибудь может объяснить вообще?

2 ответа

Идея здесь в том, что это:

var super_get_name = that.superior('get_name');

делает super_get_name функцией, которая — каждый раз, когда он называется — вызывает that оригинальный get_name метод. Это позволяет новому get_name вызывать старый (суперкласс) get_name.

Теперь, если исходный метод get_name никогда не будет иметь никакого эффекта, кроме как вернуть одно значение, которое никогда не изменится, тогда да, это совершенно бессмысленно; вы можете просто сохранить одно значение, которое никогда не изменяется, а затем использовать его в новом get_name. Но если исходный get_name может действительно что-то делать (например, запустить запрос AJAX или изменить стиль HTML-элемента) или изменить его возвращаемое значение (скажем, если есть соответствующий set_name), тогда будет важное различие между тем, что делает ваш код (сохранить исходное возвращаемое значение и использовать его) и тем, что делает код Crockford (сохранить исходный метод и вызвать его).


Путаница, возникающая из этой главы книги Крокфорда, возникает, как описывает Крокфорд, "его" предпочтительный образец для реализации наследования в JavaScript, который полагается на его расширение объекта Function с помощью Function.prototype.method (глава 1.3), которое он использует для добавления методов к объекту Function.

Проблема, рассмотренная в примере coolcat, - это необходимость доступа к методу родительского типа. В "классических" языках OO, таких как Java, это естественно, поскольку классы существуют сами по себе. В JavaScript наследование прототипично, вы создаете объект типа mammal, а затем модифицируете объект для создания типа cat или coolcat.

В зависимости от вашего дизайна вы можете добавлять свойства и функции или переопределять функцию "унаследованный". Проблема возникает, когда вы переопределяете "унаследованную" функцию, в JavaScript вы в основном заменяете старую функцию новой функцией, тем самым теряя более старую функцию.

Теперь Крокфорд должен сделать две вещи:

  • получить метод parent (cat's) get_name; и
  • сохранить его таким образом, который может использоваться из переопределенного метода.

В этом коде:

var coolcat = function(spec) {
 var that = cat(spec), 
 super_get_name = that.superior('get_name');
 that.get_name = function(n) {
 return 'like ' + super_get_name() + ' baby';
 };
 return that;
};

Он делает 1., вызывая метод superior, чтобы получить функцию, которая получает функцию cat get_name; и он делает 2., сохраняя его в переменной super_get_name в функции coolcat (/object), предоставляя доступ к функции cat get_name до того, как она переопределена (более правильно перезаписана) с помощью функции coolcat get_name.

По-моему, путаница возникает из-за того, что:

  • Метод superior назван странно: метод "superior" - это просто метод для поиска по имени и может быть лучше назван, например, как getFunctionByName (вы можете попробовать это заменив строку get_name на purr, coolcat get_name теперь вызовет purr, просто не забудьте назвать его как super_get_name(10), иначе вы получите пустую строку).
  • Во-вторых, код и шаблон обманывают код, полагаясь на некоторые конкретные шаблоны Крокфорда, и, вероятно, вас упрекнет, если вы попытаетесь погрузиться в эту главу, не следуя всей книге.

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

var coolcat = function(spec) {
 var that = cat(spec);
 that.parent_get_name = that.get_name;
 that.get_name = function() {
 return 'like ' + this.parent_get_name() + ' baby';
 };
 return that;
};

Есть еще некоторые странности, например аргумент n в определении функции coolcat get_name, который я могу предположить только при копировании функции purr, которая предложила бы призрак-писатель!

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

licensed under cc by-sa 3.0 with attribution.