>Rabbit.prototype.dance = function() {
> console.log("А " + this.type + " кролик танцует джигу.");
>};
>killerRabbit.dance();
>// → А убийственный кролик танцует джигу.
Это удобно. Но в некоторых случаях это приводит к проблемам. В предыдущих главах мы использовали объект как способ связать значения с именами – мы создавали свойства для этих имён, и давали им соответствующие значения. Вот пример из 4-й главы:
>var map = {};
>function storePhi(event, phi) {
> map[event] = phi;
>}
>storePhi("пицца", 0.069);
>storePhi("тронул дерево", -0.081);
Мы можем перебрать все значения фи в объекте через цикл >for
/>in
, и проверить наличие в нём имени через оператор >in
. К сожалению, нам мешается прототип объекта.
>Object.prototype.nonsense = "ку";
>for (var name in map)
> console.log(name);
>// → пицца
>// → тронул дерево
>// → nonsense
>console.log("nonsense" in map);
>// → true
>console.log("toString" in map);
>// → true
>// Удалить проблемное свойство
>delete Object.prototype.nonsense;
Это же неправильно. Нет события под названием >“nonsense”
. И тем более нет события под названием >“toString”
.
Занятно, что >toString
не вылезло в цикле >for
/>in
, хотя оператор >in
возвращает >true
на его счёт. Это потому, что JavaScript различает счётные и несчётные свойства.
Все свойства, которые мы создаём, назначая им значение – счётные. Все стандартные свойства в >Object.prototype
– несчётные, поэтому они не вылезают в циклах >for
/>in
.
Мы можем объявить свои несчётные свойства через функцию >Object.defineProperty
, которая позволяет указывать тип создаваемого свойства.
>Object.defineProperty(Object.prototype, "hiddenNonsense", {
> enumerable: false, value: "ку"
>});
>for (var name in map)
> console.log(name);
>// → пицца
>// → тронул дерево
>console.log(map.hiddenNonsense);
>// → ку
Теперь свойство есть, а в цикле оно не вылезает. Хорошо. Но нам всё ещё мешает проблема с оператором >in
, который утверждает, что свойства >Object.prototype
присутствуют в нашем объекте. Для этого нам понадобится метод >hasOwnProperty
.
>console.log(map.hasOwnProperty("toString"));
>// → false
Он говорит, является ли свойство свойством объекта, без оглядки на прототипы. Часто это более полезная информация, чем выдаёт оператор >in
.
Если вы волнуетесь, что кто-то другой, чей код вы загрузили в свою программу, испортил основной прототип объектов, я рекомендую писать циклы >for
/>in
так:
>for (var name in map) {
> if (map.hasOwnProperty(name)) {
> // ... это наше личное свойство
> }
>}