В этом посте рассматривается, как супер-ссылки работают в JavaScript и как они будут упрощены ECMAScript.next. Чтобы понять этот пост, полезно ознакомиться с наследованием JavaScript. Если нет, обратитесь к [2].
Расширение конструкторов в JavaScript
Давайте посмотрим на следующий код JavaScript, где конструктор Employee расширяет конструктор Person. Расширение выполняется с помощью пользовательской функции inherits () (код которой будет показан позже).
// Super-constructor
function Person(name) {
this.name = name;
}
Person.prototype.describe = function() {
return "Person called "+this.name;
};
// Sub-constructor
function Employee(name, title) {
Person.call(this, name);
this.title = title;
}
Employee.prototype.describe = function me() {
return Person.prototype.describe.call(this)+" ("+this.title+")";
};
inherits(Employee, Person);
Сотрудник используется следующим образом:
> var jane = new Employee("Jane", "CTO");
> jane.describe()
'Person called Jane (CTO)'
Супер ссылки
Чтобы понять, как Employee.prototype.describe вызывает свой супер-метод, мы рассмотрим структуру экземпляра jane:
Джейн является первым участником цепочки прототипов. Его прямым прототипом является Employee.prototype, прототипом которого является Person.prototype. Супер-ссылки (в том числе супер-вызовы) — это встроенная функция (предложенная Алленом Уирфс-Броком) в ECMAScript.next, которая позволяет гораздо более кратко написать description ():
Employee.prototype.describe = function () {
return super.describe()+" ("+this.title+")";
};
Чтобы сделать супер-вызов super.describe (), выполняются следующие шаги:
- Определите супер, прототип объекта, в котором находится текущий метод.
- Поиск по описанию: начните с супер, проходите по цепочке прототипов, пока не найдете объект, у которого есть свойство description, верните значение этого свойства.
- Вызовите функцию, которую вы нашли, но оставьте ее как раньше. Обоснование: переопределенная версия метода description (), которая должна быть вызвана, должна иметь возможность доступа к свойствам jane.
Принимая это во внимание, мы видим, что super.describe () правильно реализован
Person.prototype.describe.call(this)
Все шаги выполняются:
- Определить супер:
Person.prototype
- Ищите описание.
Person.prototype.describe
Обратите внимание, что мы также находим описание, если его нет в Person.prototype напрямую, но в одном из его прототипов.
- Выполните метод, но сохраните текущий this:
Person.prototype.describe.call(this)
Объект-прототип имеет конструктор свойства, указывающий на конструктор [3]:
Employee.prototype.constructor === Employee
Это позволяет конструктору Employee обращаться к своему супер-конструктору как к супер-методу:
function Employee(name, title) {
super.constructor(name);
this.title = title;
}
Предостережение: вышеописанное работает только со статическими супер-ссылками (подробности см. Ниже). Обратите внимание, что семантика супер-ссылок зависит только от наличия цепочки прототипов и от способности определять объект, который содержит текущий метод. Не имеет значения, как была создана цепочка прототипов: через конструктор, через образец объекта (прототип как класс [2]) или через Object.create (). Суперреференции также не ограничиваются подтипами: можно также переопределить метод в прототипе с помощью метода в экземпляре, и последний вызовет первый.
Определение супер
Существует два способа определения значения super (шаг № 1 выше):
- Динамические супер-ссылки. Когда вы ищите метод, вы сообщаете ему, в каком объекте вы его нашли, подобно тому, как это передается методу. При разрешении супер-ссылки значение super является прототипом этого объекта. Недостаток этого подхода заключается в том, что он требует затрат времени выполнения для всех методов, а не только для тех, которые создают супер-ссылки. Эти затраты не позволяют использовать динамические супер-ссылки для ECMAScript.next.
- Статические супер-ссылки. Каждый метод, создающий супер-ссылку, имеет свойство, указывающее на объект, содержащий метод. Это свойство может быть установлено одним из двух способов. Во-первых, декларативно: если метод находится внутри литерала объекта, тогда ECMAScript.next автоматически устанавливает свойство. Во-вторых, обязательно: метод Object.defineMethod () позволяет вам добавить метод к объекту и установить свойство одновременно. После этого метод должен иметь доступ только к самому себе, чтобы получить соответствующее значение для super. ECMAScript.next обеспечивает такой доступ, но только внутри, когда создается суперссылка.
Имитация статических супер-ссылок
Чтобы смоделировать супер-ссылки ECMAScript.next в ECMAScript 5, нам нужно сохранить ссылку из метода на содержащий его объект и ссылаться на текущий метод изнутри метода. Первое можно сделать с помощью методаоризмы (). Последнее возможно было возможно через
arguments.callee , но это свойство устарело и является недопустимым в строгом режиме [4]. Альтернативой является выражение с
именованной функцией — вы можете присвоить выражению функции имя. Затем он выглядит как объявление функции, но все еще является выражением, а не выражением:
var fac = function me(x) {
if (x <= 0) {
return 1
} else {
return x * me(x-1)
}
};
Функция в правой части назначения может ссылаться на себя через меня, независимо от переменной, которой она была назначена. Идентификатор me существует только внутри тела функции:
> (function me() { return me }());
[Function: me]
> me
ReferenceError: me is not defined
Таким образом, в одной и той же области может быть несколько функций, которые все используют имя me.
var Employee = function me(name, title) {
me.super.constructor.call(this, name);
this.title = title;
}
Employee.prototype.describe = function me() {
return me.super.describe.call(this)+" ("+this.title+")";
};
наследование () работает следующим образом ( полный исходный код см. в
сущности ):
function inherits(subC, superC) {
var subProto = Object.create(superC.prototype);
// At the very least, we keep the "constructor" property
// At most, we preserve additions that have already been made
copyOwnFrom(subProto, subC.prototype);
addSuperReferencesTo(subProto);
subC.prototype = subProto;
};
function addSuperReferencesTo(obj) {
Object.getOwnPropertyNames(obj).forEach(function(key) {
var value = obj[key];
if (typeof value === "function" && value.name === "me") {
value.super = Object.getPrototypeOf(obj);
}
});
}
function copyOwnFrom(target, source) {
Object.getOwnPropertyNames(source).forEach(function(propName) {
Object.defineProperty(target, propName,
Object.getOwnPropertyDescriptor(source, propName));
});
return target;
};
Связанное чтение
- Супер-ссылки инициализатора объектов
- Прототипы как классы — введение в наследование JavaScript
- Что случилось со свойством «конструктор» в JavaScript?
- Строгий режим JavaScript: резюме
Источник: http://www.2ality.com/2011/11/super-references.html
