Марейн Хавербеке - Выразительный JavaScript
- Название:Выразительный JavaScript
- Автор:
- Жанр:
- Издательство:неизвестно
- Год:неизвестен
- ISBN:978-1593275846
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Марейн Хавербеке - Выразительный JavaScript краткое содержание
В процессе чтения вы познакомитесь с основами программирования и, в частности, языка JavaScript, а также выполните несколько небольших проектов. Один из самых интересных проектов — создание своего языка программирования.
Выразительный JavaScript - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Упражнения
Сумма диапазона
Во введении был упомянут удобный способ подсчёта сумм диапазонов чисел:
console.log(sum(range(1, 10)));
Напишите функцию range, принимающую два аргумента – начало и конец диапазона – и возвращающую массив, который содержит все числа из него, включая начальное и конечное.
Затем напишите функцию sum, принимающую массив чисел и возвращающую их сумму. Запустите указанную выше инструкцию и убедитесь, что она возвращает 55.
В качестве бонуса дополните функцию range, чтобы она могла принимать необязательный третий аргумент – шаг для построения массива. Если он не задан, шаг равен единице. Вызов функции range(1, 10, 2)должен будет вернуть [1, 3, 5, 7, 9]. Убедитесь, что она работает с отрицательным шагом так, что вызов range(5, 2, -1)возвращает [5, 4, 3, 2].
console.log(sum(range(1, 10)));
// → 55
console.log(range(5, 2, -1));
// → [5, 4, 3, 2]
Обращаем вспять массив
У массивов есть метод reverse, меняющий порядок элементов в массиве на обратный. В качестве упражнения напишите две функции, reverseArrayи reverseArrayInPlace. Первая получает массив как аргумент и выдаёт новый массив – с обратным порядком элементов. Вторая работает как оригинальный метод reverse– она меняет порядок элементов на обратный в том массиве, который был ей передан в качестве аргумента. Не используйте стандартный метод reverse.
Если иметь в виду побочные эффекты и чистые функции из предыдущей главы, какой из вариантов вам кажется более полезным? Какой более эффективным?
console.log(reverseArray(["A", "B", "C"]));
// → ["C", "B", "A"];
var arrayValue = [1, 2, 3, 4, 5];
reverseArrayInPlace(arrayValue);
console.log(arrayValue);
// → [5, 4, 3, 2, 1]
Список
Объекты могут быть использованы для построения различных структур данных. Часто встречающаяся структура – список (не путайте с массивом). Список – связанный набор объектов, где первый объект содержит ссылку на второй, второй – на третий, и т. п.
var list = {
value: 1,
rest: {
value: 2,
rest: {
value: 3,
rest: null
}
}
};
В результате объекты формируют цепочку:
Списки удобны тем, что они могут делиться частью своей структуры. Например, можно сделать два списка, {value: 0, rest: list}и {value: -1, rest: list}, где list– это ссылка на ранее объявленную переменную. Это два независимых списка, при этом у них есть общая структура list, которая включает три последних элемента каждого из них. Кроме того, оригинальный список также сохраняет свои свойства как отдельный список из трёх элементов.
Напишите функцию arrayToList, которая строит такую структуру, получая в качестве аргумента [1, 2, 3], а также функцию listToArray, которая создаёт массив из списка. Также напишите вспомогательную функцию prepend, которая получает элемент и создаёт новый список, где этот элемент добавлен спереди к первоначальному списку, и функцию nth, которая в качестве аргументов принимает список и число, а возвращает элемент на заданной позиции в списке или же undefinedв случае отсутствия такого элемента.
Если ваша версия nthнерекурсивна, тогда напишите её рекурсивную версию.
console.log(arrayToList([10, 20]));
// → {value: 10, rest: {value: 20, rest: null}}
console.log(listToArray(arrayToList([10, 20, 30])));
// → [10, 20, 30]
console.log(prepend(10, prepend(20, null)));
// → {value: 10, rest: {value: 20, rest: null}}
console.log(nth(arrayToList([10, 20, 30]), 1));
// → 20
Глубокое сравнение
Оператор ==сравнивает переменные объектов, проверяя, ссылаются ли они на один объект. Но иногда полезно было бы сравнить объекты по содержимому.
Напишите функцию deepEqual, которая принимает два значения и возвращает true, только если это два одинаковых значения или это объекты, свойства которых имеют одинаковые значения, если их сравнивать рекурсивным вызовом deepEqual.
Чтобы узнать, когда сравнивать величины через ===, а когда – объекты по содержимому, используйте оператор typeof. Если он выдаёт "object"для обеих величин, значит нужно делать глубокое сравнение. Примите во внимание одно дурацкое исключение, существующее по историческим причинам: typeof nullтоже возвращает "object".
var obj = {here: {is: "an"}, object: 2};
console.log(deepEqual(obj, obj));
// → true
console.log(deepEqual(obj, {here: 1, object: 2}));
// → false
console.log(deepEqual(obj, {here: {is: "an"}, object: 2}));
// → true
5. Функции высшего порядка
Цу-ли и Цу-су похвалялись размерами своих новых программ. «Двести тысяч строк»,- сказал Цу-ли,- «не считая комментариев!» Цу-су ответил: «Пф-ф, моя – почти миллион строк». Мастер Юнь-Ма сказал: «Моя лучшая программа занимает пятьсот строк». Услышав это, Цу-ли и Цу-су испытали просветление.
Мастер Юнь-Ма, «Книга программирования»Есть два способа построения программ: сделать их настолько простыми, что там очевидно не будет ошибок, или же настолько сложными, что там не будет очевидных ошибок.
Энтони Хоар, 1980, лекция на вручении премии ТьюрингаБольшая программа – затратная программа, и не только из-за времени её написания. Большой размер обычно означает сложность, а сложность сбивает с толку программистов. Сбитые с толку программисты делают ошибки в программах. Большая программа означает, что багам есть где спрятаться, и их получается труднее отыскать.
Вернёмся ненадолго к двум примерам из введения. Первый самодостаточен и занимает шесть строк.
var total = 0, count = 1;
while (count <= 10) {
total += count;
count += 1;
}
console.log(total);
Второй основан на двух внешних функциях и занимает одну строку.
console.log(sum(range(1, 10)));
В каком из них скорее встретится ошибка?
Если мы добавим размер определений sumи range, вторая программа тоже получится большой – больше первой. Но я всё равно утверждаю, что она скорее всего будет правильной.
Это будет потому, что выражение решения непосредственно относится к решаемой задаче. Суммирование числового промежутка – это не циклы и счётчики. Это суммы и промежутки.
Определения этого словаря (функции sumи range) будут включать циклы, счётчики и другие случайные детали. Но потому, что они выражают более простые концепции, чем вся программа, их проще сделать правильно.
Абстракции
В программном контексте эти «словарные» определения часто называют абстракциями. Абстракции прячут детали и дают нам возможность разговаривать о задачах на высшем, или более абстрактном, уровне.
Читать дальшеИнтервал:
Закладка: