Pull to refresh

JavaScript 1.8

Reading time5 min
Views9.3K
Original author: John Resig
JavaScript 1.8 предоставляет огромное количество вкусного синтаксического сахара, в основном любителями функциональщины. Но очень мало разработчиков знает об этой красоте. Конечно, к сожалению, все эти вкусности не поддерживает даже Chrome (что уж говорить об IE?), а только Firefox 3+, но JavaScript-разработчик просто обязан знать обо всех этих новинках.

Наиболее полную информацию можно найти в статьях на MDN:

А я перевела небольшую, но интересную статью Джона Ресига (автора jQuery), который раскрывает в ней некоторые из новых фич: Expression Closures, Generator Expressions, __iterator__, Array Reduce и кое-что ещё:

// Останавливаем выполнение события по-умолчанию
document.addEventListener("click", function() false, true);
// Выводим три сообщения
for ( let i in 3 ) alert( i );
// Создаем массив из 100 элементов, заполненный нулями
[ 0 for ( i in 100 ) ];
// Создаем единичную матрицу 10*10
[[ i == j ? 1 : 0 for ( i in 10 ) ] for ( j in 10 )];


Хотя статья написана в 2007 году, я не нашла её перевода на русский язык и вообще достаточно информации по этой теме на русском языке. Весь код в примерах отлично работает в Firefox 3+.

Прогресс в JavaScript 1.8


Если вы не следили за прогрессом в разработке JavaScript 1.8, я предлагаю вашему вниманию быстрый обзор того, что уже есть в ночной сборке Firefox 3.
Пока что в последних версиях добавилось три основные возможности, и ожидается еще несколько. Основной задачей этой “легкой” версии является приближение текущей реализации JavaScript к желаемой спецификации JavaScript 2.

Лексические замыкания (Expression Closures)


Лексические замыкания недавно добавлены в транк, и они наверняка будут интересны любителям функционального программирования. Фактически, это просто изящная запись для написания простых функций, которая делает язык подобным типичной лямбда-нотации.
Давайте глянем на синтаксис лямбда-функций в разных языках:

Python:
lambda x: x * x

Smalltalk:
[ :x | x * x ]

JavaScript 1.8:
function(x) x * x

JavaScript 1.7 и старше:
function(x) { return x * x; }


Пожалуй, мой любимый пример — это установка обработчика событий:
document.addEventListener("click", function() false, true);

Или в сочетании этой нотации и некоторых функций применяемых для массивов из JavaScript 1.6:
elems.some(function(elem) elem.type == "text");

Это даст вам элегантный JS/DOM код.

Выражения-генераторы (Generator Expressions)


Это ещё один пример из новенького. Он посложнее предыдущего, так как здесь охвачено несколько понятий. Конкретно пример требует знаний большинства особенностей JavaScript 1.7 — особенно итераторов, генераторов и генераторов массивов. Эта особенность базируется на выражениях-генераторах, позаимствованных из Python.
В тикете, отслеживающем данную возможность, Брэндан запостил элегантный и функциональный решатель Судоку, написаный с использованием нового синтаксиса, предлагаемого этим дополнением. Эта демо-версия базируется на аналогичной, написанной на Python, которая демонстрирует применение выражений-генераторов.

Для лучшего понимая значения этой функции, давайте посмотрим на пример кода на JavaScript 1.8, взятого из решателя Судоку.
dict([s, [u for (u in unitlist) if (u.contains(s))]] for (s in squares))

Это выражение опирается на функцию dict(), которая берёт матрицу размером 2xN и конвертирует её в пару ключ/значение. Вот её код:
function dict(A) {
    let d = {}
    for (let e in A)
        d[e[0]] = e[1]
    return d
}

Давайте рассмотрим каждый элемент этого выражения для лучшего понимания того, что именно происходит.
[u for (u in unitlist) if (u.contains(s))]

Первая часть выражения — пример генерации массивов из JavaScript 1.7. А именно, мы проходим каждый элемент списка
и создаем массив с индексами, которые содержат s.
[s, ...] for (s in squares)

