- •Введение
- •Введение в 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
Добавить функциям defer с аргументами
важность: 4
Добавьте всем функциям в прототип метод defer(ms), который возвращает обёртку, откладывающую вызов функции на ms миллисекунд.
Например, должно работать так:
function f(a, b) { alert( a + b );
}
f.defer(1000)(1, 2); // выведет 3 через 1 секунду.
То есть, должны корректно передаваться аргументы.
К решению
Свои классы на прототипах
Используем ту же структуру, что JavaScript использует внутри себя, для объявления своих классов.
Обычный конструктор
Вспомним, как мы объявляли классы ранее.
Например, этот код задаёт класс Animalв функциональном стиле, без всяких прототипов:
function Animal(name) { this.speed = 0; this.name = name;
this.run = function(speed) { this.speed += speed;
alert( this.name + ' бежит, скорость ' + this.speed ); };
this.stop = function() { this.speed = 0;
alert( this.name + ' стоит' ); };
};
var animal = new Animal('Зверь');
alert( animal.speed ); // 0, начальная скорость animal.run(3); // Зверь бежит, скорость 3 animal.run(10); // Зверь бежит, скорость 13 animal.stop(); // Зверь стоит
Класс через прототип
А теперь создадим аналогичный класс, используя прототипы, наподобие того, как сделаны классы
Object, Dateи остальные.
Чтобы объявить свой класс, нужно:
1.Объявить функцию-конструктор.
2.Записать методы и свойства, нужные всем объектам класса, в prototype.
Опишем класс Animal:
//конструктор function Animal(name) {
this.name = name; this.speed = 0;
}
//методы в прототипе
Animal.prototype.run = function(speed) { this.speed += speed;
alert( this.name + ' бежит, скорость ' + this.speed ); };
Animal.prototype.stop = function() { this.speed = 0;
alert( this.name + ' стоит' ); };
var animal = new Animal('Зверь');
alert( animal.speed ); // 0, свойство взято из прототипа animal.run(5); // Зверь бежит, скорость 5 animal.run(5); // Зверь бежит, скорость 10 animal.stop(); // Зверь стоит
В объекте animalбудут хранится свойства конкретного экземпляра: nameи speed, а общие методы — в прототипе.
Совершенно такой же подход, как и для встроенных классов в JavaScript.
Сравнение
Чем такое задание класса лучше и хуже функционального стиля?
Достоинства
●Функциональный стиль записывает в каждый объект и свойства и методы, а прототипный — только свойства.
Поэтому прототипный стиль — быстрее и экономнее по памяти.
Недостатки
●При создании методов через прототип, мы теряем возможность использовать локальные переменные как приватные свойства, у них больше нет общей области видимости с конструктором.
Таким образом, прототипный стиль — быстрее и экономнее, но немного менее удобен.
К примеру, есть у нас приватное свойство nameи метод sayHiв функциональном стиле ООП:
function Animal(name) { this.sayHi = function() {
alert( name ); };
}
var animal = new Animal("Зверь"); animal.sayHi(); // Зверь
При задании методов в прототипе мы не сможем её так оставить, ведь методы находятся вне конструктора, у них нет общей области видимости, поэтому приходится записывать nameв сам
объект, обозначив его как защищённое:
function Animal(name) { this._name = name;
}
Animal.prototype.sayHi = function() { alert( this._name );
}
var animal = new Animal("Зверь"); animal.sayHi(); // Зверь
Впрочем, недостаток этот — довольно условный. Ведь при наследовании в функциональном стиле также пришлось бы писать this._name, чтобы потомок получил доступ к этому значению.
Задачи
Перепишите в виде класса
важность: 5
Есть класс CoffeeMachine, заданный в функциональном стиле.
Задача: переписать CoffeeMachineв виде класса с использованием прототипа.
Исходный код:
function CoffeeMachine(power) { var waterAmount = 0;
var WATER_HEAT_CAPACITY = 4200;
function getTimeToBoil() {
return waterAmount * WATER_HEAT_CAPACITY * 80 / power;
}
this.run = function() { setTimeout(function() {
alert( 'Кофе готов!' ); }, getTimeToBoil());
};
this.setWaterAmount = function(amount) { waterAmount = amount;
};
}
var coffeeMachine = new CoffeeMachine(10000); coffeeMachine.setWaterAmount(50); coffeeMachine.run();
P.S. При описании через прототипы локальные переменные недоступны методам, поэтому нужно будет переделать их в защищённые свойства.
К решению
Хомяки с __proto__
важность: 5
Вы — руководитель команды, которая разрабатывает игру, хомяковую ферму. Один из программистов получил задание создать класс «хомяк» (англ – "Hamster").
Объекты-хомяки должны иметь массив foodдля хранения еды и метод found, который добавляет к нему.
Ниже — его решение. При создании двух хомяков, если поел один — почему-то сытым становится и второй тоже.
В чём дело? Как поправить?