Незначительный недостаток Crockford Prototype Inheritance

Просто экспериментируя с различными методами наследования в 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. Я видел классическую эмуляцию, в которой вы можете переназначить конструктор - есть ли аналогичный метод для принуждения объектов (консоль) ссылка?


person sunwukung    schedule 09.11.2011    source источник


Ответы (2)


Проблема в том, что он ссылается на name функции-конструктора. Это быстро превращается в обсуждение функциональных выражений и операторов, а также свойства имени. Оказывается, совершенно невозможно создать новую именованную функцию во время выполнения без использования eval. Имена могут быть указаны только с помощью функционального оператора function fnName(){}, и невозможно создать этот фрагмент кода динамически, кроме его оценки. var fnExpression = function(){} приводит к присвоению переменной анонимной функции. Свойство name функций неизменяемо, так что дело сделано. Использование Function("arg1", "arg2", "return 'fn body';") также может создать только анонимную функцию, несмотря на то, что она похожа на eval.

В основном это просто недосмотр в спецификации JS (Брендан Эйх заявил, что сожалеет о том, что определил отображаемое имя так, как он это сделал 10 или около того лет назад), и обсуждается решение для ES6. Это введет больше семантики для получения отображаемого имени функции для инструментов отладки или, возможно, явный способ его установки и настройки.

На данный момент у вас есть один путь: eval или какая-либо другая форма позднего выполнения настраиваемого кода. (eval под любым другим именем...)

function displayName(name, o){
  var F = eval("1&&function "+name+"(){}");
  F.prototype = o; 
  return new F;
}

Сам по себе оператор функции не вернется из eval, но выполнение 1 && fnStatement приводит к выражению, которое можно вернуть.

(Прокси-серверы Harmony также позволяют настраивать функции, которые сообщают имена, которые вы можете настроить без eval, но в настоящее время их нельзя использовать, кроме как в Node.js и Firefox).

Отмечу здесь, что все те "злые" функции, на которые наткнулся Крокфорд и многие другие, ВСЕ на своем месте. eval, with, расширяющие нативы, все они позволяют использовать определенные методы, которые в противном случае совершенно невозможны, и нет ничего плохого в том, чтобы использовать их в подходящем случае. Вполне вероятно, что большинство людей не имеют права судить о том, когда настало подходящее время. На мой взгляд, безобидное использование eval для компенсации плохой языковой семантики и инструментов в ожидании решения вполне приемлемо и не причинит вам никакого вреда, если вы не направляете произвольный код в это выражение eval.

person Community    schedule 10.11.2011
comment
ах, выпотрошенный - это своего рода большая ошибка ... спасибо за подробное объяснение - person sunwukung; 10.11.2011
comment
Во-первых, это объявление функции, а не оператор функции. Это может показаться педантичным, но есть различие: оператор функции — это имя, данное Mozilla своей нестандартной реализации того, что выглядит как объявление функции внутри блока (например, if (true) { function f() {} }). См. kangax.github.com/nfe. - person Tim Down; 10.05.2012
comment
Во-вторых, размещение 1&& перед тем, что в противном случае было бы объявлением функции, превращает его (как вы сказали) в функциональное выражение, поэтому раньше была ошибка, когда вы говорили, что Имена могут быть указаны только с помощью оператора функции: на самом деле вы показали, что имя функции также может быть указано через выражение именованной функции. - person Tim Down; 10.05.2012

Если я зарегистрирую объект, я увижу: Object { foo="bar", baz=function()}, поэтому я не понимаю вашей проблемы...

В любом случае, вместо функции Крокфорда можно использовать Object.create():

var P = {
         foo:'bar',
         baz: function(){ alert("bang"); }
         }

var C = Object.create (P);

console.log(С):

Object { foo="bar", baz=function()}

person Gabriel Llamas    schedule 06.03.2012
comment
только с ECMAscript 5, который по-прежнему поддерживает устаревшие браузеры. - person sunwukung; 09.03.2012