
- •Введение
- •Введение в JavaScript
- •Справочники и спецификации
- •Редакторы для кода
- •Консоль разработчика
- •Основы JavaScript
- •Привет, мир!
- •Внешние скрипты, порядок исполнения
- •Структура кода
- •Современный стандарт, «use strict»
- •Переменные
- •Правильный выбор имени переменной
- •Шесть типов данных, typeof
- •Основные операторы
- •Операторы сравнения и логические значения
- •Побитовые операторы
- •Взаимодействие с пользователем: alert, prompt, confirm
- •Условные операторы: if, '?'
- •Логические операторы
- •Преобразование типов для примитивов
- •Циклы while, for
- •Конструкция switch
- •Функции
- •Функциональные выражения
- •Именованные функциональные выражения
- •Всё вместе: особенности JavaScript
- •Качество кода
- •Отладка в браузере Chrome
- •Советы по стилю кода
- •Как писать неподдерживаемый код?
- •Автоматические тесты при помощи chai и mocha
- •Структуры данных
- •Введение в методы и свойства
- •Числа
- •Строки
- •Объекты как ассоциативные массивы
- •Объекты: перебор свойств
- •Объекты: передача по ссылке
- •Массивы c числовыми индексами
- •Массивы: методы
- •Массив: перебирающие методы
- •Псевдомассив аргументов «arguments»
- •Дата и Время
- •Замыкания, область видимости
- •Глобальный объект
- •Замыкания, функции изнутри
- •[[Scope]] для new Function
- •Локальные переменные для объекта
- •Модули через замыкания
- •Управление памятью в JavaScript
- •Устаревшая конструкция «with»
- •Методы объектов и контекст вызова
- •Методы объектов, this
- •Преобразование объектов: toString и valueOf
- •Создание объектов через «new»
- •Дескрипторы, геттеры и сеттеры свойств
- •Статические и фабричные методы
- •Явное указание this: «call», «apply»
- •Привязка контекста и карринг: «bind»
- •Функции-обёртки, декораторы
- •Некоторые другие возможности
- •Типы данных: [[Class]], instanceof и утки
- •Формат JSON, метод toJSON
- •setTimeout и setInterval
- •Запуск кода из строки: eval
- •Перехват ошибок, «try..catch»
- •ООП в функциональном стиле
- •Введение
- •Внутренний и внешний интерфейс
- •Геттеры и сеттеры
- •Функциональное наследование
- •ООП в прототипном стиле
- •Прототип объекта
- •Свойство F.prototype и создание объектов через new
- •Встроенные «классы» в JavaScript
- •Свои классы на прототипах
- •Наследование классов в JavaScript
- •Проверка класса: «instanceof»
- •Свои ошибки, наследование от Error

ООП в прототипном стиле
В этом разделе мы изучим прототипы и классы на них — де-факто стандарт объектноориентированной разработки в JavaScript.
Прототип объекта
Объекты в JavaScript можно организовать в цепочки, так, чтобы если свойство не найдено в одном объекте — оно автоматически искалось бы его в другом.
Связующим звеном выступает специальное свойство __proto__.
Прототип __proto__
Если один объект имеет специальную ссылку __proto__на другой объект, то при чтении свойства из него, если свойство отсутствует в самом объекте, оно ищется в объекте __proto__.
Свойство __proto__доступно во всех браузерах, кроме IE10-, а в более старых IE оно, конечно
же, тоже есть, но напрямую к нему не обратиться, требуются чуть более сложные способы, которые мы рассмотрим позднее.
Пример кода (кроме IE10-):
var animal = { eats: true
};
var rabbit = { jumps: true
};
rabbit.__proto__ = animal;
// в rabbit можно найти оба свойства alert( rabbit.jumps ); // true alert( rabbit.eats ); // true
1.Первый alertздесь работает очевидным образом — он выводит свойство jumpsобъекта rabbit.
2.Второй alertхочет вывести rabbit.eats, ищет его в самом объекте rabbit, не находит — и продолжает поиск в объекте rabbit.__proto__, то есть, в данном случае, в animal.
Иллюстрация происходящего при чтении rabbit.eats(поиск идет снизу вверх):

