Pull to refresh

Comments 45

UFO landed and left these words here

Цель в том чтобы читатель, в будущем наткнувшись на какую-нить чертовщину, вспомнил про тот или иной подвох. Статья вроде не критикует, просто показывает подводные камни (которые везде есть).

UFO landed and left these words here

Автор не мог поставить два минуса. За одного человека можно голосовать только один раз.

Ещё одна особенность JavaScript? ;)

UFO landed and left these words here

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

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

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

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

Докинул третий за карманытьё на ровном месте.

Шикарная статья! Js мой "второй" язык по работе - но вот подставу с for я никогда не замечал - в шоке от того, что оно так работает)

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

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

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

И какие проблемы устранятся с типизацией? Типизации в самом js не будет никогда. Это все только для ide.

V8 научился запускать typescript без компиляции в js. По сути это оно и есть: js со статической типизацией

Я полагаю нода у себя внутри также и будет транспилировать ts в js

Нет. Никакой транспиляции, и никаких проверок. Проверять придется отдельно. Только стирание информации о типах (Type stripping). Именно поэтому ts поддерживается не в полном объеме. Не поддерживается то, что требует изменения кода js - типа перечислений или декораторов.

https://nodejs.org/api/typescript.html#type-stripping

Только вот это не фича V8 (как заявлено выше), а конкретно Node.js, и поддерживает она только strip (который просто заменяет все ваши типы пробелами и не поддерживает все фичи TS) и transform (под отдельным флагом) режимы, без проверки самих типов (это всё ещё делает tsc). В стандарте ещё ничего нет, но есть разные предложения (например https://github.com/tc39/proposal-type-annotations), соответственно, никакой поддержки в движках нет.

Сам js во время работы и компиляции (в байткод) никаких проверок этой типизации не делает и не будет делать. Только ide (типа VSCode) этим будет заниматься. А движок js (пока только в Node) просто удаляет информацию о типах (заменяет ее пробелами).

Запускать-то научился, но типизация всё ещё только для IDE и CI.

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

Самое печальное, что этот мем уже неоднократно становился реальностью.

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

На удивление, интересная статья.

странности, которым не хватило место: - и +0, NaN не равен самому себе, == и !=. так работает абсолютно любой язык. в других статься ещё было, что typeof NaN это number. это не спецификация js, а физическое устройство процессора

В списке любых языков есть Си?

Статья интересная, со многими "граблями" уже встречался. Но по своему опыту могу сказать, вряд-ли кто-то будет держать это всё в голове постоянно. Механизм такой: наткнулся => узнал => исправил => забыл

"JavaScript отстой, потому что '0' == 0!" — буквально каждый когда-либо

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

Ура! Согласен! Наконец-то будет статья которая не будет обсасывать сравнение чисел и строк!

  1. eval хуже, чем вы думаете

А, нет, расходимся. Автор решил пробить дно и писать об eval в 2025 году

Скриптовый язык состоит из костылей, чего тут странного.

Вставка элемента в массив. Если индекс выходит за пределы массива, массив наполняется пустыми элементами:

const msgArray = [];
msgArray[0] = "Привет";
msgArray[99] = "мир";

if (msgArray.length === 100) {
  console.log("Длина равна 100.");
}

Нет, не наполняется. Обновляется только длина, а сам массив становится "дырявым":

let x = [1];
x[9] = 2;
console.log(x.length); // 10
console.log(x.hasOwnProperty(0)); // true
console.log(x.hasOwnProperty(1)); // false

Очень подлая история с точкой с запятой. Когда-то писал что-то вроде

functionCalled(someArgs)
[1, 2, 3].includes(1)

и никак не мог понять, что ж за ерунда происходит. Через несколько минут стенаний и слёз пришло осознание. Всегда ставил точку с запятой, пока вот в новую команду не перешел. А там все болт на это забили. Ситуация, конечно, редкая и даже забавная в каком-то смысле, но только потом, когда ты пережил дебаг и осознание :)

А можно еще вот так

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 1);
}

не сильно отличается от

for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 1);
}

но результат абсолютно разный

В какой-то степени это логично и скорее нюанс блочной видимости let. Так-то блок для каждой итерации цикла "свой", и значение замыкается "своё". Для var эта область функциональная, и поэтому цикл входит в нее "полностью". Можно представить как

