Всем привет! Меня зовут Леша, я фронтенд-разработчик. Крашу кнопочки, пишу js скрипты, веду канал в TG https://t.me/frontend_tales (подписывайтесь, стараюсь выкладывать полезный материал).
В этой статье хотел бы поделиться с вами лайфхаками JavaScript, которые, возможно, помогут вам понять тонкости языка и улучшить ваш код. В общем, статья рассчитана на джуниоров и мидлов, сеньорам возможно будет скучно, но рады всем. Начнем!
1. Разделитель числа
В числах можно использовать _
которое помогает улучшить читаемость чисел в коде.
const sixBillion = 6000000000
// Очень трудно читать
const sixBillion2 = 6000_000_000
// Читать намного легче
console.log(sixBillion2) // 6000000000
// В вычислениях тоже можно использовать
const sum = 1000 + 6000_000_000 // 6000001000
2. Оператор ? для упрощения && и тернарных операторов
Например, у нас есть код, который мы хотим упростить:
const obj = null
console.log(obj && obj.name)
const title1 = document.querySelector('.title')
const title = title1 ? title.innerText : undefined
Перепишем код с использованием оператора ?
:
const obj = null
console.log(obj?.name)
const title1 = document.querySelector('.title')
const title = title1?.innerText
3. BigInt для решения задач по вычислению больших целых чисел
К сожалению, корректность вычисления чисел в JS, превышающих Number.MAX_SAFE_INTEGER (9007199254740991), не гарантирована, это очень грустно.
Например:
Math.pow(2, 53) === Math.pow(2, 53) + 1 // true
// Math.pow(2, 53) => 9007199254740992
// Math.pow(2, 53) + 1 => 9007199254740992
Для вычисления больших чисел советую использовать BigInt
. Это позволит избежать вычислительных ошибок.
BigInt(Math.pow(2, 53)) === BigInt(Math.pow(2, 53)) + BigInt(1) // false
4. Чем можно заменить оператор in
Чтобы узнать, существует ли свойство у объекта, обычно мы используем оператор in
, но можно еще использовать obj.hasOwnProperty()
.
Оба они имеют свои недостатки:
Оператор
in
проверяет наличие свойства в объекте, включая свойства, унаследованные от прототипа. Это может привести к нежелательным результатам, если вы хотите проверить только наличие свойства в самом объекте, а не в его прототипе.Метод
obj.hasOwnProperty()
проверяет наличие свойства только в самом объекте, и не учитывает свойства, унаследованные от прототипа. Однако, этот метод не работает корректно, если объект переопределяет методhasOwnProperty
. В таком случае, вызовobj.hasOwnProperty()
может привести к ошибке или неправильному результату.Оба подхода не учитывают свойства, которые могут быть доступны через цепочку прототипов. Если вам нужно проверить наличие свойства в объекте, включая его прототипы, вам придется использовать другие методы, такие как
Object.getPrototypeOf()
илиObject.prototype.isPrototypeOf()
.Использование
in
иobj.hasOwnProperty()
может быть неудобным и неэффективным при работе с большими объектами или вложенными структурами данных. Это может привести к необходимости выполнять множество проверок и вызовов методов, что может замедлить выполнение программы.
Небольшие примеры:
// Оператор in
const obj = { name: 'John', age: 25 };
console.log('name' in obj); // true
console.log('gender' in obj); // false
// Проверка наличия свойства в прототипе объекта
console.log('toString' in obj); // true
// Метод obj.hasOwnProperty()
const obj = { name: 'John', age: 25 };
console.log(obj.hasOwnProperty('name')); // true
console.log(obj.hasOwnProperty('gender')); // false
// Проверка наличия свойства в прототипе объекта
console.log(obj.hasOwnProperty('toString')); // false
Есть еще один оператор Object.hasOwn()
. Он удобнее и безопаснее, чем метод obj.hasOwnProperty()
.
const object1 = {
prop: 'exists',
};
console.log(Object.hasOwn(object1, 'prop'));
// Expected output: true
console.log(Object.hasOwn(object1, 'toString'));
// Expected output: false
console.log(Object.hasOwn(object1, 'undeclaredPropertyValue'));
// Expected output: false
5. # для объявления частных свойств
Раньше для того чтобы показать что поле приватное мы добавляли _ , но теперь можно использовать #
:
class Person {
#money=1
constructor (name) {
this.name = name
}
get money () {
return this.#money
}
set money (money) {
this.#money = money
}
showMoney () {
console.log(this.#money)
}
}
const p1 = new Person('fatfish')
console.log(p1.money) // 1
// p1.#money = 2 // Таким способом нельзя изменить #money
p1.money = 2
console.log(p1.money) // 2
console.log(p1.#money)
6. ?? вместо ||
Используйте ??
вместо ||
, чтобы определить, является ли значение в левой части оператора null
или undefined
, а затем вернуть значение в правой части.
const name = '';
const defaultName = 'John';
const result = name ?? defaultName;
console.log(result); // ''
const age = 0;
const defaultAge = 25;
const result2 = age ?? defaultAge;
console.log(result2); // 0
В примере выше, оператор ??
возвращает значение переменной слева от него, если это значение не равно null
или undefined
.
В противном случае, если значение переменной слева от ??
равно null
или undefined
, оператор возвращает значение переменной справа от него.
7. Преобразование String в Number
Многие почему-то используют для преобразования string в number функцию parseInt()
, когда можно использовать только оператор +
const num = parseInt("1000");
// и
const num = +"1000";
8. Сокращенный вариант для Math.floor при округлении чисел
Вместо функции Math.floor()
для округления числа можно использовать оператор ~~
:
Math.floor(5.25) // 5.0
// или
~~5.25 // 5.0
9. Преобразование значения в Boolean
Для преобразования любого значения в Boolean нужно использовать двойной восклицательный знак !!:
!!true // true
!!2 // true
!![] // true
!!"Test" // true
!!false // false
!!0 // false
!!"" // false
10. Объединение массивов
Для объединения массивов хорошо использовать spread (…)
оператор, вместо метода concat()
:
const nums1 = [1, 2, 3];
const nums2 = [4, 5, 6];
let newArray = nums1.concat(nums2);
// spread
newArray = [...nums1, ...nums2];
// можно добавлять значения в массив
numbers = [...numbers, 4, 5];
11. Удаление повторяющихся элементов из массива
Удаляем через Set()
- множество:
const numbers = [1, 1, 20, 3, 3, 3, 9, 9];
const uniqueNumbers = [...new Set(numbers)]; // [1, 20, 3, 9]
12. Изменение мест двух переменных без использования третьей
В JavaScript можно “оторвать” значения от массива с помощью деструктуризации. Этот прием также применим, если нужно поменять местами две переменные без вспомогательной третьей.
let x = 1;
let y = 2;
let temp = x;
x = y;
y = temp;
[x, y] = [y, x];
13. document.designMode
Связанный с интерфейсным JavaScript, designMode
позволяет редактировать любой контент на странице. Просто откройте консоль браузера и введите следующее:
document.designMode = 'on';
Обязательно попробуйте, очень классная штука)
14. Быстрое преобразование Float в Integer
Если вы хотите преобразовать число с плавающей точкой в целое число, вы можете использовать Math.floor()
, Math.ceil()
или Math.round()
.
Но есть также более быстрый способ обрезать число с плавающей точкой до целого числа, используя | оператор побитового ИЛИ.
console.log(23.9 | 0); // 23
console.log(-23.9 | 0); // -23
15. Обрезание массива
Если вы хотите удалить значения из конца массива деструктивно, есть более быстрые альтернативы, чем использование splice()
.
Пример:
let array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
array.length = 4;
console.log(array); // [0, 1, 2, 3]
Кстати, часто на собесах спрашивают, что будет если мы свойству length
присвоим значение.
16. Получить n последних элементов массива
Метод массива slice()
может принимать отрицательные целые числа, и при наличии он будет принимать значения с конца массива, а не с начала.
let array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log(array.slice(-1)); // [9]
console.log(array.slice(-2)); // [8, 9]
console.log(array.slice(-3)); // [7, 8, 9]
Комментарии (21)
shasoftX
23.12.2023 13:09Преобразование String в Number
Преобразование значения в Boolean
Быстрое преобразование Float в Integer
Всё-таки в коде лучше использовать функции которые понятны. Когда потом это всё будет минимизироваться, вот там уже пусть преобразовывает во что-то другое. Но для разработчика чем понятнее код тем лучше.
s_f1
23.12.2023 13:09Вместо функции
Math.floor()
для округления числа можно использовать оператор~~
Только для положительных чисел!
Alexandroppolus
23.12.2023 13:09И если число меньше 2147483648
Поразрядные операции сначала приводят к Int32. Потому лучше вообще не использовать этот способ - от греха подальше.
Bigata
23.12.2023 13:09Если бы привели примеры, как использовать "улучшения", было бы много эффективнее подача Вашего материала.
Ну и тернарный оператор, + и ~~ точно не улучшают читабельность кода
Deirel
23.12.2023 13:09Дня отбрасывания дробной части ни floor, ни ceil, ни round не подходят - для этого есть Math.trunc().
Dolios
23.12.2023 13:09А рекламу телеги в начале автор поместил, т.к. боялся, что до конца никто читать не станет?
Vaulter
23.12.2023 13:09Возможности js , о которых лучше и не знать.
vitiok78
23.12.2023 13:09Мне бы хотелось вообще о js не знать. Но такой привилегии у нас, к сожалению нет. Всегда он где-то вылазит
Metotron0
23.12.2023 13:09Почему же? Второе очень сократило код, убрав кучу проверок, а шестое позволило отличать null/undefined от 0. Скажем, написал text = data?.b ?? 'Не найдено' и знаешь, что если в b будет ноль, то он присвоится как ноль, а если b не обнаружится, то будет соответствующая надпись. Удобно, когда нужно вывести что-то с сервера, но сервер может это и не прислать.
vitiok78
23.12.2023 13:09Коллеги-фронтендеры, не используйте где попало spread (...) оператор. Если массивы большие и постоянно пересчитываются вашими любимыми фреймворками (и "не фреймворками", а библиотеками))) при каких-либо изменениях, то вы просто уничтожаете память и производительность.
Metotron0
23.12.2023 13:092. Оператор ? для упрощения
Хотелось бы упоминания, что это зовётся Optional chaining, дабы можно было его сгуглить. Ну, и ссылку на MDN. И MDN таки говорит, что оператор имеет вид ?., а не просто ?
В примере использования не хватает использованися с массивами:a = {x: 10}
a.b[1] ← ошибка
a?.b[1] ← ошибка
a?.b?.[1] ← undefined6. ?? вместо ||
Хотелось бы упоминания, что это зовётся Nullish coalescing и ссылки на MDN
Многие почему-то используют для преобразования string в number функцию
parseInt()
, когда можно использовать только оператор+
А я знаю, почему:
s = '100s' // Предположим, это у нас transitionDuration
console.info(parseInt(s))
console.info(+s)
Если уж сравнивать, то с Number(s)Для преобразования любого значения в Boolean нужно использовать двойной восклицательный знак !!
Не нужно, а можно. По-человечески это всё же Boolean(variable), а !! — это чуть-чуть из области минификации кода, примерно как if (a) run() заменять на a && run()
13. document.designMode
Если это упомянули (я ещё на IE использовал, если память не врёт), тогда нужно было написать и про contentEditable, который можно применять к отдельным тегам
monochromer
23.12.2023 13:09Из приведенных примеров не видна разница между использованием операторов
~~
и|
. Выражения ниже приводят к одному результату.20.8 | 0 ~~20.8
Djaler
Потому что это явно читается, в отличие от унарного плюса?
Тоже ужасный совет. Действительно, зачем использовать функцию, по имени которой понятно, что она делает, лучше использовать хак с битовыми операторами.
Опять-таки. Вам точно нужен этот микроскопический буст в производительности, который можно заметить только при миллионах итераций?
ponikrf
Разве в этом проблема? Это вобще разные операции
Вывод
Если я использую parseInt то я явно указываю что я хочу получить на выходе. В случае же использования "+" и не корректной строки - я даже не получаю исключение, а просто NaN на выходе.
Поведение у этих операций абсолютно разное. Они вобще для разных целей сделаны.
EneroPl
Сравнил `+` - который работает с Float и Int одновременно, с `parseInt` - который работает по принципу получения целого числа, на примере Float значения. Чел ты гений)
HomoHikka
parseFloat?
nin-jin
В JS нет целочисленных типов, и соответственно целочисленной арифметики, а писать Math.floor для каждой операции задалбывает.
impwx
Метод
parseInt
может в некоторых случаях сыграть злую шутку, когда на вид всё понятно, но работает совсем не так, например:Djaler
тут ведь нюанс не в parseInt, а в таком использовании map
Metotron0
Лишь с теми, кто не знает о том, что у parseInt есть второй параметр radix, или не знает, что map передаёт в колбек три значения.