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

https://air.ghost.io/js-things-i-never-knew-existed/
  • Перевод
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, о чём мало кто знает? Если так — просим поделиться.

RUVDS.com 834,60
RUVDS – хостинг VDS/VPS серверов
Поделиться публикацией
Похожие публикации
Комментарии 144
  • +11
    Метки можно назначать циклам for или блокам кода в JS… А вы не знали? Я — так точно этого не знал. Затем на эти метки можно ссылаться и использовать команды break или continue в циклах for, а также применять команду break в блоках кода.

    Прям эволюция сишного goto.
    • 0

      JS напоминает в некоторых элементах Perl

      • +1
        Это не фича только лишь JS. Она доступна и в др. языках, к примеру Java. Вполне логичный подход, когда надо выйти из глубоко вложенного цикла
        • 0
          Правда иногда можно случайно вывалиться из switch по незнанию
          • +1
            Программист это профессия предполагающая применение мозга. Если что ;)
            • +1
              Забавно применять мозг с каким-нибудь undefined behavior
              • +1
                Что именно ub в том, когда видишь «continue label»?

                Я не говорю, что это нужно постоянно такое использовать. Я говорю о тех редких случаях, когда по-другому будет на уровне «почесть левое ухо ногой правой пятки».
                • –2
                  Суть в том что есть вещи которые приходитс изучать эмпирически, а не логически. undefined behavior — как раз одна из таких вещей и сколько мозг не прикладывай — пока не глянешь в сорцы или не скомпилируешь — не угадаешь что ожидать на выходе. Повезло, если в компиляторе/интерпретаторе не слишком много ошибок. Поэтому многие неявные механизмы никак не связаны с применением мозга.
                  Continue label посреди кода конечно же не является ub, однако без диагностических ошибок можно попасть не совсем туда, куда ожидалось. Прыжок из/в области видимости switch может быть настолько же неожиданными как и 0x873162387 вместо 0х0 в неинициализированном поле структуры — не все знакомы с текстом стандартов языка и помнят все нюансы из него.
                  • 0

                    Для этого есть документация.
                    И в JS, афаик, нет таких мест, как, например в php документации, как сейчас помню: "если вы передадите параметром тип номер результат функции непредсказуем".
                    Есть нелогичные моменты, но они как раз покрыты документацией.

                    • 0
                      Речь изначально не ограничивалась JS.
        • 0
          del
          • В Go тоже есть такая же конструкция. Обычно используется для выхода из бесконечного цикла в блоке select
            LOOP:
                for {
                    select {
            	case message:= <-messageChannel:
            		fmt.Println(message)
            	case canselMessage:= <-canselChannel:
            		fmt.Println(canselMessage)
            		break LOOP
            	}
                }
            • 0
              А встроенной конструкции loop не завезли как в Rust?
          • +2

            про "конвеер" стоит упомянуть что он исполняется справа налево, из вашего примера это не очевидно

            • +4

              хм… это существенно снижает полезность конструкции

              • 0
                Почему?
                • +2

                  если в a |> b |> c вначале исполняется c — то это совсем неинтуитивно, но внизу уже вроде объяснили, что все-таки "слева направо"

                  • +1

                    да, беру свои слова обратно, исполняется слева на право, т.е. как бы первое значение в строчке проходит все функции и возвращается то что "вылезло" с другой стороны.

              • +3
                Что значит «справа налево»? a |> b |> c вызовет сначала b, потом c. Так и в спецификации написано, и в других языках (F#, Elm) эта конструкция так же работает.
                • +7
                  let result = exclaim(capitalize(doubleSay("hello")));
                  result //=> "Hello, hello!"
                  
                  let result = "hello"
                    |> doubleSay
                    |> capitalize
                    |> exclaim;
                  
                  result //=> "Hello, hello!"

                  Отсюда. Обычный код (1-ая строка) исполняется справа-налево (doubleSay расположена правее capitalize и тем более exclaim), а piped-код как раз слева-направо.

                  • 0
                    Можно пояснить?
                    «справа налево» это не как в баше/эликсире?
                    • 0
                      Именно как в эликсире.
                  • +18
                    Справедливости ради, за метки и запятую стоит бить по рукам.
                    • –11

                      Это смотря что вы понимаете под справедливостью.

                      • 0
                        Метки нужны для выхода во внешний цикл из глубоко вложенного. Вполне легитимное дело.
                        • +7
                          Это дело явно требует пересмотра архитектуры.
                          • 0
                            Это дело требует выделения отдельной функции под циклы, это сложно назвать «пересмотром архитектуры».

                            Но иногда программисту приходится не сочинять алгоритм, а кодить готовый. В такой ситуации заведение лишней функции там где в оригинале ее не было не повысит читаемость кода, и понизит. Вот тут-то все эти метки порой и пригождаются.
                            • 0
                              Допустим, у нас есть функция, написанная на C, для всеми любимого языка (здесь читатель смиренно падает ниц):

                              Код функции
                              static const char *php_fgetcsv_lookup_trailing_spaces(const char *ptr, size_t len, const char delimiter) /* {{{ */
                              {
                              	int inc_len;
                              	unsigned char last_chars[2] = { 0, 0 };
                              
                              	while (len > 0) {
                              		inc_len = (*ptr == '\0' ? 1 : php_mblen(ptr, len));
                              		switch (inc_len) {
                              			case -2:
                              			case -1:
                              				inc_len = 1;
                              				php_mb_reset();
                              				break;
                              			case 0:
                              				goto quit_loop;
                              			case 1:
                              			default:
                              				last_chars[0] = last_chars[1];
                              				last_chars[1] = *ptr;
                              				break;
                              		}
                              		ptr += inc_len;
                              		len -= inc_len;
                              	}
                              quit_loop:
                              	switch (last_chars[1]) {
                              		case '\n':
                              			if (last_chars[0] == '\r') {
                              				return ptr - 2;
                              			}
                              			/* break is omitted intentionally */
                              		case '\r':
                              			return ptr - 1;
                              	}
                              	return ptr;
                              }
                              


                              Очевидно, что её можно разбить на несколько, избежав «плохой» метки. Однако, это не только усложнит код, но еще и потребует дополнительное процессорное время на вызовы.
                              Кто то скажет, что это экономия на спичках. Но таких спичек может быть много. И даже самые ярые противники использования такого рода оптимизации периодически «страдают» от непомерных цен на хостинг или зависания используемого софта.
                              Поэтому, использование меток всё же лучше избегать, нежели тотально пренебрегать.
                              • +1
                                Почему Вы думаете, что эта функция написана оптимально?
                                А, вообще, это, в первую очередь, пример запутанного кода, что подтверждает аргументы против неструктурного контроля выполнения.
                                Качество интерпретатора соответствует качеству языка, не так ли?
                                • 0
                                  Ответ дан сразу после приведенного кода. Качество интерпретатора и компиляторов вне обсуждаемой темы.
                                  • 0
                                    Где же ответ? В функции есть лишние присваивания, не оптимальное использование switch, но Вы делаете акцент на экономии процессорного времени. Где же оно в этом плохо написанном коде?
                                    • 0
                                      Приведите оптимальный вариант на ваш взгляд, но с учетом RFC 4180. Это не займет много времени.
                                      • +1
                                        с учетом RFC 4180. Это не займет много времени.
                                        Увы, коллега, не похоже на то. Я начал смотреть, что такое php_mblen и уже это заняло много времени с учётом контекста функции. Дело в том, что php_mblen в зависимости от значений макросов при сборке и переменных окружения может быть гарантировано однобайтным. Тогда неясно, на каком основании выполняется эта часть
                                        *ptr == '\0' ? 1
                                        потому что в таком случае цикл может остановиться только по достижении конца массива и вообще нет смысла его перебирать, так как сразу можно перейти к концу(экономия процессорного времени, да).
                                        Указанная Вами функция не является самосотоятельной частью, воплощающей стандарт, а зависит от объемлющего кода. Откуда мне знать обо всех задумках или ошибках, которые были здесь допущены. Например, почему в многобайтной кодировке, точный тип который задаётся окружением, символ перевода строки задаётся одним char? Здаётся мне, что в этой функции проблемы не исчерпываются использованием неструктурных переходов.

                                        Ну и, наконец, неужели для того, чтобы понять, что двойное копирование строки для 1-байтных последовательностей — это нечто неоптимальное, Вам нужен другой алгоритм?
                                        • 0
                                          Например, почему в многобайтной кодировке, точный тип который задаётся окружением, символ перевода строки задаётся одним char?

                                          В мультибайтовых кодировках существует обратная совместимость с ASCII, поэтому символы имеют разную длину. И всё становится предельно ясно.
                                          Откуда мне знать обо всех задумках или ошибках, которые были здесь допущены.

                                          Как в таком случае можно рассуждать об оптимальности кода?
                                          Вам нужен другой алгоритм?

                                          Мы всё еще рассматриваем пользу ссылок в некоторых случаях.
                                          • 0
                                            В мультибайтовых кодировках существует обратная совместимость с ASCII
                                            Из чего это следует? Я в стандарте этого не видел. Можете указать, где это сказано точно? Если это так, то алгоритм можно написать намного эффективней, так как mblen тяжеловесна.
                                            И всё становится предельно ясно.
                                            Тогда объясните мне, пожалуйста, как это понимать: *ptr == '\0' ? 1 для таких настроек:
                                            #ifndef HAVE_MBLEN
                                            # define php_mblen(ptr, len) 1

                                            И почему при обратной совместимости в качестве конца строки используется не '\0', а null wide character.
                                            Мы всё еще рассматриваем пользу ссылок в некоторых случаях.
                                            Можно конкретней? Ссылок на то, что
                                            last_chars[0] = last_chars[1];
                                            last_chars[1] = *ptr;

                                            Приводит к двойному копированию последовательностей 1-байтовых символов, или что-то ещё?
                                            • 0
                                              Я в стандарте этого не видел

                                              Согласно RFC3629, например, у кодировки переменная длина символа. Можно ли в таком случае обойтись без mblen?
                                              И почему при обратной совместимости в качестве конца строки используется не '\0', а null wide character

                                              Палка на двух концах.
                                              Приводит к двойному копированию

                                              Уверены? :)
                                              • 0
                                                Согласно RFC3629, например, у кодировки переменная длина символа. Можно ли в таком случае обойтись без mblen?

                                                RFC3629 — это же UTF-8, или я что-то не понимаю? Там как раз все просто: никаких отличий от однобайтовых кодировок конкретно в этой задаче (символы CR, LF и конец строки кодируются одинаково и с частями других символов их спутать невозможно).


                                                Собственно, UTF-8 специально же так проектировалась чтобы большинство старых строковых алгоритмов могло с ней нормально работать.

                                                • 0
                                                  Согласно RFC3629
                                                  При чём тут UTF-8? В стандарте ISO C на mblen, который используется в определении php_mblen, сказано, что он определяет количество байт символа кодировки, определяемой по LC_CTYPE. На каком основании в алгоритме делаются вещи, будто бы LC_CTYPE задаёт именно UTF-8? Повторю ещё раз, покажите, пожалуйста, место, где Вы увидели в стандарте на mblen, что
                                                  В мультибайтовых кодировках существует обратная совместимость с ASCII

                                                  Можно ли в таком случае обойтись без mblen?
                                                  Если же задачей всё ограничивается именно UTF-8, то, конечно, mblen — это избыточный тормоз.
                                                  Палка на двух концах.
                                                  Это объяснение? Потому что совместимо, поэтому не совместимо?
                                                  Уверены? :)
                                                  Удивляюсь, что это нужно объяснять. :)
                                              • +1
                                                Как в таком случае можно рассуждать об оптимальности кода?
                                                Потому что для оценки не всегда нужен весь контекст, а вот для создания корректного алгоритма — нужен. Особенно, если окажется, что изначальный алгоритм некорректен и сравнение бессмысленно.
                                                В этом-то и проблема плохого кода, что его потом и не перепишешь, не нарвавшись на остальные пахучие особенности общего кода.
                                    • –3
                                      Какой-то индусский наркоман писал эту функцию.

                                      По-моему 1-ый switch будет проще и быстрее так
                                      		if (inc_len == 0) {
                                      			break;
                                      		}
                                      		else if (inc_len > 0) {
                                      			last_chars[0] = last_chars[1];
                                      			last_chars[1] = *ptr;
                                      			ptr += inc_len;
                                      			len -= inc_len;
                                      		}
                                      		else {
                                      			php_mb_reset();
                                      			ptr++;
                                      			len--;
                                      		}
                                      

                                      • –2
                                        Походу некоторые из этих наркоманов ещё и сидят на хабре :) Объясните хоть дураку, что не так. Сакральные исходники священного PHP нельзя критиковать?
                                    • –1

                                      Как бы вы переписали следующий код и почему?


                                      check : if( this.status === 'check' ) {
                                          for( let master of this.masters ) {
                                              master.value()
                                              if( this.status !== 'check' ) break check
                                          }
                                          if( this.error ) throw this.error
                                          return this.cache
                                      } 
                                      
                                      this.status = 'compute'
                                      const result = this.handler()
                                      return this.push( result )
                                      • –2
                                        if(this.status === "check"){
                                          const breakCheckValue = this.masters.find(m => {
                                            m.value();
                                            return this.status !== "check";
                                          });
                                        
                                          if( this.error ) 
                                            throw this.error;
                                        
                                          if(breakCheckValue)
                                           return this.cache;
                                        }
                                        
                                        this.status = "compute";
                                        const result = this.handler();
                                        return this.push( result );
                                        


                                        Почему? Ну во первых ваш пост звучал как вызов:)
                                        Во вторых потому что без метки, а про функционал метки я узнал из этой статьи и вряд ли кто-то в моей команде ещё про этот функционал знает.
                                        • +1

                                          У this.masters нет find ибо это экземпляр Set. Ну и логика у вас сложная и не правильная получилась. Очевидно вы хотели воспользоваться более уместным some, а не find, но всё равно запутались в логических отрицаниях. К тому же проверка this.errors вдруг перестала зависеть от изменения this.status.


                                          Ну а если никто в вашей команде не знает языка, на котором пишет, то у меня для вас плохие новости :-)

                                          • +2
                                            Ну чем является this.masters мне знать не откуда было, вам стоило отметить это. А так да не учёл зависимость проверки ошибки от изменения this.status и перепутал методы, косяк.
                                            Напишу ещё раз:
                                            if(this.status === "check" && allChecked(this.masters)) {
                                                if( this.error ) throw this.error;
                                                return this.cache;
                                            }
                                            
                                            this.status = "compute";
                                            const result = this.handler();
                                            return this.push( result );
                                            
                                            function allChecked(masters){
                                              return [...masters].every(m => {
                                                m.value();
                                                return m.status === "check";
                                              });
                                            }
                                            


                                            Этот код на мой взгляд существенно чище чем ваш.
                                            • –2

                                              Замечательно, кода стало ещё больше и теперь нужно глазами мотать вверх-вниз чтобы понять что тут происходит. А происходит опять что-то не то, ибо вынося код в отдельную функцию вы разумно споткнулись на this.status и "починили" это заменив на m.status. Соответственно и функцию назвали в соответствии с тем, что она делает, а не с тем, что она должна делать.


                                              Кроме того, выносом в отдельный метод вы увеличили размер стека процентов на 25%, ибо код этот находится в рекурсивно вызываемой функции. Тем самым вы увеличили риск получить ошибку переполнения стека, а также замусорили stack trace и flame chart, увеличив их вывод на те же 25%.


                                              Ну и в довершении всего, у вас тут происходит лишнее выделение памяти на создание массива, чтобы проитерироваться лишь по части его элементов. Это существенно замедлило функцию, которая выполняется очень часто.

                                              • +2
                                                Мне очень нравится как вы меня постепенно посвящаете в контекст вашего кода, то masters вдруг Set, то функция часто вызывается, да ещё и рекурсивно… Я блин откуда это знаю!?

                                                Я ставил перед собой задачу написать более читаемый код, который делает тоже самое. Оптимизация это отдельная задача выполняемая по необходимости.

                                                Вы бы вообще над дизайном класса подумали, почему например метод .value() меняет поля чужого класса? Может ему лучше возвращать данные и плевать исключения в случае ошибки, тогда вопросов как написать красивый код будет меньше?

                                                Мотать глазами не надо, спокойно можно читать сверху вниз. (даже после фикса проверки поля status, const self = this;… self.staus === «checked» )

                                                Кстати конвертацию Set`а в массив наверняка js движок оптимизирует и лишняя память не будет выделена. Вызов метода наверное заинлайнится, так что стек не вырастит.
                                                • 0

                                                  Это же вы делаете необоснованные предположения, что this.masters — это масссив, а косвенная рекурсия невозможна. С чего вы это взяли?


                                                  value не меняет поля чужого класса, он лишь запускает пользовательские вычисления, которые могут косвенно инвалидировать кеш. Именно это тут и check-ается. Если кеш не инвалидировался, то мы возвращаем значение из него, иначе вычисляем заново.


                                                  Прыгать глазами надо ровно до тех пор, пока вы не отразите в её названии всё, что она делает. Что по длине будет сопоставимо с её исходным кодом.


                                                  Откуда такая уверенность, что компилятор сумеет развернуть вызов замыкания для каждого элемента из массива созданного из множества в итерирование по элементам множества?


                                                  Давайте подытожим...


                                                  Какие мы заимели проблемы:


                                                  1. Код стал в полтора раза больше.
                                                  2. Вместо нескольких простых условий появилось одно комплексное. Третее в ту же строку добавим?
                                                  3. Добавилась новая функция с названием, не соответствующим действительности.
                                                  4. Замедлилось время исполнения.
                                                  5. Увеличилось потребление стека.
                                                  6. Усложнились отладка (точки останова, исполнение по шагам, лишние элементы в стеке, просмотр значений переменных из разных скоупов)
                                                  7. Усложнился профайлинг (увеличение графика, дробление "собственного" времени, сортировка по "собственному" времени).

                                                  Чего мы добились:


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

                                                  Оно того стоило?

                                                  • +1
                                                    Нет, оно того не стоило. В смысле, добавлять в код эту конструкцию не стоило — тогда и избавляться бы героически не пришлось.
                                                    • 0

                                                      А какие могут быть объективные причины не использовать подобные конструкции, если они не нарушают принципы структурного программирования, и делают код чище, понятнее и зачастую производительнее?

                                        • +1
                                          Вот так можно:

                                          if( this.status === 'check' ) {
                                              for( let master of this.masters ) {
                                                  master.value()
                                                  if( this.status !== 'check' ) break;
                                              }
                                              if( this.status === 'check' ) {
                                                  if( this.error ) throw this.error
                                                  return this.cache
                                              }
                                          }
                                          // ...
                                          


                                          На одно сравнение со строкой больше — зато воспринимается проще.
                                          • 0

                                            Замечательно, теперь у вас две строчки в которые изменения нужно вносить синхронно иначе бо-бо. Ок, после рефакторинга мой код стал таким:


                                            check : if( this.status === 'check' ) {
                                                for( let master of this.masters ) {
                                                    master.value()
                                                    if( this.status === 'obsolete' ) break check
                                                    if( this.status === 'sleep' ) break check
                                                }
                                                if( this.error ) throw this.error
                                                return this.cache
                                            } 

                                            Как вы измените свой?

                                            • 0
                                              Я не буду в своем коде делать усложняющий код рефакторинг.
                                              • 0

                                                Ок, это импрувмент/багфикс, а не рефакторинг. На вопрос-то ответите?

                                              • +3

                                                А вам в целом не кажется, что этот код (и тот что выше) какой-то мутный. Столько всего понамешано, даром, что строк мало. Тут и throw, и метки, и смена статуса по вызову value() (без знания кодовой базы вообще неочевидный момент), и какой-то cache. Мне кажется использование тут меток не является проблемой. Проблемой является вообще существование этого. Что это? Почему оно такое странное? Причина в производительности?

                                                • 0

                                                  Это новая реализация вот этой библиотеки, с поддержкой квантификации вычислений: https://habrahabr.ru/post/317360/
                                                  Сможете реализовать менее "мутно" — я вам спасибо скажу :-)

                                            • 0
                                              function computedValue() {
                                                  this.status = 'compute'
                                                  const result = this.handler()
                                                  return this.push(result)
                                              }
                                              
                                              function value() {
                                                  if (this.status === 'check') {
                                                      for (let master of this.masters) {
                                                          master.value()
                                                          if (this.status !== 'check') return computedValue()
                                                      }
                                                      if (this.error) throw this.error
                                                      return this.cache
                                                  }
                                                  return computedValue()
                                              }
                                              
                                              • –2

                                                Какие мы заимели проблемы:


                                                1. Код стал в полтора раза больше.
                                                2. Функция разделена на 2 без практической ценности (computedValue опасно вызывать где-либо, кроме как в value).
                                                3. Увеличилось потребление стека (опять же косвенная рекурсия).
                                                4. Усложнились отладка.
                                                5. Усложнился профайлинг.

                                                Чего мы добились:


                                                1. Избавились от использования стандартной, но редко необходимой конструкции языка.

                                                Оно того стоило?

                                                • +2
                                                  1. Кода (без учёта скобок и пустых строк) добавилось всего одна строчка: объявление функции. Учтите, что я также явно добавил объявление function value(), которое в вашем коде было опущено.
                                                  2. computedValue() вполне может вызываться где-то ещё, где кэширование явно не нужно. Если её опасно отдавать пользователю, можно сделать её приватной.
                                                  Кроме того, в вашем варианте одна функция занимается двумя вещами: обслуживанием вычисления значения (выставление правильного статуса перед вызовом хэндлера, сохранением результата в правильном месте) и управлением кэшированием. Это нарушение SRP. Если в каком-то другом месте нужно будет вызвать хэндлер, нужно будет опять не забыть, какой выставить статус и куда девать результат.
                                                  3. У вас рекурсия без явного ограничения глубины? Т.е. кто-то может запросто устроить бесконечную рекурсию и его спасёт только стэк оверфлоу?
                                                  А вообще, тут хвостовой вызов, который скорее всего соптимизируется (tail call optimization).
                                                  4. Отладка упростилась, потому что для каждой функции можно написать отдельный юнит-тест, и отлаживать их в принципе не нужно будет. А если кто-то будет раскручивать стек, то по имени функции увидит, какой элемент функциональности выполнялся.
                                                  5. Не вижу усложнения.
                                                  • –1

                                                    2 — value()я не привёл так как приведённый код это далеко не всё её содержимое. В частности весь приведённый код завёрнут в try-catch для перехвата ошибок. Именно поэтому вызывать computedValue из других мест нельзя. Да и ситуаций, когда "кэширование явно не нужно" нету, ибо сам этот класс применяется, когда нужно кеширование. Вы делаете слишком далекоидущие выводы по обрывку кода.


                                                    3 — тут нет хвостового вызова. Хэндлер может обратиться к другим реактивным переменным (и зачастую так и поступает) в которых вызывается эта же функция, но в другом контексте. Циклическая зависимость разумеется отсекается — код для этого располагается выше, но его я опять же не привёл, ибо он не имеет отношения к обсуждаемому вопросу.


                                                    4 — не путайте отладку и тесты. Отладка вступает в дело, когда тесты упали и нужно понять почему. Кроме того юнит тесты бесполезны для коммуникационного модуля. А отладка — это далеко не только "раскручивание стека", но ещё и: точки останова, исполнение по шагам, просмотр значений переменных из разных скоупов (нужно скакать по стеку, чтобы посмотреть их содержимое).


                                                    5 — ну вот если бы вы им пользовались, то заметили бы следующие проблемы: увеличение высоты графика, дробление "собственного" времени на несколько функций и улёт этих функций вниз при сортировке по собственному времени.

                                                    • 0
                                                      3. return computedValue() — вот это вот хвостовой вызов и есть. Других форм вызова computedValue в моём коде нет, так что интерпретатор вправе оптимизировать его, например превратив вызов computedValue() в тот самый break to label который вы подставили руками.
                                                      • 0
                                                        Я, кстати, не спорю, что goto и break to label могут быть использованы для ручной оптимизации, равно как и ручная раскрутка стека и проч., если надеяться на оптимизатор не хочется.
                                          • +3
                                            Как тяжело с пришельцами из других языков…
                                            • 0
                                              В этом виноват WEB. Всем кто работает с WEB`ом приходится работать с js, нравится он им или нет. Альтернатив, пока нет :-\
                                              • +2
                                                Вот только здоровый человек, осваивая язык, читает спеку, а не надеется, что код из ранее изученного языка случайно окажется валидным и в этом языке.
                                            • 0
                                              Ну видимо перекочевало из C.
                                              • +1
                                                Можно чуть больше подробностей? Почему вы так думаете?
                                                • +2

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

                                                • +1
                                                  В дискуссии о вреде goto обычно никаких серьёзных минусов последнего, кроме некоего абстрактного «излишнего зашумления кода» не называется.
                                                  • 0
                                                    Если бы в Си не было goto, то чел, который писал функцию в интерпретаторе PHP из примера выше, напряг бы мозг и написал её нормально. Представьте, что другой наркоман решит добавить для функции php_mblen ещё одно возвращаемое значение: -3 или -1000. И вся эта жесть посыпется как карточный домик. В этом примере код с goto длинный, непонятный, потенциально глючный. Было бы интересно увидеть обратные примеры…
                                                    • 0
                                                      То что кто-то использует язык для написания корявых функций — вовсе не значит что операторы, которые он использует, глючные или не нужные.

                                                      Если бы в Си не было goto, то чел, который писал функцию в интерпретаторе PHP из примера выше, напряг бы мозг и написал её нормально.

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

                                                      Если функция достаточно большая и имеет большое количество последовательно выполняемых ветвлений, без goto код получается просто монструозным, причём даже всё что можно выносить в отдельные функции.

                                                      код с goto длинный, непонятный

                                                      Ну, собственно, о чём я и говорил — никаких объективных минусов никто не называет.

                                                      потенциально глючный

                                                      А так вообще про огромное количество кода можно заявить, особенно если всякие оговорки к этому добавлять.
                                                      • 0
                                                        Судя по минусу в карму я понял, что goto — это ещё одна священная корова, о которой нельзя пренебрежительно отзываться на хабре :)

                                                        А в чем логика моего кода не соответствует исходному?
                                                        • 0
                                                          Минус ставил не я, если что.

                                                          А в чем логика моего кода не соответствует исходному?

                                                          Вы походу чуть ошиблись со скобочками. В оригинале:
                                                          		switch (inc_len) {
                                                          			case -2:
                                                          			case -1:
                                                          				inc_len = 1;
                                                          				php_mb_reset();
                                                          				break;
                                                          			case 0:
                                                          				goto quit_loop;
                                                          			case 1:
                                                          			default:
                                                          				last_chars[0] = last_chars[1];
                                                          				last_chars[1] = *ptr;
                                                          				break;
                                                          		}
                                                          		ptr += inc_len;
                                                          		len -= inc_len;
                                                          	}


                                                          ptr += inc_len;
                                                          len -= inc_len;

                                                          Находятся вне switch, т.е. будут исполнены в любом случае. В вашем же коде они исполнятся только если inc_len > 0
                                                          • 0
                                                            В этом и смысл.

                                                            Для inc_len == 0 эти два выражения как-раз не будут выполнены, потому что там goto, который выводит из цикла.

                                                            Для inc_len == -2 или -1 переменная inc_len устанавливается в 1, и я заменил эти два выражения на более эффективные: ptr++ и len--. Раз, уж, в исходном комментарии шла речь об эффективности.

                                                            Насчет минусов это не лично к вам претензия. Но судя потому, что люди минуснули, они не врубились в логику исходной функции с goto. Что подтверждает то, что в этом примере goto затрудняет понимание. Т.е. вариант без goto тут и понятней, и эффективнее, и короче, и менее глючный.
                                                            • 0
                                                              А что будет если inc_len = -100?
                                                              • 0
                                                                Об этом я уже писал. В индусском коде с goto скорее всего будет segmentation fault. Потому что будет выполнена ветка default. Она очевидно рассчитана только на положительные значения. При -100 всё сломается.

                                                                В моем же варианте любые отрицательные значения (-1, -2, -100), обозначающие что в строке что-то не найдено, интерпретируются одинаково. ptr увеличивается на единицу и поиск продолжается.

                                                                Конечно, в моем варианте логика немного изменилась. Но это просто исправление ошибки наркоманов-авторов PHP.
                                                                • 0
                                                                  В индусском коде с goto скорее всего будет segmentation fault. Потому что будет выполнена ветка default. Она очевидно рассчитана только на положительные значения.

                                                                  Не понимаю с чего вы это взяли.
                                                                  default:
                                                                      last_chars[0] = last_chars[1];
                                                                      last_chars[1] = *ptr;
                                                                      break;

                                                                  Отработает вполне корректно. Ведь если inc_len == -100, ещё не значит, что ptr отрицательный. Далее выполнится:
                                                                  ptr += inc_len;
                                                                  len -= inc_len;

                                                                  И цикл может вполне закончится. В вашем же случае этого не произойдёт.

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

                                                                  И я не пойму с чего вы так уверены, что ваши два эльса работают быстрее чем switch.
                                                                  • –1
                                                                    Цикл не завершится. ptr уменьшится на 100 и возможно станет отрицательным.

                                                                    А len увеличится на 100! При том, что условие выхода из цикла len > 0. На следующей итерации либо php_mblen упадет с segmentation fault. Либо этот цикл будет вечным, потому что len всегда будет положительным.

                                                                    Мой вариант работает быстрее потому, что

                                                                    1) меньше сравнений, у меня 3 ветки (=0, >0, <0), а в варианте со switch 5 веток (-2, -1, 0, 1, default),

                                                                    2) мои сравнения занимают меньше тактов процессора. Я уже 20 лет не писал на ассемблере, но на сколько я помню, сравнение с 0 или определение знака операнда быстрее, чем сравнение с -2, -1, 1.

                                                                    3) в моем варианте для случая inc_len < 0 нет лишнего присваивания inc_len = 1, с последующим прибавлением и вычитанием. Вместо этого инкремент ptr++ и декремент len--. Которые так же занимают меньше тактов процессора, чем прибавление/вычитание.

                                                                    Можно было бы скомпилировать этот код и проверить какие машинные инструкции генерятся, посчитать такты, но это тема для статьи, а не комментария.

                                                                    Насчет изменения логики. Для inc_len >= -2 логика не изменилась. Она изменилась только для inc_len < -2 (чего как вы утверждаете быть не должно), что я сделал сознательно, чтобы исправить ошибку индусов.

                                                                    Вы всё ещё настаиваете, что goto в данном случае — это хорошо? В этой теме по-моему я единственный смог понять как этот индусский код реально работает.
                                                                    • –1
                                                                      Ну для, чёт while(len > 0) я просмотрел, да.

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

                                                                      Хотя я всегда считал что switch работает быстрее if, думаю без ассемблерного кода сравнивать бессмысленно. Собственно, что касается инкремента и декремента — тут вы вероятнее всего ошибаетесь. Подобная форма записи, насколько я знаю, даёт выигрыш, только если используется предикативная форма ++ptr, в противном случае, по скорости ptr++ эквивалентно ptr += inc_len если их формат совпадает.

                                                                      Однако, разговор в общем-то изначально был не об этом. Вполне возможно вы в целом правы и ваш пример лучше. Также вполне возможно, есть какие-то нюансы, которых не учли вы, изменив логику, которые могут выявиться только на тесте.

                                                                      Что касается goto, я нигде не писал, что «в данном случае» это хорошо. Я лишь сказал что есть случаи, в которых без него либо будет плохо, либо вообще не сделать. Просто потому что у меня в продакшене был код, который я пробовал написать и на if и на циклах и на goto. И самый читаемый вариант получился как раз на нём.

                                                                      Всем этим я лишь хочу сказать, что goto это ровно такой же инструмент как и все остальные и пользоваться им надо с умом. А если генерить бредовый код, он всё равно останется бредовым, и не важно как он написан.
                                                                      • 0
                                                                        `++ptr` и `ptr++` в случае обычных указателей исполняются с одинаковой скоростью. Скорость префиксной и постфиксной формы инкремента может отличаться только для перегруженного оператора (и то если оптимизатор проморгает что возвращаемое значение не используется).
                                                                        • 0
                                                                          Почему? Чем для компилятора отличаются обычный и перегруженный ++?
                                                    • 0
                                                      довелось мне лет 15 назад поддерживать код на фортране (кажется 77), написанный немцами, связаться с которыми возможности не было.
                                                      он был весь на го ту.
                                                      тот ещё адок, уж поверьте.
                                                      • 0
                                                        Дык верю, только не факт что без goto его вообще можно было реализовать на фортране, не так ли?
                                                        • 0
                                                          согласен.
                                                          но с тех пор не люблю го ту и ненавижу фортран
                                                          • +1
                                                            а немцев?
                                                            • +2
                                                              Мне их кино нравится. Как их после этого не любить?!)
                                                      • 0

                                                        А минуса два:


                                                        1. непредсказуемые переходы (не очень понятно, куда и при каких условиях перейдет управление). Особенно забавно, если goto в середину какой-нибудь функции прыгает. Не знаю, можно ли так в JS, но и проверять не буду
                                                        2. непредсказуемый контекст. Вот прыгнуло исполнение в какое-то тридцатьтретье место. Переменные и константы теперь какие значения имеют? Надо ходить, смотреть, вспоминать.
                                                        • 0

                                                          При помощи конструкций break <label>; и continue <label>; можно только выходить из блоков, но не входить в них. Тут все же переходы остаются предсказуемыми, хоть и получаются страшно некрасивыми.

                                                          • 0
                                                            Это всего лишь один из способов «выстрелить себе в ногу», коих из без goto с десяток наберётся.
                                                            • 0
                                                              При помощи goto в Lua можно только выходить из блоков. А область видимости меток, емнип, так же блочная.
                                                          • 0
                                                            ну это полезно понимать, читая минифицированный код.
                                                            это почти никогда не надо, но знать полезно.
                                                          • +3
                                                            Знал об этом только потому, что просматриваю минифицированный код, а минификаторы как раз подобным штуками злоупотребляют.
                                                            • 0
                                                              заметка про запятую навела на мысль, но практического применения не смог найти
                                                              const a = (a, b) => (
                                                              a = a + b,
                                                              a * 2
                                                              )

                                                              • +6
                                                                Иногда так использую:
                                                                for(let i = 0; i < n; i++, j++) {
                                                                }
                                                                

                                                                И не только в JS.
                                                                • 0
                                                                  раз пошла такая пьянка, то for можно записать вообще как вызов одной функции — в таком случае цикл будет исполняться, пока функция не вернет false, undefined, null (или вообще ничего не вернет — тогда for завершится после первого прогона)

                                                                          for(; someAction(););  
                                                                  
                                                                          // просто для примера, данный код работоспособен
                                                                          function someAction() {
                                                                              someAction.i = someAction.i || 0; 
                                                                              console.log(someAction.i++);
                                                                              return someAction.i < 10;
                                                                          }
                                                                  
                                                                  • +2
                                                                    Так while же, зачем for? И выглядит понятнее.
                                                                    • +2
                                                                      я не зря сказал «раз пошла такая пьянка»
                                                                      в for можно выполнять что угодно без тела цикла, но это ухудшает читаемость
                                                                      то же самое относится к чудесам с запятой, описанным в статье — если внедрять такую практику, код станет заметно более плохим

                                                                      отвечая на ваш вопрос — да, этот конкретный случай — полный аналог while
                                                                      он лишь служит иллюстрацией, что в любой из трех позиций внутри for можно использовать функции — первая выполнится один раз перед запуском цикла, вторая и третья будут выполняться каждый цикл — вторая перед очередным прогоном, третья после
                                                                    • 0
                                                                      Накладные расходы на вызов функции.
                                                                      • 0
                                                                        согласен — если нам не нужно модифицировать цикл, вызов функции избыточен (лучше цикл уложить внутрь отдельной функции, если нам надо, скажем, повысить читаемость кода)

                                                                        но если нам нужен цикл со «сменным поведением», обороты вроде
                                                                        while(doBehavior());
                                                                        или
                                                                        for(initBehavior();doBehavior();afterDoBehavior());
                                                                        открывают целые бездны гибкой настройки кода, адаптивных алгоритмов и майндфаков
                                                                        • 0

                                                                          Если участок кода станет "горячим" — оптимизатор разберётся, если не станет — этих накладных расходов никто не заметит. Так что в угоду читаемости (а не так как в примере выше) можно.

                                                                      • –1
                                                                        Ну вот же как удобно отсортировать массив.
                                                                        const a = [1,2,3,4,5];
                                                                        for (let i = 0, j = a.length - 1, t; i < j; i++, j--) {
                                                                        	t = a[i];
                                                                        	a[i] = a[j];
                                                                        	a[j] = t;
                                                                        }
                                                                        console.log(a);

                                                                        Правда пришлось ввести дополнительную переменную. В го выглядит красивее, хотя синтаксис там несколько другой:
                                                                        a := []int{1,2,3,4,5}
                                                                        for i, j := 0, len(a) - 1; i < j; i, j = i +1, j - 1 {
                                                                        	a[i], a[j] = a[j], a[i]
                                                                        }
                                                                      • –3
                                                                        Запятая очень часто помогает упростить простые функции не теряя читабельность или делая лучше (именно простые функции!)

                                                                        Из:
                                                                        function toSortReverseArray(str){
                                                                        str = str.split('');
                                                                        str = sort();
                                                                        return str.reverse();
                                                                        }


                                                                        В:
                                                                        function toSortReverseArray(str){
                                                                        return str = str.split(''), str.sort.reverse();
                                                                        }


                                                                        Скорее всего привел плохой пример. Советую посмотреть минифированный код некоторых скриптов, где есть примеры их правильного использования. Запятые часто помогают упростить код, но злоупотреблять не стоит
                                                                        • 0
                                                                          Советую посмотреть минифированный код некоторых скриптов, где есть примеры их правильного использования

                                                                          Всё таки задачи у программиста и у программы-минификатора стоят разные. И довольно сложно в результате работы последней найти что-то полезное для обычного кода.


                                                                          P.S. приведённый вами пример яркий пример почему запятых стоит избегать. Это вообще очень плохая мания — экономить строчки за счёт читаемости и очевидности кода.

                                                                        • 0
                                                                          Минификация, например.
                                                                        • +11
                                                                          ого, спасибо за статью, я уже как раз хотел написать фреймворк, чтобы внедрить в JavaScript все эти возможности
                                                                          • 0
                                                                            Еще не поздно, а еще можно сделать приоритетным использование for in для массивов, без использования при этом hasOwnProperty. Или, к примеру, введя приоритетным создание массивов, объектов и так далее через new Array, new Object… А то что все эти литералы и литералы. Да и не только же для создания строк n-длины использовать, заполненных каким-то символом, с вызовом Array через new.
                                                                          • 0
                                                                            Про void function интересно, в остальном автор Америки не открыл (естественно, речь идёт про меня).
                                                                            • 0
                                                                              Тоже самое, но с оператором ! вместо void:
                                                                              !async function () {
                                                                                  // some code...
                                                                              } ();
                                                                              
                                                                            • 0
                                                                              Пишу void 0 потому что подсвечивается лучше чем undefined. + защита от всяких undefined = true;
                                                                              • 0
                                                                                На счастье, это давным давно исправили. Но запись короче, да.
                                                                              • 0
                                                                                Пригодится для чтения обфусцированного кода, самому такое лучше не применять.
                                                                                • 0
                                                                                  Интересный момент, что я в js никогда особо не углублялся, писал всякие мелкие расширения для хрома, но обо всём этом уже знал. Видимо сказывается, что изучал не «по порядку», а вырывая куски знаний под конкретные вещи :)
                                                                                  • +1
                                                                                    Мне, когда я была зеленым джуном, бывалый фронт-ендер не рекомендовал использовать метки. Мне в реальном коде не довелось их видеть. Может не случайно некоторые возможности языка не так и известны.
                                                                                    • 0
                                                                                      Нельзя использовать goto, а вот метки, чтобы выходить из внешнего цикла — вопрос открытый.
                                                                                    • 0
                                                                                      не использую
                                                                                      .querySelector
                                                                                      , пропущу почему, использую
                                                                                      .getElementsByTagName[tag][0]
                                                                                      ,
                                                                                      .getElementsByClassName[class][0]
                                                                                      ,
                                                                                      .getElementById(id)
                                                                                      и
                                                                                      .querySelectorAll
                                                                                      теми же средствами, хотя это не совсем к теме, скорее к примерам.
                                                                                      • +1

                                                                                        Да нет уж, вы не пропускайте и обоснуйте, почему пишете на js как в 2005 и считаете это правильным.

                                                                                        • +2
                                                                                          быстрее (погуглите тесты jsperf), отлавливают элементы созданные на лету, «не обертка», в нашем коллективе приводит к консенсусу чем выбирать элемент/массив/коллекцию, быстро конвертирует в библиотеку и прочее. Не торопитесь судить, я с вами в этом же году живу и опыт у меня с 2006 в вэб-разработке. Не считаю правильным поэтому написал использую а не используйте.
                                                                                          • 0

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

                                                                                            • 0
                                                                                              .querySelector полезен при сложной/ленивой выборке. В реальных проектах вам приходится так все структурировать, чтобы не было подобных ситуаций. Плюс — консистенси — маленький или большой проект — выбираем теми же методами. Получается, что .querySelector красиво, но только для псевдокода/демок, оговорюсь, в нашей команде.
                                                                                      • +1

                                                                                        Передача аргументов в setTimeoutдоступна была не всегда, а, например, с IE9. Об этом как раз написано на MDN.

                                                                                        • 0
                                                                                          let personEl = document.querySelector('#person');
                                                                                          console.log(person.dataset)
                                                                                          В чем отличие personEl и person?
                                                                                          • 0
                                                                                            очепятались
                                                                                            • +1
                                                                                              Да, видимо опечатка. А при отладке не заметили, потому что в браузере доступны глобальные переменные с именами по id у элементов. Т.е. для элемента с #person браузер автоматически создаёт глобальную переменную person.
                                                                                              • +1
                                                                                                для элемента с #person браузер автоматически создаёт глобальную переменную person.

                                                                                                поэтому и проскочила person. Спасибо.
                                                                                                • 0
                                                                                                  Интересно когда понадобится делать let personEl = document.querySelector('#person'); если автоматом создается person?
                                                                                                  Не во всех браузерах автоматом создается person?
                                                                                                  • +1
                                                                                                    Использовать глобальные переменные по id элементов плохая практика по двум причинам:

                                                                                                    1. Потому что они глобальные. Если в вашем коде будет другая глобальная переменная person, то код, который работал с элементом #person, сломается.

                                                                                                    2. Изначально не все браузеры это поддерживали. Первым был IE, остальные подтянулись для совместимости.

                                                                                                    В общем не надо так делать. Знать об этом полезно, использовать — нет.
                                                                                              • +1
                                                                                                for (a = 0, b= 0; a < N; ++a, ++b) — и тут запятая в двух местах — вполне легитимная
                                                                                                if (let x = Something(), x > 0) — ограничим область видимости подобно переменной для for

                                                                                                for (var e = document.getElementById('smth'; e; e = e.parentNode) — итерация вверх по дому
                                                                                                • –1
                                                                                                  if (let x = Something(), x > 0)

                                                                                                  ?
                                                                                                  if (Something() > 0) 

                                                                                                  • 0
                                                                                                    В вашем варианте куда-то пропала переменная.
                                                                                                    • 0
                                                                                                      Так ведь в том и смысл, что не ясно для чего её вообще использовать в таком ключе.
                                                                                                      • 0
                                                                                                        Как это не ясно? Переменную можно использовать для чего угодно.

                                                                                                        Ну вот вам более конкретный код:

                                                                                                        if (let x = foo.indexOf(bar), x > -1) {
                                                                                                            bar.splice(x, 1);
                                                                                                        }


                                                                                                        Как вы здесь без переменной обойдетесь?

                                                                                                        PS кстати, в каких версиях языка так можно делать?
                                                                                                        • 0
                                                                                                          Ой, да, действительно. Мой промах… Как в for же, один в один.
                                                                                                          кстати, в каких версиях языка так можно делать?

                                                                                                          Это хороший вопрос, учитывая что последний хром такое отказывается исполнять. Может a-tk скажет?

                                                                                                          Вообще, эта конструкция, конечно, выглядит как что-то вредное с точки зрения читаемости. Я б такое не стал писать… Но пример забавный. Хотел написать «жаль, не рабочий», но нет, как-то и не жаль даже.
                                                                                                • +2
                                                                                                  Я не понял, зачем автор говорил об атомарных операциях. Я не слышал ни об одном многопоточном рантайме. А с веб-воркерами нужно атомарность не в язык, а в апи хранения (типа indexed) добавлять.
                                                                                                • 0

                                                                                                  Автор, ты мой герой, теперь я знаю больше.

                                                                                                  • +1

                                                                                                    Когда я был маленький и учил C++, я очень радовался, что там можно переопределить operator,() так, чтобы (x, y) обозначало скалярное произведение векторов x и y. Потому что всякие математики его часто именно так обозначают, а не точечкой и не треугольными скобочками. Правда, скобочки тут не нужны, но из-за приоритета всё равно нужны. P.S. не делайте так :/

                                                                                                    • +2

                                                                                                      Предлагаю хабру запретить анимированные картинки до ката.

                                                                                                      • 0
                                                                                                        void можно использовать для сокращения проверки на undefined:
                                                                                                        if(typeof myVar=='undefined')

                                                                                                        превращается в
                                                                                                        if(myVar== void 0)


                                                                                                        Правда, может усложнить читабельность тем, кто не знает про void (если в документе по стилю кода этот момент есть, то, как по мне, допустимо).

                                                                                                        А вот запятая это хороший элемент для минификаторов кода, читабельность у нее однозначно нулевая.

                                                                                                        За доп. параметры setTimeout спасибо. Не уверен что стоит так писать с точки зрения понятность кода, но каких-то набросков вполне сгодится.
                                                                                                        • +1
                                                                                                          Не совсем верно, в данном случае лучше использовать "==="

                                                                                                          const myVar = null;
                                                                                                          
                                                                                                          myVar == void 0; // true
                                                                                                          
                                                                                                          typeof myVar == 'undefined'; // false
                                                                                                          
                                                                                                          • 0
                                                                                                            да, верно, ===
                                                                                                          • +1
                                                                                                            if(myVar === undefined)
                                                                                                            читаемее.
                                                                                                          • 0
                                                                                                            Прочитал статью, понял что знаю о всех перечисленых особенностях. Теперь сижу и думаю хорошо это или плохо.
                                                                                                            • 0
                                                                                                              местами очень спорные конструкции. особенно запятая в условном плохо читается.
                                                                                                              и с async както странно…
                                                                                                              • 0
                                                                                                                Спасибо! Хорошая побдорка! Вебшторм на запятую в тернарнике говорит overly clever code )

                                                                                                                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.