Статьи

Перспектива Java-разработчика в отношении силы и опасности объектного прототипа JavaScript

В разделе «  Anti-Patterns»  книги «  Изучение шаблонов проектирования JavaScript» автор  Addy Osmani  называет «Изменение  Object прототипа класса» «особенно плохим анти-шаблоном». Одним из интересных (и пугающих) аспектов этого является то, что разработчик может изменить поведение для всех  объектов JavaScript  с помощью одного определения. Это аналогично тому, что было бы возможно в Java, если бы разработчику Java было разрешено изменять  класс объектов Java  .

Я кратко упомянул эту рискованную функцию в своем посте «  Объекты JavaScript с точки зрения разработчика Java» . Представьте себе хаос, который можно было бы визуализировать, если бы можно было изменить, например, то, как  была реализована реализация Java по  Objectумолчанию,  равная (Object) . В только что упомянутом сообщении в  блоге я продемонстрировал переопределение реализации конкретного объекта Java  toString() . Я упомянул, но не продемонстрировал переопределение  toString() для всех объектов JavaScript через  Object.prototype . В этом посте, я это продемонстрировать, что является эквивалентом того , что разработчик Java может сделать в Java , если разрешено менять в Java  Object«s ToString () напрямую (разработчики Java могут расширять Object и переопределять его только для каждого класса).

Слишком легко изменить поведение по умолчанию для всех объектов JavaScript. В следующем листинге кода показано, как легко изменить toString() поведение JavaScript по умолчанию  с предоставления строки » [object Object]» на предоставление строки » I'm a JavaScript object!«

Переопределение реализаций toString () всех объектов JavaScript по умолчанию

Object.prototype.toString = function objectToString()
{
   return "I'm a JavaScript object!";
}

Простые четыре строки в приведенном выше листинге кода (и я мог бы легко разместить их все в одной строке) изменяют поведение по умолчанию  toString() для всех объектов JavaScript. Я все еще могу переопределить эту реализацию по умолчанию  toString на основе именованных объектов (на сегодняшний день в JavaScript нет классов). Это было продемонстрировано в моем предыдущем посте и воспроизведено здесь для удобства:

Переопределение реализации toString () только для объекта Person

function Person(lastName, firstName)
{
   this.firstName = firstName;
   this.lastName = lastName;
}

Person.prototype.toString = function personToString()
{
   return this.firstName + ' ' + this.lastName;
}

В приведенном выше листинге кода показано создание Person объекта JavaScript  с функцией конструктора и переопределение  toString() для этого вновь созданного  Person объекта. Следующий листинг кода демонстрирует тестирование  toString() реализаций таким образом, что Person отображаются переопределенная реализация по умолчанию и настроенные  реализации. Результат выполнения этого демонстрационного кода показан после кода.

Демонстрация переопределенного по умолчанию toString () и настраиваемого лица toString ()

function demonstrateObjectPrototype()
{
   var indy = new Person('Jones', 'Henry');
   console.log("Indiana Jones's real name is " + indy);

   var solo = {};
   solo.lastName = 'Solo';
   solo.firstName = 'Han';
   console.log("Chewbacca's buddy is " + solo);
}

Из вывода, показанного выше, и перечисления кода перед ним, мы можем видеть, что мы изменили значение по умолчанию toString() с «[object Object]» на «Я объект JavaScript!» и что мы все еще можем переопределить реализацию конкретного объекта, чтобы использовать его собственное настроенное поведение, а не поведение по умолчанию.

Легко увидеть, как эта способность легко манипулировать поведением по умолчанию для всех объектов JavaScript может быть привлекательной и пугающей. Это не было бы повторным «шаблоном» (даже если это анти-шаблон), если бы у него не было привлекательности. Object.toString() Реализация по умолчанию в Java  , предоставляющая  хеш-код системного идентификатора  объекта, для которого он вызывается, редко кажется полезной, кроме как для дифференциации его от других объектов того же типа. Это может быть заманчиво на первый, если можно было бы легко изменить в Java  Object«s  toString(), чтобы изменить эту реализацию использовать рекурсию для перебора всех элементов данных заданного объекта. Тем не менее, также будут существенные риски и вопросы:

  • Как можно предотвратить  toString() отображение использованных отражений значений полей, которые не следует показывать по соображениям безопасности или по другим причинам?
  • Будут ли отображаться элементы данных (на уровне класса) в дополнение к членам уровня экземпляра?
  • Должны ли все объекты оплачивать затраты производительности отражения, особенно если эти объекты могут включать коллекции других объектов, которые могут привести к отражению в глубоких коллекциях?
  • Какой будет предпочтительный формат вывода?

Эти вопросы и проблемы, касающиеся переопределения Object.toString() поведения по умолчанию  в Java, представляют собой лишь часть вопросов и проблем, которые могут возникнуть, и можно утверждать, что изменение  toString()поведения по умолчанию менее рискованно, чем изменение поведения по умолчанию других  Object методов, таких как equals(Object). Можно всегда переопределять поведение в Java измененных Objectреализаций по умолчанию  , но его необходимо будет переопределить в каждом расширенном классе либо напрямую, либо через его классы-предки. Разработчики, плохо знакомые с базой кода, могут принять Object поведение JDK по умолчанию  и почувствовать неприятный сюрприз, когда узнают, что кодовая база изменила Object поведение по умолчанию  .

В этой статье я продемонстрировал, как легко переопределить Object поведение JavaScript по умолчанию с  помощью Object.prototype,  и попытался также показать, почему это следует использовать редко или никогда не использовать. Я намеренно подошел к этому с точки зрения разработчика Java, чтобы сформулировать больше различий в объектных моделях между Java и JavaScript.