Вторая часть выражения — другой пример генерации массива. На первый взгляд — это очередная возможность JavaScript 1.7 — деструктивное присваивание (destructuring assignment), но это не так.
Деструктивное присваивание возникает тогда, когда вы присваиваете новое значение старому элементу массива — а здесь мы добавляем это значение в новый массив. Новые двумерные массивы впоследствии будут переданы в функцию dict.
dict([s, ...] for (s in squares))

Вот где скрыта магия. В JavaScript 1.7 мы можем вызвать функцию dict() так:
dict([[s, ...] for (s in squares)])

Обратите внимание на явное использование генерации списков. Проблема с дополнительной генерацией заключена в том, что она должна быть выполнена полностью для построения целого массива (который конвертируется в словарь/хэш). Впрочем, отсутствие еще одного [...] и определяет выражение-генератор. Это делает строчку на JavaScript 1.8 эквивалентной следующей строчке на JavaScript 1.7:
dict((function(){ for (s in squares) yield [s, ...] ; })())


Здесь вы видите, что выражение-генератор создает массив лениво — то есть, значения не будут сгенерированы до того момента, как они будут необходимы в функции dict() (в результате мы получаем меньше операций и лучшую производительность)

Вот другой пример выражения-генератора:

// Создает генератор, проходящий по массиву значений объекта
function val_iter(obj) {
    return (obj[x] for (x in obj));
}
// Итерирует по ключам объекта
for ( let key in obj ) { ... }
// Итерирует по значениям объектаа
for ( let value in val_iter(obj) ) { ... }


Конечно, функция val_iter() может быть построена и с помощью JavaScript 1.7, с использованием yield:

function val_iter(obj) {
    for (let x in obj)
        yield obj[x];
}


Выражения-генераторы будут особенно полезны в коде, жадном до памяти и CPU — например, в том же решателе Судоку, поскольку теперь есть возможность получать результаты решений только тогда, когда они нужны — а не расчитывать их заранее.

Развлекаемся с итераторами



Между прочим, я недавно уже разбирался с итераторами и генераторами — и мне не хватало возможности простой итерации по набору чисел (например, 0-9). Чуть поработав напильником, мы можем добавить такую возможность в язык:

// Добавляем итератор ко всем числам
Number.prototype.__iterator__ = function() {
    for ( let i = 0; i < this; i++ )
        yield i;
};
// Выводим три сообщения
for ( let i in 3 ) alert( i );
// Создаем массив из 100 элементов, заполненный нулями
[ 0 for ( i in 100 ) ]
// Создаем единичную матрицу 10*10
[[ i == j ? 1 : 0 for ( i in 10 ) ] for ( j in 10 )]


Может и боян, но я пришёл в дичайший восторг от этого.

Array Reduce


Последнее, что стоит разобрать — новые методы Array.reduce/Array.prototype.reduce с JavaScript 1.6 Array Extras.

Вы можете применить reduce к массиву таким образом:
someArray.reduce( fn [, initial] );


с функцией это будет выглядеть так:
someArray.reduce(function(lastValue, curValue){
  return lastValue + curValue;
});


Аргумент «lastValue» — результат выполнения коллбэка функции reduce. При первом вызове lastValue будет равно первому элементу списка или значению initial, если вы его передали, а значение curValue — следующему элементу списка

Таким образом, если вы хотите найти суму чисел от 0 до 99, вы можете сделать это следующим образом (используя JavaScript 1.8 и вышеупомянутый числовой итератор)
[x for ( x in 100 )].reduce(function(a,b) a+b);


Изящно, не так ли?

Вы также можете применять reduce для таких вещей, как слияние множества узлов DOM в один массив:
nodes.reduce(function(a,b) a.concat(b.childNodes), []);


Попробуйте сами!


Всё, что я упомянул выше работает в последних ночных сборках Firefox 3, так что если вы хотите попробовать выполнить что-то из вышепоказанного, просто сделайте следующее:
1. Скачайте ночную сборку Firefox 3
2. Создайте страницу, которая содержит следующий тэг (с атрибутом ‘version=1.8’, который только что был добавлен):
<script type="application/javascript;version=1.8"> ... your code ...</script>

И это всё, что нужно – наслаждайтесь!
Tags:
Hubs:
Total votes 82: ↑71 and ↓11+60
Comments34

Articles