12 приемов работы с JavaScript, которых нет в большинстве туториалов

Автор оригинала: Bret Cameron
  • Перевод


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

В этой статье я покажу 12 отличных способов улучшить и ускорить свой JavaScript-код. В большинстве случаев они универсальны.

Напоминаем: для всех читателей «Хабра» — скидка 10 000 рублей при записи на любой курс Skillbox по промокоду «Хабр».

Skillbox рекомендует: Практический курс «Мобильный разработчик PRO».

Фильтрация уникальных значений


МАССИВЫ

Тип объекта Set был введен в ES6, вместе с ..., spread-оператором, мы можем использовать его для создания нового массива, в котором содержатся лишь уникальные значения.

const array = [1, 1, 2, 3, 5, 5, 1]
const uniqueArray = [...new Set(array)];
 
console.log(uniqueArray); // Result: [1, 2, 3, 5]

В обычной ситуации для выполнения той же операции нужно гораздо больше кода.

Этот прием работает для массивов, содержащих примитивные типы: undefined, null, boolean, string и number. Если вы работаете с массивом, содержащим объекты, функции или дополнительные массивы, вам понадобится другой подход.

Длина кэш-массива в циклах


ЦИКЛЫ

Когда вы изучаеете for для циклов, то следуете стандартной процедуре:

for (let i = 0; i < array.length; i++){
  console.log(i);
}

Тем не менее, при таком синтаксисе цикл for повторно проверяет длину массива при каждой итерации.

Иногда это может быть полезно, но в большинстве случаев эффективнее кэшировать длину массива, что потребует одного обращения к нему. Мы можем сделать это путем определения переменной длины, где задать переменную i, например, так:

for (let i = 0, length = array.length; i < length; i++){
  console.log(i);
}

В принципе, почти то же самое, что и выше, но при увеличении размера цикла мы получим значительную экономию времени.

Оценка короткого замыкания (оценка Маккарти)


УСЛОВНЫЕ ОПЕРАТОРЫ

Тернарный оператор — быстрый и эффективный способ написать простые (а иногда и не очень простые) conditional statements:

х> 100? «больше 100»: «меньше 100»;
х> 100? (x> 200? «больше 200»: «между 100-200»): «меньше 100»;

Но иногда даже тернарный оператор сложнее, чем требуется. Вместо него мы можем использовать ‘and’ && и ‘or’ || логические операторы для оценки некоторых выражений еще более кратким способом. Его часто называют «коротким замыканием» или «оценкой короткого замыкания».

Как это работает

Скажем, мы хотим вернуть только одно из двух или более условий.

Использование && вернет первое false значение. Если каждый операнд оценивается как true, то будет возвращено последнее вычисленное выражение.

let one = 1, two = 2, three = 3;
console.log(one && two && three); // Result: 3
 
console.log(0 && null); // Result: 0

Использование || позволит вернуть первое true значение. Если каждый операнд оценивается как false, то будет возвращено последнее вычисленное значение.

let one = 1, two = 2, three = 3;
console.log(one || two || three); // Result: 1
 
console.log(0 || null); // Result: null

Пример 1

Скажем, мы хотим вернуть length переменной, но не знаем ее тип.

В этом случае можно использовать if/else для проверки того, что foo — подходящий тип, но этот способ может оказаться слишком долгим. Поэтому лучше возьмем наше «короткое замыкание».

return (foo || []).length;

Если переменная foo имеет подходящую length, то она и будет возвращена. В противном случае мы получим 0.

Пример 2

Были ли у вас проблемы с доступом к вложенному объекту? Вы можете не знать, существует ли объект или одно из его подсвойств, и это может привести к проблемам.

К примеру, мы хотели получить доступ к свойству data в this.state, но data не определено, пока наша программа не вернет запрос на выборку.

В зависимости от того, где мы его используем, вызов this.state.data может помешать запуску приложения. Чтобы решить проблему, мы могли бы обернуть это в условное выражение:

if (this.state.data) {
  return this.state.data;
} else {
  return 'Fetching Data';
}

Более подходящим вариантом будет использование оператора «or».

return (this.state.data || 'Fetching Data');

Мы не можем изменить код выше, чтобы использовать &&. Оператор 'Fetching Data' && this.state.data вернет this.state.data независимо от того, является он undefined или нет.

Опциональная цепочка

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

Например, мы могли бы выполнить рефакторинг примера выше, получив this.state.data?.. (). То есть data возвращается лишь в том случае, если значение не null.

Или, если важно, определено ли state или нет, мы могли бы вернуть this.state?.data.

Преобразование в Boolean


ПРЕОБРАЗОВАНИЕ ТИПОВ

Кроме обычных логических функций true и false, JavaScript также рассматривает все другие значения как truthy или falsy.

Пока не указано иное, все значения в JavaScript — truthy, за исключением 0, "", null, undefined, NaN и, конечно, false. Последние являются falsy.

Мы можем легко переключаться между теми и другими, используя оператор !, который также преобразует тип в логический.

const isTrue  = !0;
const isFalse = !1;
const alsoFalse = !!0;
 
console.log(isTrue); // Result: true
console.log(typeof true); // Result: "boolean"

Преобразование в строку


ПРЕОБРАЗОВАНИЕ ТИПОВ

Быстрое преобразование целого числа в строку можно выполнить следующим образом.

const val = 1 + "";
 
console.log(val); // Result: "1"
console.log(typeof val); // Result: "string"

Преобразование в целое число


ПРЕОБРАЗОВАНИЕ ТИПОВ

Обратное преобразование выполняем так.

let int = "15";
int = +int;
 
console.log(int); // Result: 15
console.log(typeof int); Result: "number"

Этот способ может быть использован и для преобразования логического типа данных boolean в обычные числовые значения, как показано ниже:

console.log(+true);  // Return: 1
console.log(+false); // Return: 0

Могут быть ситуации, когда + будет интерпретироваться как оператор конкатенации, а не сложения. Во избежание этого стоит использовать тильды: ~~. Этот оператор эквивалентен выражению -n-1. К примеру, ~ 15 равно -16.

Использование двух тильд подряд сводит на нет операцию, потому что — (- — n — 1) — 1 = n + 1 — 1 = n. Другими словами, ~ -16 равно 15.

const int = ~~"15"
console.log(int); // Result: 15
console.log(typeof int); Result: "number"

<Quick Powers


ОПЕРАЦИИ

Начиная с ES7, можно использовать оператор возведения в степень ** в качестве сокращения для степеней. Это гораздо быстрее, чем использование Math.pow (2, 3). Вроде просто, но в список приемов этот момент включен, поскольку далеко не везде он упоминается.

console.log(2 ** 3); // Result: 8

Не стоит путать его с символом ^, который обычно используется для возведения в степень. Но вот в JavaScript это оператор XOR.

До ES7 сокращение ** можно было применять только для степеней с основанием 2 с использованием оператора побитового сдвига влево <<:

Math.pow(2, n);
2 << (n - 1);
2**n;

К примеру, 2 << 3 = 16 эквивалентно выражению 2 ** 4 = 16.

Float в целое число


ОПЕРАЦИИ / ПРЕОБРАЗОВАНИЕ ТИПОВ

При необходимости преобразовать float в целое число можно воспользоваться Math.floor(), Math.ceil() или Math.round(). Но есть и более быстрый путь, для этого используем |, то есть оператор OR.

console.log(23.9 | 0);  // Result: 23
console.log(-23.9 | 0); // Result: -23

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

n | 0 удаляет все, что идет после десятичного разделителя, усекая число с плавающей точкой до целого числа.

Вы можете получить тот же эффект округления, используя ~~. После принудительного преобразования в целое число значение остается неизменным.

Убираем замыкающие числа

Оператор OR можно использовать для того, чтобы убрать любое количество цифр из числа. Это означает, что нам не нужно преобразовывать типы, как здесь:

let str = "1553";
Number(str.substring(0, str.length - 1));

Вместо этого просто прописываем:
console.log(1553 / 10   | 0)  // Result: 155
console.log(1553 / 100  | 0)  // Result: 15
console.log(1553 / 1000 | 0)  // Result: 1

Автоматическое связывание


КЛАССЫ

Стрелочные обозначения ES6 можно использовать в методах класса, и при этом подразумевается привязка. Благодаря этому можно попрощаться с повторяющимися выражениями, такими как this.myMethod = this.myMethod.bind (this)!

import React, { Component } from React;
 
