Незначительный недостаток прототипического наследования по Крокфорду

#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, который по-прежнему поддерживает устаревшие браузеры.