Comments 155
let x, { x: y = 1 } = { x }; y;
Это прекрасная задача для собеседований. Каким будет значение y
Я к вам работать не пойду с такими собеседованиями.
return и возвращаемое выражение должны находится в одной строке
Разве это объяснение? Это скорее то, как избежать такого поведения.
А объяснение в автоматической установке точки с запятой
Sometimes when I'm writing Javascript I want to throw up my hands and say "this is bullshit!" but I can never remember what "this" refers to © Ben Halpern
А относительно остальных пунктов хочется заметить, что если язык спроектирован так, чтобы давать свободу выражения, и не писать «простыню» однозначно-очевидного кода для простых вещей, то на граничных случаях будут такие как будто «смешные» случаи.
Пост в целом хороший, потому что подборка граничных случаев большая, но это совсем не «что за чёрт, Javascript» из заголовка. Такое впечатление может быть только на человека, далёкого от программирования и высокоуровневых языков.
Это перевод wtfjs, отсюда и заголовок
И как в этих «многих языках», получить наименьшее возможное число? Именно число, не Infinity, которое как-бы не число.
Си/C++: FLT_MIN, DBL_MIN
C++: std::numeric_limits::min(), std::numeric_limits::min()
Java: Float.MIN_VALUE, Double.MIN_VALUE
Именно наименьшее возможное число во всех этих случаях можно получить как противоположное к наибольшему.
std::numeric_limits<double>::min(): 2.22507e-308
std::numeric_limits<int>::min(): -2147483648
НО
std::numeric_limits<double>::lowest(): -1.79769e+308
std::numeric_limits<int>::lowest(): -2147483648
ну и выше про C, Java написал mayorovp
C#: Double.MinValue
PHP: PHP_FLOAT_MIN
Мораль сей басни: по возможности не используйте конструкции с неочевидным поведением в своем коде.
var x =5;
(function(){
console.log(x);
var x = 10;
console.log(x);
})();
Область видимости переменных (var) — функция, и есть внутренний объект [[Scope]], который хранит всё объявленное, если есть объявление внутри функции, то он сразу создает под неё элемент и тем самым сразу перетирает, что видно в глобальной области видимости.
Перетирает? Вы что-то путаете.
var x =5;
(function(){
console.log(x);
var x = 10;
console.log(x);
})();
на самом деле исполняется
var x =5;
(function(){
var x;
console.log(x);
x = 10;
console.log(x);
})();
поэтому первый console.log напечатает undefined, а не 5, как это было бы в большинстве других языков. Но об этой проблеме могут не догадываться те, кто никогда не переопределяет внешние переменные в локальном скопе.
Dim textString As String
If(If((-If(((Me.ViewMode = LocalDimension.ViewModality.LengthOnly) > False), 1S, 0S)), 1S, 0S) Or 5S) <> 0S Then
... some code...
Else
textString = ""
End If
О! Прямо детство вспомнил. Я, правда, тогда на VB6.0 развлекался. Оптимизация с помощью побитовых операций — наше всё )))
var x = 1 > 2;
if (x.toString().length == 5){
...
}
Если криворукий программист написал такой код на VB, он такой же кривой код напишет и на других языках.
Впрочем, затрагивая тему JS: приведённый код хоть и запутан — но выполняет то, что написано. В JS же код зачастую выполняет не то, что написано.
Спасибо тебе, Господи, что я питонист!
Number() // -> 0
Number(undefined) // -> NaN
Должен сопротивляться.... Черт, все, теперь я ломаю голову как это сделать в своей функции. Спасибо вам.
function foo(num) {
if (arguments.length === 0) return 0;
if (num === undefined) return NaN;
return num;
}
Вообще тут обнаружил еще крутую штуку — так сказать, кривой, как раз в стиле JS, метод переопределения операторов. Symbol.toPrimitive Особенно мне в нем нравится аргумент hint.
В JS нет переопределения операторов. Symbol.toPrimitive — придуман совсем для другого. А кривизна нецелевого использования… как бы самоочевидна.
после проверки arguments.length нужно возвращать +num
, иначе поведение другое ;)
Вообще тут обнаружил еще крутую штуку — так сказать, кривой, как раз в стиле JS, метод переопределения операторов.
Но ведь это не перегрузка операторов, это неявное приведение типов. Что здесь "кривого"?
Особенно мне в нем нравится аргумент hint.
Если это ирония, то я её не понял. Вы ведь знаете что такое динамическая типизация?
Вы бы, коль уж берёте смелость на себя критиковать язык, хоть не признавались в незнании в нём базовых вещей :)
Это не переопределение операторов, это обычное приведение типов. Попробуйте создать объект с функциями toString и valueOf (возвращающимися соответственно строку и число) и скастить его в строку или число.
Наверное, не совсем WTF, но меня такое недавно спрашивали
Что произойдёт, если выполнить (к примеру, сделать console.log
) этот код?
[0, 1, 2][0, 1]
Таких, как, например, оператор "," — позволяет писать несколько команд в строку, возвращает последний.
var x = (1, 2, 3); // x = 3
А ещё можно изображать питон:
if (something)
firstFunc(),
secondFunc();
К другим не очень известным фичам относятся, например, блоки или метки циклов (чтобы выходить чрез break во внутреннем цикле из внешнего цикла), кому интересно.
Реальная польза от её наличия есть только в двух случаях: макросы/условная компиляция (например, макрос assert в C) и в variadic templates в C++ (пример).
Да, не нужна. Потому почти никто про неё и не знает.
auto result = db < "SELECT f1, f2 FROM ... WHERE f3=:value:", bind::to<std::tupple<int, string>>(), bind::from("value", 5);
Что-то похожее на это. Запятая пригодилась.
auto result = db.Query("SELECT f1, f2 FROM ... WHERE f3=:value:", bind::to<std::tupple<int, string>>(), bind::from("value", 5));
Заодно и IDE проще с кодом работать — оператор она вам не подскажет, а вот вызов фунции с аргументами — всегда пожалуйста.
for(var len = arr.length, i=0; i<len; ++i);
for (var i = 0; i < arr.length; ++i);
Выигрыша от сохранения размера массива вы все равно не получите.
В данном случае это обычное объявление двух переменных, которое к оператору "запятая" отношения не имеет.
Единственный напрягающий момент — автодополнение строк точками с запятой, из-за которого приходится придерживаться не очень приятного мне стиля расстановки скобок.
Единственный напрягающий момент — автодополнение строк точками с запятой, из-за которого приходится придерживаться не очень приятного мне стиля расстановки скобок.
Как вариант, можно вручную ставить точки с запятой, тогда не будет проблем со скобками :)
Подозреваю, проблема в том, что JS ставит их там где не надо.
{} + []; // -> 0
({} + []); // -> "[object Object]"
Вот что бывает когда лиспера просят запилить яву (тоже не подарок). Весь ад из лиспа, плюс куча ада который лиспер не понимал потому что он лиспер. Я не говорю что ява лучший язык и блабла, просто отвечаю на вопрос как сам понимаю. На самом деле разработка языка — это очень кропотливая работа, нужно проработать тысячи вариантов взаимодействия разных языковых конструкций, проверить как сделано в других языках, взвесить все плюсы и минусы каждой реализации и тп. За десять дней ничего путного не сделать.
Сколько языков вы разработали?
Я вот видел два ада: один ад — это код на JavaScript, написанный Явером, второй — код на Java, написанный ЯваСкриптером.
Отсюда мораль: либо читай спеку, либо не жалуйся на язык.
Неоднозначная спека языка, в котором легко накосячить — проблема языка, а не "косячника".
Я работаю в компании, где все знают язык на должном уровне и придерживаются установленного стандарта кода.
Никаких трудностей не возникает даже с выражениями типа ~a.indexOf(b) или a&1||2.
Спека — однозначная. Просто читать надо уметь.
Второе — возвращает 2, если число чётное, и 1 — если нечётное (0 — не ожидался). Вы хотите сказать, что это не очевидно? У нас в конторе очевидно всем. Уровень, господа!
Но я забыл, что речь идёт о JavaScript, в котором всё перевёрнуто с ног на голову. Ну а так как мой суммарный опыт программирования на JS — не больше недели, мне простительно, что я не знал, что || возвращает не true/false, а один из операндов.
Собственно, поэтому я и против таких конструкций. Конструкция вида (a % 2 == 1? 1: 2), либо упрощённая до (a & 2? 1: 2) будет понятна любом программисту, знающему C/C++/C#/Java/PHP. И поэтому, когда такому программисту понадобится прочитать код на JS (мне, бывает, нужно), у него не возникнет проблем.
когда такому программисту понадобится прочитать код на JS
Он напишет в скайп своему коллеге, пишущему на JS. Или даже переведёт задачу на него, чтобы поправил по своей части.
Я за то, чтобы знать язык, а не жаловаться, что в языке всё не так как хочется.
Над лиспом тоже ныть будете, что всё не так? А PL SQL Вы изучали?
А если надо поправить вот прям щас? Исправление мелочи — минута, а вот коллега потратит 15-20 минут (объяснение проблемы + время на возвращение коллеги обратно в поток).
Ну или если человек уже не работает, а код поправить надо?
> Я за то, чтобы знать язык, а не жаловаться, что в языке всё не так как хочется.
Практически на любом языке можно писать трудночитаемый код. И таким страдают, в основном, начинающие программисты, только изучившие язык и хотящие показать мастерство его владения.
> Над лиспом тоже ныть будете, что всё не так? А PL SQL Вы изучали?
А причём тут это? Речь о том, что одинаковые конструкции разных языков программирования имеют разную семантику, и это плохо.
А если надо поправить вот прям щас? Исправление мелочи — минута, а вот коллега потратит 15-20 минут (объяснение проблемы + время на возвращение коллеги обратно в поток).
После такого "прям щас" и появляется говнокод в проекте.
Ну или если человек уже не работает, а код поправить надо?
Прямо совсем ява-скприптеров в компании не осталось? Это что же за компания такая?
И таким страдают, в основном, начинающие программисты, только изучившие язык и хотящие показать мастерство его владения.
Ну и чёрт с ними. Если человек идиот — это надолго.
А причём тут это?
При том, что языки — разные.
одинаковые конструкции разных языков программирования имеют разную семантику, и это плохо.
Что в этом плохого? Спеку приходится читать? Ой какая трудность!
Видимо, у нас разные понятия о говнокоде.
> Прямо совсем ява-скприптеров в компании не осталось? Это что же за компания такая?
Штатных вообще ноль. А проектом занимаются два человека + пара-тройка эпизодических. Если текущих знаний достаточно для написания работающего кода и удовлетворения заказчика, в чём проблема?
> При том, что языки — разные.
А я про языки с одинаковым синтаксисом, но разной семантикой, в проекте, где одновременно используется несколько языков.
> Что в этом плохого? Спеку приходится читать? Ой какая трудность!
Не трудность, но время занимает. И именно написание обфуцированного кода затрудняет его поддержку, потому что даже для знающих язык приходится напрягать извилины. Короче, код вы не только для себя пишите.
Штатных вообще ноль.
Согласитесь, это точно не проблема языка.
в проекте, где одновременно используется несколько языков
Работают несколько программистов. Логично?
Был у нас один двухстаночник — Java/JavaScript. Не знаю, какой он был явер, но его JS потом полгода рефакторили к стандарту. Потому что не ходи со своим уставом в чужой монастырь.
обфуцированного
Что что? В каком месте он обфуцированный?
Короче, код вы не только для себя пишите.
Ну да. Я код пишу для себя и своих коллег, которые знают язык не хуже чем я. Об этом я и писал с самого начала.
Напомню:
Я работаю в компании, где все знают язык на должном уровне и придерживаются установленного стандарта кода.
Работал в общей сложности с десятком ява-скриптеров, и среди них не было тех, кто не читает спеку. Любой из них понял бы вышеприведённые выражения.
Особенность работы оператора || — стандартно используется для присвоения дефолтных значений параметрам. Мне дико слышать, что человек, для которого конструкция вида
a = a || 0;
непривычна, считает, что может писать на JavaScript настоящий проект, а не поделку для личного пользования.
Во-вторых если в JS сложение 3 и 5 внезапно давало бы -1/12, а во всех остальных случаях работало как обычно, но в спеке это было бы описано, то вы бы назвали это нормальным поведением? Если в спеке сказано, что при 3 фазе луны при заходе в юпитер сложение строк внезапно дает NaN, то это никак не умаляет общего идиотизма.
Хороший язык делает интуитивно понятные вещи. А если работа с языком идет по принципу «написал-получил-хрень-почитал спеку-»ааааа, ну теперь-то понятно", то этот язык — хрень.
Что касается того, что у вас команда бородочей, которые отлично разбираются в JS его как язык никак не исправляет. Уверен, есть специалисты должного уровня даже на брейнфаке. А что, спека всё описывает (причем тот редкий случай, когда даже нет UB!), твори да и только.
Вы до сих пор верите, что бывают интуитивно понятные вещи?
По вашему кто не эни-кейщик — тот сразу бородач?
"Интуитивно понятно" то, что привычно. Ни больше и ни меньше.
Мне лично привычно, что оператор || возвращает первый из операндов, приводящийся к true, или последний операнд. В других языках часто не хватает оператора с таким поведением, а без него — как без рук.
Не интуитивным является например нетранзитивность сравнения. Если a == b и b == c, то a == c. Только для объектов типа NaN такое сравнение может не выполнятся, но об этом все знают, потому что у всех языков поведение тут одинаковое, и обоснованное на уровне IEEE.
Если a == b и b == c, то a == c
Оператор == означает "равно или приводится друг к другу".
(Очень полезный оператор, если вместо числа может придти строка, содержащая число.)
Если a, b и c — имеют разные типы, то в парах [a,b], [b,c] и [a,c] — они приведутся к друг другу разным образом.
Если они одного типа, то с транзитивностью проблем не возникнет.
Всё стройно и логично.
В общем основная претензия в том, что операторы в JS явно занимаются не тем, чем должны. Оператор «проверить на равенство и выслать емейл если они не равны» не должен существовать по очевидным причинам. Оператор «приведи хоть как-нибудь и сравни» это из той же оперы.
Ну и да, в обычных языках, если a != b, то !!a != !!b
То что результат этого равенства может быть различным в зависимости от типа (информации о котором, конечно же, в языке нет) просто недопустимо. Говорить «это есть в спеке» все равно, что вспоминать, как у нас человек на 3 курсе на лабе по программировании написал такую костылину, которая перемножает матрицы, что работало только с нечетными размерами матриц — 3х3, 5х5… Так он просто в лабу написал «спеку»: «не поддерживается перемножение матриц с четным рангом». Это прокатило, но называть это нормальным решением лично у меня язык не поворачивается.
Язык может быть не похож на другие, хотя чем больше бекграунда с других языков примкенимо — тем в целом лучше. Но правила должны быть логичными и непротиворечивыми. Каждая сноска «а вот если тут такие специальные значения то оператор работает совсем не так» это минус языку. Это как с эсперанто — 12 правил, ни одного исключения. Интуитивно понятен и хорош. Естественные языки — сборник костылей, можно почитать известную пасту про «сапог на столе лежит, на полу стоит, а на ноге сидит» в комментах к другому посту. Это — плохо.
Наличие в языке двух разных «равно» это уже провал, имхо.
ИМХО, два разных "равно" — необходимы в языках с нестрогой типизацией.
в обычных языках
С нестрогой типизацией?
Каждая сноска «а вот если тут такие специальные значения то оператор работает совсем не так» это минус языку.
Назовите хоть один такой случай в JS. Пока что Вы не назвали ни одного оператора, который бы работал с какими-то специальными значениями иначе, чем во всех остальных случаях.
const areEqualWithInverse = x => x == !x;
undefined
areEqualWithInverse(true)
false
areEqualWithInverse([])
true
По сути нарушается закон исключенного третьего. Единственное значение, для которого подобно допустимо — это NaN.
Да, Вы правы. Это явный косяк языка. Я на него до сих пор не натыкался.
Кстати, почему для NaN допустимо быть равным собственному отрицанию?
Кстати, реально не понятно, какого дьявола пустой массив приводится к false, а не к true, как положено любому порядочному объекту.
developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness
Ну и да, в обычных языках, если a != b, то !!a != !!b
Языки Си и С++: 1 != 2, но !!1 == !!2
Питон: 1 != 2, но (not (not 1)) == (not (not 2))
Какие еще обычные языки вам нужны?
И да, выдуманное вами правило "если a != b, то !!a != !!b" не имеет ничего общего с законом исключенного третьего.
Стоит отметить, что такая конструкция возможна только в языках со слабой динамической типизацией, поэтому неудивительно, что её больше нигде нет.
Вот и здесь так: если убрать эту ритуальную фразу, то набигут тысячи js-еров и с криками затопчут, закопают и надпись «он не любил яваскрипт» написают.
А так автор сначала, как те порасята из сказки, благоразумно строит каменный домик с большой красивой надписью «JavaScript is a great language!» и уже оттуда, хохоча сатанинским смехом, набирает полные лопаты wtfjs-ов и кидает ими через забор в проходящую мимо публику. На потребу своей чОрной душе.
И всё, вместо тонн хейта всё что он получит — это беспомощное: «ну да, ведь на дне запертого шкафа с бумагами, который стоял в неработающем туалете, на двери которого висела табличка „Осторожно, леопард!“ — так вот там именно это поведение и описано, а что тут такого».
P.S. Всегда так делаю.
Статься хорошая, учитывая повсеместное использование этого языка в вебе.
Предпочитаю писать программы, а не разбираться в тонкостях синтаксиса, ибо слишком много потенциальных и действительных факапов, и не хотел бы быть нинздей языка, если это — исключительно суровая необходимость, чтобы не писать бешено медленную/постоянно ломающуюся фигню, чисто по незнанию россыпи мелких тонкостей.
Лично мне кажется, что жаваскрипту пора завязывать с обратной совместимостью, ибо пора бы уже перелопачивать. В сторону того же typescript, хотя бы.
В качестве решения проблемы обратной совместимости, на первое время, можно указывать браузеру версию js, вроде этого:
#javascriptX.X
.Ну, а если и правда окажется здорово — то и фреймворки будут переписаны под последнюю версию. Конечно, возможна проблема как с python2/3, но только на первое время. В зависимости от форса новых стандартов/их фич.
Хм, мечты.
Мимо-луашник.
// -> означает результат console.log или другие выходные данные
Что за чертовщина тут написана?
NaN === NaN // -> false
Мой опыт подсказывает, что почти во всех ЯП NaN («не число») не равно самому себе и для такой ситуации предусмотрены отдельные методы:
Object.is(NaN, NaN); // -> true
Аналогичная ситуация в ЯП обстоит с +0 и -0:
+0 === -0 // -> true
но:
Object.is(+0, -0); // -> false
Кто заметил странную подсветку синтаксиса в публикации?
В коде статьи указан для первого блока кода — basic
,
для второго — scilab
,
третьего — javascript
(наконец-то),
четвертого — inform7
:(,
пятого — erlang-repl
и так далее.
Автор, как так вышло?
null преобразуется в строку «null», а потом пытается его конвертировать. Но для оснований от 0 до 23 не существует чисел, в которые машина может конвертировать это слово, поэтому возвращается NaN. На позиции 24 находится «n», 14-я буква латинского алфавита, она складывается с числом. На позиции 31 находится «u», 21-я буква, она тоже складывается, после чего можно декодировать всю строку. На позиции 37 уже не получится сгенерировать валидное число, поэтому возвращается NaN.
Можно подробнее про «складывается с числом», «после чего можно декодировать всю строку» и «позицию 37»? Не совсем понятно, что с чем складывается, почему после преобразования буквы «u» можно декодировать всю строку и откуда взялось 37.
.
Если не ошибаюсь, тот тут происходит преобразование null > «null», затем каждый символ проверяется на наличие в системе с основанием 23. Например, символ «n» там есть, равен 23 ((от 1 до 9) + (от «a» до «n»)).
Символ «u» из слова «null», в свою очередь, отсутствует в системе с основанием 24, так как равен 30 ((от 1 до 9) + (от «a» до «u»)), так что прекращаем разбор строки и возвращаем 23
Можно подробнее про «складывается с числом», «после чего можно декодировать всю строку» и «позицию 37»? Не совсем понятно, что с чем складывается, почему после преобразования буквы «u» можно декодировать всю строку и откуда взялось 37.
Предполагаю, что тут имелось ввиду, что если дальше продолжать увеличивать значение основания системы счисления, то при основании системе счисления 31+ все символы строки «null» будут являться цифрами системы счисления и parseInt(null, 31) вернет 714695. А 37 взсялось оттуда, что максимальное занчение основания системы счисления для parseInt является 36.
«складывается с числом»ИМХО некоректный перевод
Оригинал:
It's converting null to the string «null» and trying to convert it. For radixes 0 through 23, there are no numerals it can convert, so it returns NaN. At 24, «n», the 14th letter, is added to the numeral system. At 31, «u», the 21st letter, is added and the entire string can be decoded. At 37 on there is no longer any valid numeral set that can be generated and NaN is returned.
console.log(' ' == +' ') //true
j>
это приглашение ноды):> null == 0
false
> null > 0
false
> null >= 0
true
Чем яваскрипт похож на скалу: и там, и там половину языка лучше просто не использовать. Но в скале хотя бы не получится использовать ненужные фичи нечаянно.
Почему же — почти все примеры про применение самых обыкновенных операций (+![]) к неожиданным типам.
Припоминая принцип KISS, я могу точно сказать что если вы распишете это в несколько условий/переменных/более длинных конструкций — вы не только не умрете от этого, но и позволите читать этот код другим людям, которые не считают, что конструкция (+![]) самая явная и сразу объясняет что хотел сказать автор этим опусом.
это был просто список проблемных операторов))) Вот пример:
function f(a, b) {
return a + b
}
Что делает эта функция? Какие типы данных может вернуть эта функция? Я бы сказал, что ответ непрост и не тривиален.
Эта функция принимает и возвращает те типы данных, которые разрешены в документации на нее.
1 < 2 < 3
true
паубивав бы за такие грязные хаки!
-2 < -1 < 0
false
Хотя натуральные языки тоже изменяются, заимствуют конструкции из других языков и перестают быть похожими на себя в классическом виде.
...JavaScript. Это отличный язык.
Можно не читать, расходитесь, тут не на что смотреть.
(4.1 * 10 * 60 / 10) == (4.1 * 60) // false
Это так почти в любом ЯП будет.
4.1 * 60 // 245.99999999999997
И так тоже почти в любом ЯП будет.
Одна из первых особенностей, которая врезалась в память, это то, что например дни getDate(), от 1 до 31 одного; а блин, месяцы getMonth(), от 0 до 11.
Так еще попало, на конец месяца и погрешность небольшая была. Тестов нет, метод IDE выдала, пока описание не открыл, думал с ума с хожу ))
дни getDate(), от 1 до 31 одного; а блин, месяцы getMonth(), от 0 до 11.
В Java ровно то же самое. И в принципе, это объяснимо. Можно множить год на 12 и прибавлять к нему месяц, не парясь о единице.
Для дней это не актуально, т.к. в месяце их разное количество.
Можно множить год на 12 и прибавлять к нему месяц, не парясь о единице.
Конечно, мне интересно почему так. Но ваше объяснение не совсем понятное. Например в PHP и дни, и месяцы считаются от единицы, очень удобно. Вы можете для сравнения написать два примера, чтобы было понятно или хотя бы пример того, что вы написали, только в коде? Буду благодарен.
Честно говоря, придумывать пример лень. Я имел в виду, что рассчитывать количество месяцев между двумя датами проще, чем если бы месяцы считались от единицы.
Я завидую ацтекам, у которых дни в месяце с нуля нумеровались...
Чем проще-то? Разницы никакой.
Вот если бы декабрь был 12-м месяцем, январь — первым и т.д., это создавало бы сложности.
Вот если бы декабрь был 12-м месяцем, январь — первым и т.д., это создавало бы сложности.
Так я ровно об этом и написал. Неужели я так непонятно выражаюсь?
Ну а если считать разницу между месяцами, тогда +1 роли не играет.
!!'false' == !!'true'
Идея здесь очень проста !!X — это двойное преобразование X в булево значение. В примере !!'false' значение X равно строке с текстом 'false'. Т.к. строка не пустая, то перевое преобразование с одним! даст значение false, а второе !false, что есть true.
Вот и все. Зачем городить какие-то сложности…
Ну и так далее.
А по поводу хороших задание для интервью — слушайте, нормальные люди решают задачи, а не показывают какие они заумные и крутые… Особенно эти тупые примены с ++i++y++, etc… Когда кто-то из молодых тут такое делает — автоматически на кислород… мне некогда разбираться, что это за идиоства…
оператор разбиения TODOПростите, что? :D
Что за черт, Javascript