javascript

Возможности JavaScript, о существовании которых я не знал

  • суббота, 13 января 2018 г. в 03:13:59
https://habrahabr.ru/company/ruvds/blog/346500/
  • Разработка веб-сайтов
  • JavaScript
  • Блог компании RUVDS.com


image На днях я читал материалы на MDN и наткнулся на некоторые довольно интересные возможности и API JavaScript, о существовании которых я не знал. Хочу сегодня о них рассказать.

Не берусь судить о том, пригодятся ли они кому-нибудь, но, полагаю, что раз уж всё это есть в языке, то об этом полезно, как минимум, знать.

Метки


Метки можно назначать циклам for или блокам кода в JS… А вы не знали? Я — так точно этого не знал. Затем на эти метки можно ссылаться и использовать команды break или continue в циклах for, а также применять команду break в блоках кода.

loop1: // назначение метки "loop1" 
for (let i = 0; i < 3; i++) { // "loop1"
   loop2: // назначение метки "loop2"
   for (let j = 0; j < 3; j++) { // "loop2"
      if (i === 1) {
         continue loop1; // продолжение "loop1"
         // break loop1; // прерывание "loop1"
      }
      console.log(`i = ${i}, j = ${j}`);
   }
}

/* 
 * # Вывод
 * i = 0, j = 0
 * i = 0, j = 1
 * i = 0, j = 2
 * i = 2, j = 0
 * i = 2, j = 1
 * i = 2, j = 2
 */

Вот пример именования блоков с помощью меток. С такими метками можно использовать лишь оператор break.

foo: {
  console.log('one');
  break foo;
  console.log('this log will not be executed');
}
console.log('two');

/*
 * # Вывод
 * one
 * two
 */

Оператор void


Я полагал, что мне знакомы все операторы JavaScript до тех пор пока не увидел оператор void, который, как оказалось, присутствует в JS с 1996-го года. Его поддерживают все браузеры, работает он довольно просто. Вот что пишут об этом на MDN:

Оператор void вычисляет переданное выражение и возвращает undefined.

Этот оператор позволяет, например, использовать альтернативную форму конструирования IIFE:

void function iife() {
	
console.log('hello');
}();

// это – то же самое, что и...

(function iife() {
    console.log('hello');
})()

По поводу void можно сделать лишь одно замечание, которое заключается в том, что вычисление выражения это… void (undefined)!

const word = void function iife() {
	
return 'hello';
}();

// word имеет значение "undefined"

const word = (function iife() {
	
return 'hello';
})();

// word имеет значение "hello"

Оператор void можно использовать с ключевым слово async, затем полученная конструкция способна сыграть роль асинхронной точки входа в код:

void async function() { 
    try {
        const response = await fetch('air.ghost.io'); 
        const text = await response.text();
        console.log(text);
    } catch(e) {
        console.error(e);
    }
}()

// или просто сделайте так :)

(async () => {
    try {
        const response = await fetch('air.ghost.io'); 
        const text = await response.text();
        console.log(text);
    } catch(e) {
        console.error(e);
    }
})();

Оператор «запятая»


После того, как я почитал про оператор «запятая», я понял, что я не в полной мере понимал то, как именно он работает. Вот цитата из MDN:

Оператор запятая выполняет каждый из его операндов (слева направо) и возвращает значение последнего операнда.

function myFunc() {
  let x = 0;
  return (x += 1, x); // то же самое, что и return ++x;
}

y = false, true; // возвращает true в консоли
console.log(y); // false (самый левый операнд)

z = (false, true); // возвращает true в консоли
console.log(z); // true (самый правый оператор)

Оператор «запятая» и условный оператор


В условном операторе последнее значение в операторе «запятая» становится возвращаемым значением. Поэтому можно поместить до этого оператора любое количество выражений. В следующем примере я разместил команду console.log перед возвращаемым логическим значением:

const type = 'man';

const isMale = type === 'man' ? (
    console.log('Hi Man!'),
    true
) : (
    console.log('Hi Lady!'),
    false
);

console.log(`isMale is "${isMale}"`);

API интернационализации


