Javascript простой! Кто сказал?
Старший аспирант «Нетологии» Алена Батицкая перевела статью Aurelien Herve о том, с какими неочевидными вещами придется столкнуться новичку на пути изучения javascript.
В этой статье собраны несколько ловушек и подводных камней javascript, о которых должен знать новичок. Экспертов прошу отнестись к этому тексту с пониманием.
Javascript всего лишь еще один язык программирования. Что может пойти не так?
1. Вы когда-нибудь пытались отсортировать массив чисел?
sort () в Javascript по умолчанию использует буквенно-цифровой порядок сортировки.
Так [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 год после 1900, вторая цифра это месяц в году, в нашем случае второй месяц — февраль. Действительно, кто в своем уме начнет нумерацию с цифры 1? :) И третья цифра означает первый день месяца — потому что иногда индексы все же начинаются с 1.
Запись new Date (2016, 1, 1) не прибавит 2016 лет к 1900, а просто вернет 2016 год.
3. Replace не заменяет
Мне кажется, что это хорошо, поскольку я не люблю функции, которые мутируют вводные данные. Вам так же стоит знать, что replace заменит только первое совпадение:
let s = "bob"; const replaced = s.replace("b", "l"); console.log(replaced); // lob - заменено только первое совпадение console.log(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] являются двумя отдельными массивами. Просто по счастливому стечению обстоятельств они содержат одинаковые данные. Оба массива имеют разные ссылки и не могут сравниваться при помощи === .
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 ограничена ближайшим блоком функции. А область видимости let ограничена ближайшей областью использования, которая может быть гораздо меньше блока функции. (Обе являются глобальными если объявлены за пределами какого-либо блока).
Источник
- Альтернатива: используйте bind
Greeters.push (console.log.bind (null, i));
Это только мой топ-2 из огромного количества решений.
7. Поговорим о bind
Как вы думаете, что получится на выходе?
class Foo {
constructor (name) {
this.name = name;
}
greet () {
console.log ('hello, this is ', this.name);
}
someThingAsync () {
return Promise.resolve ();
}
asyncGreet () {
this.someThingAsync ()
.this (this.greet)
}
}
new Foo ('dog').asyncGreet ();
Одно очко в вашу пользу, если вы думаете, что все сломается и выведется ошибка Cannot read property 'name' of undefined
Причина: greet выполняется вне надлежащего контекста. И опять же есть есть множество решений этой проблемы.
- Лично мне нравится
asyncGreet () { this.someThingAsync() .this(this.greet.bind(this)) }
Таким образом вы убедитесь, что Greet вызывается в вашем экземпляре класса в качестве контента.
- Если вы понимаете, что greet никогда не должен вызываться за пределами контекста, то вы можете забиндить его в конструкторе класса:
class Foo { constructor (name) { this.name = name; this.greet = this.greet.bind(this); } }
- Вам так же стоит знать, что стрелки (=) могут быть использованы для проброса контекста. Это так же будет работать:
asyncGreet () { this.someThingAsync() .this(() = { this.greet() }) }
Хотя я считаю, что последний пример менее элегантент в данном случае.
Я рад, что мы решили эту проблему.
Заключение
Мои поздравления, теперь вы умеете делать крутые вещи. Наверное.
Иногда даже избегая глобальных поломок (хотя обычно без них не обходится).
Cheers \o/
Мнение автора и редакции может не совпадать. Хотите написать колонку для «Нетологии»? Читайте наши условия публикации.
Полный текст статьи читайте на Нетология