Кто сказал, что JavaScript простой?
-
3059
-
24

Сегодня я покажу несколько трюков и ловушек, о которых, возможно, должен знать каждый начинающий JavaScript-разработчик. Если вы опытный разработчик, то все равно не постесняйтесь прочитать эту статью (с понимающим взглядом).
# 1. Вы когда-нибудь пытались отсортировать массив чисел?
JavaScript sort()
использует буквенно-цифровую сортировку по умолчанию.
К примеру, [1,2,5,10].sort()
выведет [1, 10, 2, 5]
.
Для правильной сортировки массива, вы должны использовать [1,2,5,10].sort((a, b) => a - b)
Решение решение, если вы знали, в чем проблема в первую очередь.
# 2. new Date() - просто великолепен
new Date()
может принимать:- без параметров: возвращает настоящее время
- Один аргумент
x
: возвращает 1 января 1970 г., + x миллисекунд. Unix-пользователи знают почему. new Date(1, 1, 1)
возвращает 1 февраля 1901 г. Первое как вы можете понять это - год. Второе значение, это - месяц (то есть - февраль). Кто в здравом уме начинает индексацию таблицы с индекса 1, и очевидно, что третий, является первым днем месяца, поэтому 1 - так как индексы иногда начинаются с 1.- Да, кстати
new Date(2016, 1, 1)
не добавят 2016 к 1900 году. Он просто выводит как 2016 год.

# 3. Replace does not replace
Я нахожу хорошей идеей то, что при использовании функции replace()
, входные данные остаются в исходном виде. Мне не нравятся функции, которые изменяют входные данные. Вы также должны запомнить, что replace()
заменяет только первое совпадение:
let s = "bob"
const replaced = s.replace('b', 'l')
replaced === "lob" // true. Заменяется только первый символ.
s === "bob" // Исходная строка остается неизменной
Если вы хотите заменить все вхождения, то используйте флаг /g
в регулярном выражении.
"bob".replace(/b/g, 'l') === 'lol' // Замена всех вхождений
# 4. Осторожнее с сравнениями
// Все в порядке
'abc' === 'abc' // true
1 === 1 // true
// Но здесь...
[1,2,3] === [1,2,3] // false
{a: 1} === {a: 1} // false
{} === {} // false
Причина: [1,2,3]
и [1,2,3]
являются двумя отдельными массивами. Они просто содержат одни и те же значения, но имеют различные ссылки и не могут быть сопоставлены друг с другом, ни через простое равенство ==
, ни через строгое ===
, результат false
.
Массивы - это объекты, а объекты всегда сравниваются по ссылке, а не по содержанию. В данном случае, возвращаемые массивы это разные объекты, поэтому они не равны друг другу.
# 5. Массив не является примитивным типом
typeof {} === 'object' // true
typeof 'a' === 'string' // true
typeof 1 === number // true
// Но....
typeof [] === 'object' // true
Чтобы узнать, является ли ваша переменная массивом, вы можете использовать Array.isArray(myVar)
# 6. Замыкание
Одно из заданий на собеседовании по JavaScript:
const Greeters = []
for (var i = 0 ; i < 10 ; i++) {
Greeters.push(function () { return console.log(i) })
}
Greeters[0]() // 10
Greeters[1]() // 10
Greeters[2]() // 10
Вы ожидали, что он выведет 0, 1, 2 ...? Вы знаете, почему это не так? Как бы вы это исправить?
Есть два возможных решения этой проблемы:
- Используйте
let
вместоvar
. Бум. Готово!
"Разница между
let
иvar
- это область видимости.var
ограничен областью видимости до ближайшего функционального блока, и пусть область видимости находится в ближайшем охватывающем блоке (оба являются глобальными, если находятся вне любого блока), который может быть меньше, чем функциональный блок." (источник)
- Альтернативный способ, использование -
bind
(привязку):
Greeters.push(console.log.bind(null, i))
Есть много других решений. Это только мои лучшие 2 варианта :)
# 7. Говоря о привязке
Как вы думаете, что это даст?
class Foo {
constructor (name) {
this.name = name
}
greet () {
console.log('hello, this is ', this.name)
}
someThingAsync () {
return Promise.resolve()
}
asyncGreet () {
this.someThingAsync()
.then(this.greet)
}
}
new Foo('dog').asyncGreet()
Одно очко в вашу пользу, если вы решили, что это приведет к сбою: Cannot read property 'name' of undefined
Причина: greet
не запускается с надлежащим контекстом. Опять же, есть много способов решить эту проблему.
- Мне лично нравится
asyncGreet () {
this.someThingAsync()
.then(this.greet.bind(this))
}
Таким образом вы гарантируете, что greet
вызывается с вашим экземпляром класса как контекст.
- Если вы считаете, что
greet
никогда не должен быть запущен вне контекста, вы можете также связать его в своем конструкторе класса:
class Foo {
constructor (name) {
this.name = name
this.greet = this.greet.bind(this)
}
}
Вы также должны знать, что стрелочные функции (=>
) могут использоваться для сохранения контекста. Это будет работать точно так же, как и способ выше:
asyncGreet () {
this.someThingAsync()
.then(() => {
this.greet()
})
}
Хотя я считаю последний пример менее элегантным в нашем случае.
Оригинальная статья: Who said javascript was easy? (Aurélien Hervé)
P.S.: Для практики, я решил перевести эту статью, так как она мне понравилась. Однако, из-за моего слабого английского, я часто использовал Google Translate. Если я где-то допустил ошибку, прошу извинить и указать на мою ошибку. Как я уже понял, писать статьи самому намного проще, нежели их переводить.
ПОДПИШИСЬ НА ОБНОВЛЕНИЯ
Только новые публикации и никакого мусора.