Интернационализация — это задача, которую, в большинстве случаев, решить непросто, но радует то, что в JS имеется API интернационализации, хорошо поддерживаемое большинством браузеров. Одна из моих любимых возможностей этого API — средство для форматирования дат:

const date = new Date();

const options = {
  year: 'numeric', 
  month: 'long', 
  day: 'numeric'
};

const formatter1 = new Intl.DateTimeFormat('es-es', options);
console.log(formatter1.format(date)); // 22 de diciembre de 2017

const formatter2 = new Intl.DateTimeFormat('en-us', options);
console.log(formatter2.format(date)); // December 22, 2017

Оператор конвейера


Оператор конвейера, на момент написания этого материала, поддерживается лишь в Firefox 58+, да и то, его нужно включать, используя специальный флаг. Однако, в Babel уже имеется предложение по этому поводу. Код с таким оператором выглядит как Bash-команды, и мне это нравится!

const square = (n) => n * n;
const increment = (n) => n + 1;

// без применения оператора конвейера
square(increment(square(2))); // 25

// с применением оператора конвейера
2 |> square |> increment |> square; // 25

Возможности, о которых стоит упомянуть


▍Атомарные операции


Атомарные операции дают возможность выполнения предсказуемых операций чтения и записи в ситуации, когда доступ к данным есть у нескольких потоков.

Благодаря таким операциям система ждёт завершения предыдущего действия перед выполнением следующего. Эти операции полезны в случаях, когда надо организовать надёжную работу с данными, скажем, доступными для главного потока и потока, представленного веб-воркером.

Мне очень нравятся атомарные операции в других языках вроде Java. Я полагаю, такие операции будут чаще использоваться в JS, когда всё больше и больше программистов будет использовать веб-воркеры для того, чтобы выносить в них тяжёлые вычисления из главного потока.

▍Метод Array.prototype.reduceRight


Мне никогда не доводилось видеть, чтобы на практике кто-то использовал метод Array.prototype.reduceRight(), так как он представляет собой комбинацию Array.prototype.reduce() и Array.prototype.reverse(). Дело в том, что подобное нужно очень редко. Однако, если вам это необходимо, reduceRight() отлично вам подойдёт.

const flattened = [[0, 1], [2, 3], [4, 5]].reduceRight(function(a, b) {
    return a.concat(b);
}, []);

//результирующий одномерный массив [4, 5, 2, 3, 0, 1]

▍Параметры setTimeout()


Вероятно, были в моей жизни несколько ситуаций, когда я мог бы избавиться от .bind(…), если бы знал о параметрах setTimeout(), которые, как оказалось, JS поддерживал всегда.

setTimeout(alert, 1000, 'Hello world!');

/*
 * # Вывод (alert)
 * Hello World!
 */

function log(text, textTwo) {
    console.log(text, textTwo);
}

setTimeout(log, 1000, 'Hello World!', 'And Mars!');

/*
 * # Вывод
 * Hello World! And Mars!
 */

▍Свойство HTMLElement.dataset


Я уже работал с пользовательскими атрибутами данных data-* в HTML-элементах, но я не знал о том, что имеется API, значительно упрощающее работу с ними. Помимо некоторых ограничений по именованию (подробности смотрите по вышеприведённой ссылке), такая работа, в целом, сводится к использованию имён с чёрточками для атрибутов и имён в верблюжьем стиле для обращения к ним из JS. В результате атрибут data-birth-planet виден в JS как birthPlanet.

<div id='person' data-name='john' data-birth-planet='earth'></div>

А вот как с этим работать из JS:

let personEl = document.querySelector('#person');

console.log(person.dataset) // DOMStringMap {name: "john", birthPlanet: "earth"}
console.log(personEl.dataset.name) // john
console.log(personEl.dataset.birthPlanet) // earth

// программно можно добавлять и другие атрибуты
personEl.dataset.foo = 'bar';
console.log(personEl.dataset.foo); // bar

Итоги


Надеюсь, в моём рассказе о неизвестных мне возможностях JavaScript вы нашли что-то новое и для себя.

Уважаемые читатели! Известно ли вам что-то такое о JS, о чём мало кто знает? Если так — просим поделиться.