Мастер-классы по Javascript Екатеринбург Ростов-на-Дону Москва Узнать больше...
Содержание (скрыть) Содержание (показать)

Прототипы встроенных объектов

  1. Прототип Object.prototype
  2. Автопреобразование примитивов
  3. Изменение встроенных прототипов
  4. Итого

Все встроенные объекты JavaScript имеют прототип.

Прототип Object.prototype

Для примера, создадим пустой объект. В нём ничего нет… Но откуда же берется метод toString?

var obj = { };
alert( obj.toString() ); // кто возвратил строку "[object Object]" ?

Никакой магии здесь нет. Запись obj = {} является краткой формой obj = new Object, где Object — встроенная функция-конструктор для объектов.

При создании объекта через new, функция-конструктор Object ставит ему obj.__proto__ = Object.prototype, где Object.prototype — встроенный объект, хранящий свойства и методы, общие для объектов, в частности, Object.prototype.toString.

Таким образом получается, что все свойства Object.prototype, включая toString, доступны для любых объектов.

То же самое происходит с массивами Array, функциями Function и с другими объектами. Встроенные методы для них находятся в Array.prototype, Function.prototype и т.п.

Как видно, получается иерархия наследования, которая всегда заканчивается на Object.prototype. Объект Object.prototype — единственный, у которого __proto__ равно null.

Поэтому говорят, что «все объекты наследуют от Object». На самом деле ничего подобного. Это все прототипы наследуют от Object.prototype.

Прототип является хранилищем методов.

Автопреобразование примитивов

В JavaScript существуют встроенные функции-конструкторы String, Number, Boolean. Но их не принято использовать для создания через new, т.к. при этом создаются объектные значения, что может поломать код:

alert(typeof 1); // "number" 
alert(typeof new Number(1)); // "object" ?!?

Несмотря на то, что в явном виде объекты String, Number, Boolean не создаются, их прототипы всё же используются. Они служат хранилищами методов для строк, чисел, булевых значений.

  1. Примитивное значение, такое как "строка", при вызове метода, например, "строка".slice(1), неявно преобразуется в соответствующий объект String.
  2. Затем ищется и вызывается метод прототипа String.prototype.slice.
  3. Результатом вызова slice является снова примитив.

Конечно же, браузеры применяют оптимизации и стараются не создавать лишних объектов, но прототип они используют как описано выше. А значит, методы для строк, чисел, булевых значений можно изменять и расширять…

Изменение встроенных прототипов

Встроенные прототипы можно изменять. Добавлять свои методы.

Мы можем написать метод для многократного повторения строки, и он тут же станет доступным для всех строк:

String.prototype.repeat = function(times) {
  return new Array(times+1).join(this);
}

alert( "ля".repeat(3) ) // ляляля

Аналогично мы могли бы создать метод Object.prototype.each(func), который будет применять func к каждому свойству:

Object.prototype.each = function(f) {
  for (var prop in this) {
    var value = this[prop];
    f.call(value, prop, value); // вызовет f(prop, value), this=value
  }
}

// Попробуем! (работает неверно!)  
var obj = { name: 'Вася', age: 25 };
obj.each(function(prop, val) {  
  alert(prop); // name -> age -> (!) each
})

Обратите внимание — пример выше работает неправильно. Он выводит лишнее свойство each, т.к. цикл for..in перебирает свойства в прототипе. Встроенные методы при этом пропускаются, а наш метод — вылез.

В данном случае это легко поправить добавлением проверки hasOwnProperty:

Object.prototype.each = function(f) {
  for (var prop in this) {
*!*
    if (!this.hasOwnProperty(prop)) continue;
*/!*
    var value = this[prop];
    f.call(value, prop, value);
  }
}

// Теперь все будет в порядке
var obj = { name: 'Вася', age: 25 };
obj.each(function(prop, val) {  
  alert(prop); // name -> age 
})

В данном случае это сработало, теперь код верен. Но мы же не хотим добавлять hasOwnProperty в цикл по любому объекту…

Свойства, добавленные в Object.prototype, появятся по всех for..in циклах. Они в них будут лишними. Не добавляйте свойства в Object.prototype.

Современный стандарт и for..in

Встроенные свойства и методы не перебираются в for..in, так как у них есть специальный внутренний флаг [[Enumerable]], установленный в false.

Современные браузеры (IE с версии 9) позволяют устанавливать этот флаг для новых свойств, которые в результате также будут пропускаться for..in.

Есть объекты, которые не участвуют в циклах for..in, например строки, функции.. И в их прототипы, пожалуй, можно добавлять свои методы. Но здесь есть свои «за» и «против»:

  • Новые методы позволяют писать более короткий и ясный код.
  • Новые свойства могут конфликтовать между собой. Представьте, что вы подключили две библиотеки, которые добавили одно и то же свойство в прототип, но определили его по-разному. Конфликт неизбежен.
  • Встроенные прототипы влияют глобально на весь код, и менять их не очень хорошо с архитектурной точки зрения.

Допустимо изменение прототипа и встроенных объектов, которое добавляет поддержку метода из современных стандартов в те браузеры, где её пока нет.

Например, добавим Object.create(proto) в старые браузеры:

if (!Object.create) {
  Object.create = function(proto) {
    function F() {}
    F.prototype = proto;
    return new F;
  }
}

Существует даже библиотека es5-shim, которая предоставляет многие функции современного JavaScript для старых браузеров. Они добавляются во встроенные объекты и их прототипы.

Итого

  • Методы встроенных объектов хранятся в их прототипах.
  • Встроенные прототипы можно расширить или поменять.
  • Добавление методов в Object.prototype ломает циклы for..in. Другие прототипы изменять не настолько опасно, но все же не рекомендуется во избежание конфликтов.

    Отдельно стоит изменение с целью добавления современных методов в старые браузеры, таких как Object.create, Object.keys, Function.prototype.bind и т.п. Это допустимо.


Комментарии

  1. Приветствуются комментарии, содержащие дополнения и вопросы по статье, и ответы на них.
  2. Если ваш комментарий касается задачи -- откройте её в отдельном окне и напишите там.
  3. Комментарии без смысла, с рекламой или не о статье вообще - удаляются.
Наверх

Содержание

Реклама

Нашли опечатку?

Нашли опечатку на сайте? Что-то кажется странным?
Выделите соответствующий текст и нажмите Ctrl+Enter!

Последние Комментарии

Помоги другим!

Помоги другим узнать о хорошей статье!