export default class App extends Compononent {
  constructor(props) {
  super(props);
  this.state = {};
  }
 
myMethod = () => {
    // This method is bound implicitly!
  }
 
render() {
    return (
      <>
        <div>
          {this.myMethod()}
        </div>
      </>
    )
  }
};

Обрезка массива


МАССИВЫ

Если вам необходимо убрать значения из массива, то есть более быстрые методы, чем splice().

К примеру, если вы знаете размер оригинального массива, то можете переопределить его свойство length следующим образом:

let array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
array.length = 4;
 
console.log(array); // Result: [0, 1, 2, 3]

Но есть и еще один метод, причем более быстрый. Если для вас имеет значение именно скорость, то вот наш выбор:

let array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
array = array.slice(0, 4);
 
console.log(array); // Result: [0, 1, 2, 3]

Вывод последнего значения (значений) массива


МАССИВЫ
Этот прием требует использования метода slice().

let array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
 
console.log(array.slice(-1)); // Result: [9]
console.log(array.slice(-2)); // Result: [8, 9]
console.log(array.slice(-3)); // Result: [7, 8, 9]

Форматирование JSON-кода


JSON

Возможно, вы уже использовали JSON.stringify. Знаете ли вы, что он помогает форматировать ваш JSON?

Метод stringify () принимает два необязательных параметра: функцию replacer, которую можно использовать для фильтрации отображаемого JSON, и значение space.

console.log(JSON.stringify({ alpha: 'A', beta: 'B' }, null, '\t'));
 
// Result:
// '{
//     "alpha": A,
//     "beta": B
// }'

Вот и все, надеюсь, что все указанные приемы были полезны. А какие хитрости знаете вы? Напишите их в комментариях.

Skillbox рекомендует:

Skillbox
169,00
Онлайн-университет профессий будущего
Поделиться публикацией

Комментарии 85

    +4
    Фильтрация уникальных значений

    лучше делать Array.from чем spread, он работает быстрее

    Длина кэш-массива в циклах

    неплохо было бы хоть какието цифры по производительности дать
      0
      «лучше делать Array.from чем spread, он работает быстрее» — нет
      +2
      а их уже нет) в хроме без кеширования вроде даже быстрее работает)))
        0
        неплохо было бы хоть какието цифры по производительности дать

        Цифры будут зависеть от интерпретатора, его версии, размера массива. Вот тут как раз оценивают эту задачу, но для свежего хрома [...Set]/Array.from(Set) уже самый быстрый.
        +24

        Шёл 2019-й год.
        Мамкины оптимизаторы всё ещё советуют:


        1. Использовать for, а не forEach;
        2. Выносить подсчёт длины массива в for в инициализацию цикла.
        3. Заниматься микрооптимизациями.

        Ну серьёзно, даже при итерации по массиву с несколькими тысячами элементов профита почти не будет.
        Пруф: https://jsperf.com/for-vs-length

          +3
          Мой Chrome показал, что optimized loop *медленнее* на 2% =)
            +1

            Лучше смотреть скомпилированый результат, что то типа такого


            Cached
            [generated bytecode for function: cached]
              144 E> 0000003E5C39D9CA @    0 : a5                StackCheck 
              170 S> 0000003E5C39D9CB @    1 : 28 02 00 00       LdaNamedProperty a0, [0], [0]
                     0000003E5C39D9CF @    5 : 26 fb             Star r0
              194 S> 0000003E5C39D9D1 @    7 : 0b                LdaZero 
                     0000003E5C39D9D2 @    8 : 26 fa             Star r1
              199 S> 0000003E5C39D9D4 @   10 : 25 fb             Ldar r0
              199 E> 0000003E5C39D9D6 @   12 : 69 fa 02          TestLessThan r1, [2]
                     0000003E5C39D9D9 @   15 : 99 14             JumpIfFalse [20] (0000003E5C39D9ED @ 35)
              181 E> 0000003E5C39D9DB @   17 : a5                StackCheck 
              221 S> 0000003E5C39D9DC @   18 : 25 fa             Ldar r1
              230 E> 0000003E5C39D9DE @   20 : 2a 02 03          LdaKeyedProperty a0, [3]
              226 E> 0000003E5C39D9E1 @   23 : 15 01 05          StaGlobal [1], [5]
              210 S> 0000003E5C39D9E4 @   26 : 25 fa             Ldar r1
                     0000003E5C39D9E6 @   28 : 4c 07             Inc [7]
                     0000003E5C39D9E8 @   30 : 26 fa             Star r1
                     0000003E5C39D9EA @   32 : 8a 16 00          JumpLoop [22], [0] (0000003E5C39D9D4 @ 10)
                     0000003E5C39D9ED @   35 : 0d                LdaUndefined 
              241 S> 0000003E5C39D9EE @   36 : a9                Return 

            Raw
            [generated bytecode for function: raw]
               51 E> 000003D17629D9CA @    0 : a5                StackCheck 
               74 S> 000003D17629D9CB @    1 : 0b                LdaZero 
                     000003D17629D9CC @    2 : 26 fb             Star r0
               84 S> 000003D17629D9CE @    4 : 28 02 00 00       LdaNamedProperty a0, [0], [0]
               79 E> 000003D17629D9D2 @    8 : 69 fb 02          TestLessThan r0, [2]
                     000003D17629D9D5 @   11 : 99 14             JumpIfFalse [20] (000003D17629D9E9 @ 31)
               61 E> 000003D17629D9D7 @   13 : a5                StackCheck 
              104 S> 000003D17629D9D8 @   14 : 25 fb             Ldar r0
              113 E> 000003D17629D9DA @   16 : 2a 02 03          LdaKeyedProperty a0, [3]
              109 E> 000003D17629D9DD @   19 : 15 01 05          StaGlobal [1], [5]
               93 S> 000003D17629D9E0 @   22 : 25 fb             Ldar r0
                     000003D17629D9E2 @   24 : 4c 07             Inc [7]
                     000003D17629D9E4 @   26 : 26 fb             Star r0
                     000003D17629D9E6 @   28 : 8a 18 00          JumpLoop [24], [0] (000003D17629D9CE @ 4)
                     000003D17629D9E9 @   31 : 0d                LdaUndefined 
              124 S> 000003D17629D9EA @   32 : a9                Return 
            Constant pool (size = 2)
            Handler Table (size = 0)

            Как-то копался, но так и не докопал… :(

              0
              Подскажите пожалуйста каким образом просмотреть такой байт код для javascript?
                0
                Я вам обязательно отвечу, но уже завтра, извините.
                  +6
                  node --print-bytecode --print-bytecode-filter=func_name my_js_file

                  где:


                  • func_name = имя функции которую вы хотите изучить
                  • my_js_file имя вашего файла с js
              0

              При более большом массиве, разница еще сильнее https://jsperf.com/for-vs-length/21 у меня оптимизированный вариант на 21% медленнее.
              UPD: странно, но тот же самый код в консоле того же браузера выдает другие результаты


              var array = new Array();
              for (var i = 0; i < 100000000; i++) {
                array.push(i);
              }
              
              console.time('normal');
              var dummy = 0;
              for (var i = 0; i < array.length; i++) {
                dummy += array[i];
              }
              console.timeEnd('normal');
              
              console.time('optimize');
              var dummy = 0;
              for (var i = 0, al = array.length; i < al; i++) {
                dummy += array[i];
              }
              console.timeEnd('optimize');

              normal: 2212.85888671875ms
              optimize: 1148.304931640625ms


              почти в два раза "оптимизированый" вариант быстрее обычного. Либо jsperf что-то химичит, либо я что-то не понимаю.

                +1
                чтобы функция была обработана JIT, ее нужно вызвать несколько раз. а у вас код в консоли только 1 раз запускается.
              +4
              Ну, на счет for vs forEach я бы не был так категоричен.
                +2

                Немного мамкиной аналитики:


                https://tinyurl.com/y3cobd4e — если ваш код соптимизируется, то (1) даст 20 раз разницы, а (2) не даст ничего.


                https://tinyurl.com/y3c5pllq — если ваш код не соптимизируется, то (1) даст 2 раза разницы, а (2) — даст 15%

                  0
                  array.map( v => { dummy{#} += v } )

                  Это еще быстрее

                    0
                    Да нет, медленнее: tinyurl.com/yyeghea9
                      +1
                      Ну вот каким образом Array.map может быть быстрее, чем Array.forEach?! Хотя бы даже теоретически?
                        0

                        Посыпаю голову пеплом: думал, что чем меньше графа, тем лучше. array.map самый медленный.

                          0
                          А что это вообще за конструкция — dummy{#}? Впервые такую вижу.
                            0
                            Или какие-то литералы с нового стандарта или шаблонизатор какой-то на сайте работает.
                              0
                              Да, обычный плейсхолдер. Подставляет номер итерации. Нужен так как код копипастится много раз в одном и том же скоупе.
                          0
                          Кнопку New benchmark нужно сделать более явной — я был искоенне уверен, что там написано "+ Optimized" (обычная плюс оптимизированная версии).
                            0
                            Там же расстояние большое между ними.
                              0
                              Сам плюс очень мелкий, он в принципе не воспринимается как кнопка. Что мешает сделать вместо иконок нормальные кнопки с текстом? Там же места навалом.
                                0
                                Да впринципе ничего, так и сделаю.
                          0
                          А вот почему нет?! Хуже то от этого не будет точно.
                            0

                            Лол, насчет длины массива это плохой совет, согласен, а вот насчет того, что между фор и форич нет разницы это бред. Во-первых, начнем с того, что фор гибче форича в плане брейка. Форич не брейкнешь просто так. Во-вторых, это функция, это отдельный LE-обьект в виде ее хвоста и прочий кложур. Ну а в третьих, это не микрооптимизация. Это как раз дохрена какая оптимизация. Дело в том, что часто почему то разрабы думают, что если там не ядерная физика под капотом какой-то функции, то вроде бы и все норм, оптимизировать не надо. Только вот одна функция может вызывать другую, а та третью и в конце концов случается, что одна какая-то маленькая функция, внутри которой форич, вызывается милион раз во время скрипта. И как раз если бы там был фор, то, возможно этот миллион работал бы быстрее. Безусловно, фор это императивщина. И не надо ее писать везде или писать нечитаемые циклы. Но, бл*дь, не надо их бояться.

                              0
                              Вместо forEach можно юзать some и every, они брейкаются возвращением true / false. Но в остальном согласен.
                            +7
                            Половина триксов из разряда «я поинтерисовался как это делают нормальные программисты»
                              +15
                              А другая половина из разряда «никогда так не делайте» :)
                              +24

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


                              Серьезно, сэкономить 8 байт и несколько секунд на parseInt() или Number(), чтобы потом потратить час, гадая что значит в данном контексте конструкция ~~someVar.

                                +3
                                Всё в учебниках есть. Про Set в роли uniq так хрестоматийный пример. На самом деле статью следовало назвать «12 приёмов, которые есть в каждом туториале, но было лень разбираться». Даже ограничения не указаны. Например, битовые операции ограничены 32-битным целым.
                                  +1
                                  Да уж, код с такими оптимизациями превращается в худшие примеры на Perl.
                                    –12
                                    !!! и ~~ настолько широко распространены, что уже давно перешли в разряд де-факто узаконенных хаков. Любой JS-программист видел их бесчетное количество раз и никогда не гадает над значением, оно общеизвестно.

                                    P.S. Я не знаю, почему хабрапарсер превращает 2 восклицательных знака в 3.
                                      +1

                                      Про хак с ~~ я, наример, не знал, хоть и работаю разработчиком уже 14 лет. Видимо, не в тех командах я работаю :)


                                      Но тут вопрос в другом: зачем вообще использовать эти хаки и ухудшать читабельность кода, если есть полноценные, понятные даже людям незнакомым с JS, конструкции? В чем сакральный смысл выполнять работу минификатора?

                                        –5
                                        У меня в голове не укладывается, как можно было за 14 лет его не узнать. Он используется в чертовой прорве статей, библиотек и проектов. Разве что, сидя в подвале, пилить один проект и не высовывать носа на гитхаб.

                                        С хаками дело такое. В большинстве случаев, согласен — незачем. Но некоторые из них оказываются настолько простыми и удобными, что выстреливают — становятся общеизвестными и де-факто как бы новыми операторами. На уровне соглашений, по типу подчеркивания у приватных свойств или доллара у ссылок на дом-элементы.
                                          0

                                          Насколько я понимаю эти хаки появились с появлением asm.js и началом компиляции нативного кода в js/wasm. Ребята из мозиллы будучи осведомленными об особенностях своего движка нагенерили хаков типа приведения дробных чисел к целочисленым или использование вычитания вместо сложения — дабы увеличить производительность. А оно еще и в хроме заодно заработало быстрее. С тех пор вот и кочуют все эти штуки по чудо гайдам на 12 шагов.

                                            0
                                            Ну тут всё просто.

                                            Во-первых, хаки с ~indexOf, ~~ и |0 действительно удобны когда ты пишешь код на коленке, или тебе нужно например по-быстрому что-то проверить или что-нибудь такое. Вставить в конец |0 гораааздо быстрее и проще, чем писать Math.floor(, затем переносить кусок в конец кода и ставить ). И |0 это одна из самых удобных вещей, что я вообще использую когда пишу код по-быстрому на коленке для себя :)
                                            Это случай, когда над кодом работаешь только ты, и всё в нём понимаешь, хочу заметить.

                                            Во-вторых, на самом деле писать !!value или +value или value|0 вместо Boolean(value) / Number(value) / Math.floor(value) совсем не ухудшает читаемость если ты знаешь, что это. Думаю, что про !!value, +value и value + '' знают примерно все, а вот использовать |0 в каком-то реальном коде естественно содержит много минусов вроде того, что тебе придётся предупреждать всех читающих код, что такие конструкции значат.
                                        +1

                                        Есть такой вариант:


                                        for (let i = array.length - 1; i >= 0; i--){ console.log(i); }
                                          +1
                                          О, я такое использую для splice (удаление из массива) по определенному условию, например. Иначе в нормальном порядке может получится так, что очередной индекс не существует. Хотя сомневаюсь, все равно, что это хорошая мысль. Может быть лучше использовать reduce?
                                          +4
                                          Хорошая рекомендация использовать понятный простой код без «финтиплюшек». Думайте о тех кто будет смотреть твой код после тебя ;)
                                            0
                                            а чем плох код
                                            obj && obj.method();

                                            в if он точно такой же. что непонятного?
                                            || тоже периодически используется как показано в статье.
                                              +3

                                              || плох тем, что он не различает 0, null, undefined, false. Когда разработчик знает, что делает, знает какие данные он присылает, проблем нет.

                                                +1

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


                                                if (control.isEnabled()) { ... } 

                                                Написал:


                                                if (control.isEnabled) { ... }

                                                Да, к сожалению, даже отсутствие подобных хаков не позволяет избегать таких ошибок. Зато правило strict-boolean-expressions в tslint работает исправно.

                                              0
                                              const uniqueArray = [...new Set(array)];

                                              Добавлю, например, если вы хотите сделать конструкцию


                                              const array = (new Array(10)).map((_, ind) => ind)

                                              Результат удивит, потому что в итоге будет не [0,1,2,3,4,5,6,7,8,9], а массив из 10-ти эфимерных empty. Чтобы этого не происходило, нужно использовать spread оператор, а еще лучше завернуть это в функцию, вроде


                                              function createArray(length) {
                                                 return [...new Array(length)];
                                              }

                                              Натолкнулся, когда захотелось сделать код в очень функциональном стиле зачем-то.


                                              Автоматическое связывание

                                              Долго пытался понять, что это значит. Никто так не говорят, все говорят "привязка контекста".


                                              myMethod = () => {
                                              // This method is bound implicitly!
                                              }

                                              С этим кодом есть несколько проблем. Основная — при наследовании нельзя будет вызвать super.myMethod, а так же функция пересоздается каждый раз при создании экземпляра класса, что тратит время и память. И её нет в прототипе.


                                              К слову, state тоже можно так же инициировать, вне конструктора.

                                                +1
                                                Здесь все примеры имеют либо неочевидные сайд-эффекты, либо сомнительную эффективность, либо плохую читаемость.
                                                  +1
                                                  нужно использовать spread оператор, а еще лучше завернуть это в функцию

                                                  Гораздо проще можно сделать Array.from({ length: 10 }).map((x, y) => y)


                                                  Долго пытался понять, что это значит.

                                                  контекст это this. В обычной функции вы можете управлять this, в стрелочной он прибит гвоздями.

                                                    +1

                                                    У Array.from() второй аргумент — функция маппинга. Незачем писать отдельный .map().


                                                    Array.from({ length: 10 }, (i, n) => n) // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

                                                    А вообще такой же результат будет и от [...Array(10).keys()].

                                                      0

                                                      Только вот [...Array(10).keys()] создаёт 3 массива:


                                                      • Array(10)
                                                      • keys()
                                                      • финальный
                                                        не помню ещё как спред внутри работает, может тоже кучу промежуточных сущностей создаёт.
                                                        0
                                                        • В Array(10) от массива разве что название. Он пуст, это эквивалент {length: 10}, только в виде инстанса массива. Ну, и да, функцию маппинга тоже надо создать, не стоит этого забывать.
                                                        • keys() не массив, метод создаёт итератор, с помощью которого и разворачивается финальный массив.
                                                    0
                                                    или const array = (new Array(10)).fill(какое-нибудь значение).map((_, ind) => ind)
                                                      0

                                                      На самом деле читаем документацию: «new Array(n) создаёт массив без элементов, но с заданной длиной».
                                                      Согласен, это не совсем логично, но это особенность.


                                                      Решение:
                                                      const array = new Array(10).fill(0).map((_, ind) => ind)

                                                        0
                                                        Тут особенность не new Array(n) а map, который не вызывает колбэк для не заданных индексов, если написать обычный for все будет норм.
                                                      0
                                                      Пожалуйста ни в коем случае не показывайте это начинающим разработчикам!
                                                        +3
                                                        Если переменная foo имеет подходящую length, то она и будет возвращена. В противном случае мы получим 0.
                                                        const foo = 5;
                                                        (foo || []).length; // undefined
                                                        
                                                        const bar = true;
                                                        (bar || []).length; // undefined
                                                          +5
                                                          Статью надо было назвать «12 шагов к нечитаемому коду».
                                                            0

                                                            Омг, если вам кажется, что такой код трудно читать, значит вы код наверное вчера писать начали. Я просто поражаюсь когда какой-то сниппет, едва ли на 1% труднее примеров с MDN начинают поливать грязью, что, мол, такое читать невозможно. Просто фейспалм вызывает такое. Нет, я не говорю, что надо писать сразу обфусцированную хреновину, но, епрст, закешировать длину массива в цикле — это каждому школьнику известно и читается очень просто. Если для вас, конкретно, нет — то это не "нечитаемый код", а малый опыт чтения кода или малый опыт работы с этим языком.

                                                              0

                                                              Скорее проблема здесь в том, что эти советы (некоторые из них) бесполезны. Например тот же кеш длины массива — это делают все движки уже на фоне и так. Также про то, что ~~ или другие битовые операции снимают дробную часть. Это верно, но нигде не сказано, что это не то же самое, что Math.floor. Диапазон чисел для Math.floor гораздо шире, чем для операторов и использовать его (тот или иной оператор) можно только тогда, когда на 100% знаешь, что число не будет слишком большим.

                                                                0
                                                                кеш длины массива — это делают все движки уже на фоне и так
                                                                Если JIT решил, что функция горячая и поддается оптимизации — да. Если нет, то извините.
                                                              +3
                                                              12 приемов работы с JavaScript, которых нет в большинстве туториалов
                                                              Ну правильно, так как туториалы обычно стараются не учить плохому :)

                                                              А статью лучше переименовать в «12 вредных советов» ну или «Как получить от пи#й от коллег при ревью кода на JS» )
                                                                0
                                                                Так, символ знака вопроса? может использоваться для извлечения свойства, только если оно не равно нулю (null).

                                                                Нет, не может. В JS нет safe navigation operator. Автор, вы про что? В вопросик умеет CoffeeScript и ангуляр. Больше никто в него не умеет. В TS может быть когда-нибудь добавят, но пока нет.
                                                                  +4
                                                                  Любопытно — я был чуть ли единственным, кто негативно откомментировал эту статью на медиуме. В основном были восторги. То ли уровень разработчиков на хабре выше, чем на медиуме, то ли там просто не любят негатив писать.
                                                                    0
                                                                    Просто разные парадигмы.
                                                                    У них: «Вау! Ну и фрик! Круто! Спляши еще!» (пусть расцветают все уродские цветы)
                                                                    У нас: «Ну и дебил! Кто ж так делает?! Убейся.» (делай правильно, будь как все (какими все хотели бы быть, но, разумеется, не всем дано))
                                                                    0
                                                                    Можно предложить использовать опциональную цепочку при попытке вернуть свойство глубоко в древовидную структуру. Так, символ знака вопроса? может использоваться для извлечения свойства, только если оно не равно нулю (null).

                                                                    Перевод просто жесть. Такое ощущение что нейронная сеть ворвалась на хабр и начала компилировать случайные слова и знаки препинания.


                                                                    в большинстве случаев эффективнее кэшировать длину массива <...> при увеличении размера цикла мы получим значительную экономию времени.

                                                                    Звучит очень сомнительно, если только этот цикл не четвертого уровня вложенности.

                                                                      +1
                                                                      автор почему-то не упомянул, что все эти выпендрежные побитовые операторы ~|& усекают число до 32-битного целого.

                                                                      еще obj + "" не всегда возвращает строку.

                                                                      все это может привести к тому, что прога будет работать 99% времени, но иногда глючить. попробуй найди такую ошибку.

                                                                      и кстати, насколько я помню, старый добрый parseInt — это самый быстрый (про IE6 не знаю) способ преобразования строки в целое число.
                                                                        0
                                                                        >еще obj + "" не всегда возвращает строку.
                                                                        можно пример пожалуйста?
                                                                          0

                                                                          Формально "не строка" будет, если у объекта определить метод toString, возвращающий не string. Но по-дефолту вы получите не менее бесполезный [object Object]

                                                                            0
                                                                            $ node
                                                                            > ({toString: () => 22}) + ""
                                                                            '22'
                                                                            Это все еще строка
                                                                              0
                                                                              ({toString: () => []}) + ''

                                                                              Кинет ошибку
                                                                            0
                                                                            я немного не так выразился. результатом будет строка, но...
                                                                          –1

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


                                                                          setTimeout(function(){ /*КОД*/},0);

                                                                          Очень велика вероятность, что код начнёт работать абсолютно непонятно почему.

                                                                            0
                                                                            код через setTimeout будет вызван не внутри стека текущей функции, а сам по себе в следующем свободном интервале call stack

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

                                                                            к примеру, такое бывает при смене экранов в игре
                                                                            когда в onClose одного экрана прямо вызывается goNextScreen, а тот вызывает onStart следующего экрана — вот эти вот onClose предыдущего экрана и оnStart нового часто схлестываются, то за ресурсы, то просто предыдущий экран не может спокойно умереть. Поэтому лайфсайкл экранов и вообще «умирающих стратегий», которые должны полностью исчезнуть и освободить захват, лучше делать через непрямые вызовы «иди к следующему экрану». Надеюсь, что примерно объяснил ))

                                                                              0
                                                                              А потом так же неожиданно бросит. Например, при показе.
                                                                              0
                                                                              Нуб, не делай так. Некоторые «хаки» могут соптимизировать тебя из конторы.
                                                                                +1
                                                                                Большинство этих приемов в реальных проектах использовать не следует. Один из признаков хорошего кода это его читаемость и понятность «типовому / среднему» разработчику.
                                                                                  +1
                                                                                  Стыдно должно быть такое переводить
                                                                                    +2
                                                                                    Float в целое число
                                                                                    Math.floor(123456789012345.1);  // 123456789012345
                                                                                    123456789012345.1 | 0;  // -2045911175

                                                                                    Упс.
                                                                                      0

                                                                                      А это и не float, а вполне себе double.


                                                                                      float x = 123456789012345.1;
                                                                                      double y = 123456789012345.1;
                                                                                      printf("float: %.2f double: %.2f\n",x,y);

                                                                                      на выводе дает


                                                                                      float: 123456788103168.00 double: 123456789012345.09

                                                                                      https://ideone.com/hBfyVr

                                                                                      0
                                                                                      я сперва усиленно педалировал стрелочные для байдинга к инстансу. а теперь вот могу сказать: если вам важна отладка вашего кода в хром девтулс, не делайте так. при использовании сорсмаппов хром не понимает стрелочные функции и говорит что this не существует, а следовательно typeerror тебе а не значения в инстансе. в том же stage0 есть новый bind syntax :: — он куда лучше, так как транслируется в обычный bind. другой подход это определение методов в конструторе или использование замыканий вместо this… и еще — это не такой уж уникальный список — половина описанного есть в учебниках по es5 — остальное в учебниках по еs6+, а некоторые его пунткты вроде замены ветвления на или — отстой. в программировании число символов не главное, главное чтобы всем было понятно, что ты делаешь и зачем, а для минификации есть инструменты.

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

                                                                                      Самое читаемое