Объект, на который указывает ссылка __proto , называется «прототипом». В данном
случае получилось, что animalявляется прототипом для rabbit.
Также говорят, что объект rabbit«прототипно наследует» от animal.
Обратим внимание — прототип используется исключительно при чтении. Запись значения, например, rabbit.eats = valueили удаление delete rabbit.eats— работает напрямую с
объектом.
В примере ниже мы записываем свойство в сам rabbit, после чего alertперестаёт брать его у прототипа, а берёт уже из самого объекта:
var animal = { eats: true
};
var rabbit = { jumps: true, eats: false
};
rabbit.__proto__ = animal;
alert( rabbit.eats ); // false, свойство взято из rabbit
Другими словами, прототип — это «резервное хранилище свойств и методов» объекта, автоматически используемое при поиске.
У объекта, который является __proto__, может быть свой __proto__, у того — свой, и так далее. При этом свойства будут искаться по цепочке.
Ссылка __proto__ в спецификации
Если вы будете читать спецификацию EcmaScript — свойство __proto__обозначено в ней как [[Prototype]].
Двойные квадратные скобки здесь важны, чтобы не перепутать его с совсем другим свойством, которое называется prototype, и которое мы рассмотрим позже.
Метод hasOwnProperty
Обычный цикл for..inне делает различия между свойствами объекта и его прототипа.
Он перебирает всё, например:

var animal = { eats: true
};
var rabbit = { jumps: true, __proto__: animal
};
for (var key in rabbit) {
alert( key + " = " + rabbit[key] ); // выводит и "eats" и "jumps"
}
Иногда хочется посмотреть, что находится именно в самом объекте, а не в прототипе.
Вызов obj.hasOwnProperty(prop) возвращает true, если свойство propпринадлежит
самому объекту obj, иначе false.
Например:
var animal = { eats: true
};
var rabbit = { jumps: true, __proto__: animal
};
alert( rabbit.hasOwnProperty('jumps') ); // true: jumps принадлежит rabbit
alert( rabbit.hasOwnProperty('eats') ); // false: eats не принадлежит
Для того, чтобы перебрать свойства самого объекта, достаточно профильтровать keyчерез hasOwnProperty:
var animal = { eats: true
};
var rabbit = { jumps: true, __proto__: animal
};
for (var key in rabbit) {
if (!rabbit.hasOwnProperty(key)) continue; // пропустить "не свои" свойства alert( key + " = " + rabbit[key] ); // выводит только "jumps"
}
Методы для работы с __proto__
В современных браузерах есть два дополнительных метода для работы с __proto__. Зачем они нужны, если есть __proto__? В общем-то, не очень нужны, но по историческим причинам тоже существуют.
Чтение: Object.getPrototypeOf(obj)

Возвращает obj.__proto__(кроме IE8-)
Запись: Object.setPrototypeOf(obj, proto)
Устанавливает obj.__proto__ = proto(кроме IE10-).
Кроме того, есть ещё один вспомогательный метод:
Создание объекта с прототипом: Object.create(proto, descriptors)
Создаёт пустой объект с __proto__, равным первому аргументу (кроме IE8-), второй необязательный аргумент может содержать дескрипторы свойств.
Итого
●В JavaScript есть встроенное «наследование» между объектами при помощи специального свойства __proto__.
●При установке свойства rabbit.__proto__ = animalговорят, что объект animalбудет «прототипом» rabbit.
●При чтении свойства из объекта, если его в нём нет, оно ищется в __proto__. Прототип задействуется только при чтении свойства. Операции присвоения obj.prop =или удаления delete obj.propсовершаются всегда над самим объектом obj.
Несколько прототипов одному объекту присвоить нельзя, но можно организовать объекты в цепочку, когда один объект ссылается на другой при помощи __proto__, тот ссылается на третий, и
так далее.
Всовременных браузерах есть методы для работы с прототипом:
●Object.getPrototypeOf(obj) (кроме IE8-)
●Object.setPrototypeOf(obj, proto) (кроме IE10-)
●Object.create(proto, descriptors) (кроме IE8-)
Возможно, вас смущает недостаточная поддержка __proto__в старых IE. Но это не страшно. В последующих главах мы рассмотрим дополнительные методы работы с __proto__, включая те, которые работают везде.
Также мы рассмотрим, как свойство __proto__используется внутри самого языка JavaScript и как организовать классы с его помощью.
Задачи
Чему равно cвойство после delete?
важность: 5
Какие значения будут выводиться в коде ниже?

var animal = { jumps: null
};
var rabbit = { jumps: true
};
rabbit.__proto__ = animal;
alert( rabbit.jumps ); // ? (1)
delete rabbit.jumps;
alert( rabbit.jumps ); // ? (2)
delete animal.jumps;
alert( rabbit.jumps ); // ? (3)
Итого три вопроса.
К решению
Прототип и this
важность: 5
Сработает ли вызов rabbit.eat()?
Если да, то в какой именно объект он запишет свойство full: в rabbitили animal?
var animal = { eat: function() {
this.full = true;
}
};
var rabbit = { __proto__: animal
};
rabbit.eat();
К решению
Алгоритм для поиска
важность: 5
Есть объекты: