#javascript #prototypal-inheritance
#javascript #прототипное наследование
Вопрос:
Просто экспериментировал с различными методами наследования в JS и наткнулся на что-то слегка смущающее в прототипном шаблоне наследования Крокфорда:
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
var C,
P = {
foo:'bar',
baz: function(){ alert("bang"); }
}
C = object(P);
Все хорошо — за исключением того, что при входе в консоль объект отображается как F. Я видел классическую эмуляцию, в которой вы можете переназначить конструктор — есть ли аналогичный метод для принудительного использования ссылки на объекты (консоль)?
Ответ №1:
Проблема в том, что это ссылается на name
функции конструктора. Это быстро переходит в обсуждение функциональных выражений и инструкций, а также свойства name. Оказывается, совершенно невозможно создать новую именованную функцию во время выполнения без использования eval. Имена могут быть указаны только с помощью инструкции function function fnName(){}
, и невозможно создать этот фрагмент кода динамически, помимо его вычисления. var fnExpression = function(){}
приводит к анонимной функции, присвоенной переменной. name
Свойство функций является неизменяемым, так что дело сделано. Использование Function("arg1", "arg2", "return 'fn body';")
также может привести только к созданию анонимной функции, несмотря на то, что она похожа на eval.
По сути, это просто оплошность в спецификации JS (Брендан Эйх заявил, что сожалеет об определении отображаемого имени так, как он это сделал 10 или около того лет назад), и обсуждается решение для ES6. Это позволило бы ввести больше семантики для получения отображаемого имени функции для инструментов отладки или, возможно, явный способ ее установки и настройки.
На данный момент у вас есть один маршрут: eval или какая-либо другая форма позднего выполнения настраиваемого кода. (оценивайте любым другим именем …)
function displayName(name, o){
var F = eval("1amp;amp;function " name "(){}");
F.prototype = o;
return new F;
}
Оператор функции сам по себе не вернет результат из eval, но выполнение 1 amp;amp; fnStatement
преобразует объект в выражение, которое можно возвращать.
(Прокси Harmony также позволяют настраивать функции, которые сообщают имена, которые вы можете настроить без eval, но это невозможно использовать, кроме как в Node.js и Firefox в настоящее время).
Я отмечу здесь, что все эти «злые» функции, на которые насрал Крокфорд и многие другие, ВСЕ имеют свое место. eval
, with
, расширяющие natives все позволяют использовать определенные методы, которые в противном случае совершенно невозможны, и нет ничего плохого в том, чтобы использовать их при подходящем случае. Вполне вероятно, что большинство людей недостаточно квалифицированы, чтобы судить о том, когда наступит подходящее время. На мой взгляд, использование eval
безвредно для компенсации плохой семантики языка и инструментов в ожидании решения вполне приемлемо и не причинит вам никакого вреда, пока вы не вставляете произвольный код в этот оператор eval .
Комментарии:
1. ах, потрошенный — это своего рода большая ошибка … спасибо за подробное объяснение
2. Во-первых, это объявление функции , а не оператор функции . Это может показаться педантичным, но есть различие: function statement — это название, данное Mozilla своей нестандартной реализации того, что выглядит как объявление функции внутри блока (например,
if (true) { function f() {} }
). См kangax.github.com/nfe .3. Во-вторых, помещение
1amp;amp;
перед тем, что в противном случае было бы объявлением функции, превращает его (как вы сказали) в функциональное выражение, поэтому ранее была допущена ошибка, когда вы сказали «Имена могут быть указаны только с помощью инструкции function» : фактически, вы показали, что имя функции также может быть указано с помощью именованного функционального выражения.
Ответ №2:
Если я регистрирую объект, я вижу: Object { foo="bar", baz=function()}
, поэтому я не понимаю вашей проблемы…
В любом случае, можно использовать Object.create() вместо функции Крокфорда:
var P = {
foo:'bar',
baz: function(){ alert("bang"); }
}
var C = Object.create (P);
console.log (C):
Object { foo="bar", baz=function()}
Комментарии:
1. только начиная с ECMAScript 5, который по-прежнему поддерживает устаревшие браузеры.