for (let _ = 0; _ < 3; _++) {
  const i = _;
  setTimeout(() => console.log(i), 1);
}

---------- 
let i;
for (let _ = 0; _ < 3; _++) {
  i = _;
  setTimeout(() => console.log(i), 1);
}


да уж, JS был хорош лет 20 назад, когда надо было прикрутить простенькую логику к сайту на PHP. но сейчас он уже полностью морально устарел. когда там к WASM прикрутят нормальную поддержку взаимодействия с браузером, чтобы можно было полностью отказаться от JS и использовать нормальные современные языки программирования?

Вся статья - следствие незнания концептов работы и устройства JS и виртуальной машины. И ее бы не было, если бы люди читали документацию ;) А то как в цитате: «в мире так много магии, если не учить физику в школе».

И писали бы на нём единицы, был бы у нас web как в 2000-м году, но у каких-нибудь MS сайт умел бы делать AJAX.

Тем не менее, большинство высокоуровневых языков (JS, Java, C# и др.) захватывают переменные по ссылке:

Это не так, в C# замыкание работает иначе и захватывает саму переменную.
https://csharpindepth.com/Articles/Closures где-то тут про это было написано.

Но в циклах это особенно нежелательно: там обычно нужно что-то сделать с итератором внутри колбэка:
// Код на C#:
// выводит "3 3 3" — вероятно, не то, что ождали

Вообще-то, это именно ожидаемый результат.
setTimeout() подразумевает выполнение фрагмента кода через определённый промежуток времени. Не выполнение сейчас и получение результата через определённую задержку, а именно начало выполнения по прошествии указанного периода времени.
В данном случае очевидно, что цикл завершится быстрее, чем отработает таймаут ожидания (за исключением разве что прохода i == 0 и то вряд ли).
Таким образом, к моменту первого выполнения WriteLine цикл уже закончит свою работу и в i совершенно ожидаемо будет 3.

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

В качестве "решения" стандарт ECMAScript сделал так, чтобы переменные цикла в заголовке for имели особое поведение

Именно поэтому JS - это ужасный язык, особое поведение на особом поведении едет и особым поведением погоняет.

Это не так, в C# замыкание работает иначе и захватывает саму переменную.

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

Вообще-то, это именно ожидаемый результат.

Хорошо если вы именно такой результат ожидаете, однако для новичков это всё ещё грабли.

Именно поэтому JS - это ужасный язык, особое поведение на особом поведении едет и особым поведением погоняет.

Но оператор foreach в C# имеет точно такое же особое поведение. Почему ему можно, а оператору for нельзя?

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

Ровно до того момента, когда "деталь реализации" станет важна. И тогда тот самый новичок, запомнивший, что передаётся по ссылке, будет пытаться понять, а что же не так.

однако для новичков это всё ещё грабли

Какие? Необходимость разобраться с моментом времени, в который исполняется код?

Но оператор foreach в C# имеет точно такое же особое поведение.

Так не имеет же. Если Вы про переменную заголовка, то она read only по определению, т.е. случай полностью аналогичен лямбде с параметром (0 1 2).
Если про вынесенную перед циклом переменную, то она точно также будет иметь значение, зависящее от момента времени выполнения кода (3 3 3).

Так не имеет же. Если Вы про переменную заголовка, то она read only по определению

По какому именно определению? В ранних версиях C# никакое определение не мешало циклу foreach работать с замыканиями так же как и цикл for.

Меня, конечно, изрядно удивило, что 👨‍👩‍👧‍👦‍👨‍👩‍👧‍👦 — это 23 символа. Скопировал в консоль — действительно 23.
Но потом я попробовал вручную ввести эти символы — и, блин, получилось 22. Что за…?

Пошёл разбираться дальше — и понял: в статье речь шла не о двух эмодзи “семья”, а о двух слитых эмодзи, между которыми стоит специальный символ \u200D. Именно он и добавляет один лишний символ, потому что склеивает семьи.

Вообще, оказалось, этот символ объединяет любые эмодзи в новые. Если такой комбинации ещё не существует — они просто отображаются рядом, но без пробела, как в нашем случае.

Так что, по сути:
const family = "👨‍👩‍👧‍👦‍👨‍👩‍👧‍👦";
Это верно, что результат — 1, ведь это считается одним символом с точки зрения пользователя.

Sign up to leave a comment.

Articles