
- •Введение
- •Введение в 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

●Методы, не привязанные к конкретному объекту, например сравнение.
●Вспомогательные методы, которые полезны вне объекта, например для форматирования даты.
●Фабричные методы.
Задачи
Счетчик объектов
важность: 5
Добавить в конструктор Article:
●Подсчёт общего количества созданных объектов.
●Запоминание даты последнего созданного объекта.
Используйте для этого статические свойства.
Пусть вызов Article.showStats()выводит то и другое.
Использование:
function Article() { this.created = new Date(); // ... ваш код ...
}
new Article(); new Article();
Article.showStats(); // Всего: 2, Последняя: (дата)
new Article();
Article.showStats(); // Всего: 3, Последняя: (дата)
Открыть песочницу с тестами для задачи.
К решению
Явное указание this: «call», «apply»
Итак, мы знаем, что this— это текущий объект при вызове «через точку» и новый объект при конструировании через new.
В этой главе наша цель получить окончательное и полное понимание thisв JavaScript. Для этого не хватает всего одного элемента: способа явно указать thisпри помощи методов callи apply.
Метод call

Синтаксис метода call:
func.call(context, arg1, arg2, ...)
При этом вызывается функция func, первый аргумент callстановится её this, а остальные передаются «как есть».
Вызов func.call(context, a, b...)— то же, что обычный вызов func(a, b...), но с явно указанным this(=context).
Например, у нас есть функция showFullName, которая работает с this:
function showFullName() {
alert( this.firstName + " " + this.lastName );
}
Пока объекта нет, но это нормально, ведь JavaScript позволяет использовать thisвезде. Любая функция может в своём коде упомянуть this, каким будет это значение — выяснится в момент запуска.
Вызов showFullName.call(user)запустит функцию, установив this = user, вот так:
function showFullName() {
alert( this.firstName + " " + this.lastName );
}
var user = { firstName: "Василий", lastName: "Петров"
};
// функция вызовется с this=user showFullName.call(user) // "Василий Петров"
После контекста в callможно передать аргументы для функции. Вот пример с более сложным вариантом showFullName, который конструирует ответ из указанных свойств объекта:
var user = { firstName: "Василий", surname: "Петров", patronym: "Иванович"
};
function showFullName(firstPart, lastPart) { alert( this[firstPart] + " " + this[lastPart] );
}
// f.call(контекст, аргумент1, аргумент2, ...) showFullName.call(user, 'firstName', 'surname') // "Василий Петров" showFullName.call(user, 'firstName', 'patronym') // "Василий Иванович"
«Одалживание метода»
При помощи callможно легко взять метод одного объекта, в том числе встроенного, и вызвать в
контексте другого.
Это называется «одалживание метода» (на англ. method borrowing).
Используем эту технику для упрощения манипуляций с arguments.
Как мы знаем, argumentsне массив, а обычный объект, поэтому таких полезных методов как push, pop, joinи других у него нет. Но иногда так хочется, чтобы были…
Нет ничего проще! Давайте скопируем метод joinиз обычного массива:
function printArgs() {
arguments.join = [].join; // одолжили метод (1)
var argStr = arguments.join(':'); // (2)
alert( argStr ); // сработает и выведет 1:2:3
}
printArgs(1, 2, 3);
1.В строке (1)объявлен пустой массив []и скопирован его метод [].join. Обратим внимание, мы
не вызываем его, а просто копируем. Функция, в том числе встроенная — обычное значение, мы можем скопировать любое свойство любого объекта, и [].joinздесь не исключение.
2.В строке (2)запустили joinв контексте arguments, как будто он всегда там был.

Почему вызов сработает?
Здесь метод join массива скопирован и вызван в контексте arguments. Не произойдёт ли что-то плохое от того, что arguments— не массив? Почему он, вообще, сработал?
Ответ на эти вопросы простой. В соответствии со спецификацией , внутри joinреализован примерно так:
function join(separator) {
if (!this.length) return '';
var str = this[0];
for (var i = 1; i < this.length; i++) { str += separator + this[i];
}
return str;
}
Как видно, используется this, числовые индексы и свойство length. Если эти свойства есть, то все в порядке. А больше ничего и не нужно.
В качестве thisподойдёт даже обычный объект:
var obj = { // обычный объект с числовыми индексами и length 0: "А", 1: "Б", 2: "В",
length: 3 };
obj.join = [].join;
alert( obj.join(';') ); // "A;Б;В"
…Однако, копирование метода из одного объекта в другой не всегда приемлемо!
Представим на минуту, что вместо argumentsу нас — произвольный объект. У него тоже есть числовые индексы, lengthи мы хотим вызвать в его контексте метод [].join. То есть, ситуация похожа на arguments, но (!) вполне возможно, что у объекта есть свой метод join.
Поэтому копировать [].join, как сделано выше, нельзя: если он перезапишет собственный join объекта, то будет страшный бардак и путаница.
Безопасно вызвать метод нам поможет call:

function printArgs() {
var join = [].join; // скопируем ссылку на функцию в переменную
//вызовем join с this=arguments,
//этот вызов эквивалентен arguments.join(':') из примера выше var argStr = join.call(arguments, ':');
alert( argStr ); // сработает и выведет 1:2:3
}
printArgs(1, 2, 3);
Мы вызвали метод без копирования. Чисто, безопасно.
Ещё пример: [].slice.call(arguments)
В JavaScript есть очень простой способ сделать из argumentsнастоящий массив. Для этого возьмём метод массива: slice .
По стандарту вызов arr.slice(start, end)создаёт новый массив и копирует в него элементы массива arrот startдо end. А если startи endне указаны, то копирует весь массив.
Вызовем его в контексте arguments:
function printArgs() {
// вызов arr.slice() скопирует все элементы из this в новый массив var args = [].slice.call(arguments);
alert( args.join(', ') ); // args полноценный массив из аргументов
}
printArgs('Привет', 'мой', 'мир'); // Привет, мой, мир
Как и в случае с join, такой вызов технически возможен потому, что sliceдля работы требует только нумерованные свойства и length. Всё это в argumentsесть.
Метод apply
Если нам неизвестно, с каким количеством аргументов понадобится вызвать функцию, можно использовать более мощный метод: apply.
Вызов функции при помощи func.applyработает аналогично func.call, но принимает массив аргументов вместо списка.
func.call(context, arg1, arg2); // идентичен вызову func.apply(context, [arg1, arg2]);
В частности, эти две строчки cработают одинаково:
showFullName.call(user, 'firstName', 'surname');
showFullName.apply(user, ['firstName', 'surname']);
Преимущество applyперед callотчётливо видно, когда мы формируем массив аргументов динамически.
Например, в JavaScript есть встроенная функция Math.max(a, b, c...), которая возвращает максимальное значение из аргументов:
alert( Math.max(1, 5, 2) ); // 5
При помощи applyмы могли бы найти максимум в произвольном массиве, вот так:
var arr = []; arr.push(1); arr.push(5); arr.push(2);
// получить максимум из элементов arr alert( Math.max.apply(null, arr) ); // 5
В примере выше мы передали аргументы через массив — второй параметр apply… Но вы, наверное, заметили небольшую странность? В качестве контекста thisбыл передан null.
Строго говоря, полным эквивалентом вызову Math.max(1,2,3)был бы вызов Math.max.apply(Math, [1,2,3]). В обоих этих вызовах контекстом будет объект Math.
Но в данном случае в качестве контекста можно передавать что угодно, поскольку в своей внутренней реализации метод Math.maxне использует this. Действительно, зачем this, если
нужно всего лишь выбрать максимальный из аргументов? Вот так, при помощи applyмы получили короткий и элегантный способ вычислить максимальное значение в массиве!

Вызов call/applyс nullили undefined
В современном стандарте call/applyпередают this«как есть». А в старом, без use strict, при указании первого аргумента nullили undefinedв call/apply, функция получает this = window, например:
Современный стандарт:
function f() { "use strict";
alert( this ); // null
}
f.call(null);
Без use strict:
function f() {
alert( this ); // window
}
f.call(null);
Итого про this
Значение thisустанавливается в зависимости от того, как вызвана функция:
При вызове функции как метода
obj.func(...) // this = obj obj["func"](...)
При обычном вызове
func(...) // this = window (ES3) /undefined (ES5)
В new
new func() // this = {} (новый объект)
Явное указание
func.apply(context, args) // this = context (явная передача) func.call(context, arg1, arg2, ...)

Задачи
Перепишите суммирование аргументов
важность: 5
Есть функция sum, которая суммирует все элементы массива:
function sum(arr) {
return arr.reduce(function(a, b) { return a + b;
});
}
alert( sum([1, 2, 3]) ); // 6 (=1+2+3)
Создайте аналогичную функцию sumArgs(), которая будет суммировать все свои аргументы:
function sumArgs() { /* ваш код */
}
alert( sumArgs(1, 2, 3) ); // 6, аргументы переданы через запятую, без массива
Для решения примените метод reduceк arguments, используя call, applyили одалживание метода.
P.S. Функция sumвам не понадобится, она приведена в качестве примера использования reduce для похожей задачи.
К решению
Примените функцию к аргументам
важность: 5
Напишите функцию applyAll(func, arg1, arg2...), которая получает функцию funcи произвольное количество аргументов.
Она должна вызвать func(arg1, arg2...), то есть передать в funcвсе аргументы, начиная со второго, и возвратить результат.
Например:
//Применить Math.max к аргументам 2, 2, 3 alert( applyAll(Math.max, 2, 2, 3) ); // 3
//Применить Math.min к аргументам 2, 2, 3 alert( applyAll(Math.min, 2, 2, 3) ); // 2