Comments 68
Можно было просто дать ссылку на wtfjs.com/ там это все есть и даже больше, гораздо больше
Можно легализовать поддержку нотации WTFJS во всех IDE — на уровне с TODO и FIXME :)
// WTFJS: We parse till we get an error using radix 16 => 0xf => 15 :)
var fifteen = parseInt('fuckItMan', 16);
Большая часть примеров оттуда. Я этого не скрываю и даже в метках указал. В качестве цели статьи я ставил разбор логики поведения. По-моему, простой просмотр «смотри какая хрень в JavaScript» не несет пользы, полезно понять почему именно так.
Возможно, выступлю в роли кэпа, но все же:
var x = NaN < 10;
var y = NaN >= 10;
x == y;
var x = NaN < 10;
var y = NaN >= 10;
x == y;
UFO just landed and posted this here
А еще хорошо бы сказать: «НИКОГДА ТАК НЕ ДЕЛАЙТЕ» в своем коде.
UFO just landed and posted this here
Еще одна задачка…
Длинна масивов:
[]
[,]
[,,,]
[,undefined,]
[,NaN,]
[,NaN,null,0]
[,null,NaN,]
Длинна масивов:
[]
[,]
[,,,]
[,undefined,]
[,NaN,]
[,NaN,null,0]
[,null,NaN,]
Запись с последней запятой чревата проблемами с подсчетом в старых IE. Но в соответствии с ECMAScript 5 я бы своими словами описал это так:
— если нет запятых и нет даже одного элемента, то массив пустой (длина = 0);
— если элемент 1 и нет запятых, то длина = 1;
— если перечисление элементов заканчивается запятой, то длина массива равна количеству запятых (в старых ие еще +1, а посему avoid!);
— если перечисление элементов заканчивается любым значением (хоть undefined), то длина массива равна количеству запятых + 1.
— если нет запятых и нет даже одного элемента, то массив пустой (длина = 0);
— если элемент 1 и нет запятых, то длина = 1;
— если перечисление элементов заканчивается запятой, то длина массива равна количеству запятых (в старых ие еще +1, а посему avoid!);
— если перечисление элементов заканчивается любым значением (хоть undefined), то длина массива равна количеству запятых + 1.
[] //0
[,] //1
[,,,] //3
[,undefined,] //2
[,NaN,] //2
[,NaN,null,0] //4
[,null,NaN,] //3
В массивах допускается завершающая запятая. Тут я могу только сослаться на Флэнагана. Раздел 7.1:
«Если литерал массива содержит несколько идущих подряд запятых без значений между ними, создается разреженный массив. Элементы, соответствующие таким пропущенным значениям, отсутствуют в массиве, но при обращении к ним возвращается значение undefined.»
«Синтаксис литералов массивов позволяет вставлять необязательную завершающую запятую.»
Зачем так сделано мне не понятно. Если учесть, что IE8 и младше падает от лишней запятой, а дефолтные настройки JSLint определяют ее как ошибку, то логично предположить, что это браузер костыль от ошибок в JS коде.
Было недавно еще (справедливо для JS, PHP, C++)
>>> parseInt((0.1 + 0.7) * 10);
7
>>> (0.1 + 0.7) * 10;
7.999999999999999
>>> parseInt((0.1 + 0.7) * 10);
7
>>> (0.1 + 0.7) * 10;
7.999999999999999
isFinite(1 / Number.MIN_VALUE); // ?
(1 / Number.MAX_VALUE) !== 0; // ?
Скрытый текст
Это не особенность JS как такового, а типа Number (который double).
А наоборот — пожалуйста, точности хватает.
Подробнее о том, почему в отрицательную сторону экспонента немного «вместительнее» — на википедии.
*UPD:* Поправил пример
isFinite(1 / Number.MIN_VALUE); // false
(1 / Number.MAX_VALUE) !== 0; // true
Это не особенность JS как такового, а типа Number (который double).
Number.MIN_VALUE
равен 5×10-324, 1 / Number.MIN_VALUE
по-идее должен бы быть равен 2×10323, но это число слишком большое. А наоборот — пожалуйста, точности хватает.
Подробнее о том, почему в отрицательную сторону экспонента немного «вместительнее» — на википедии.
*UPD:* Поправил пример
OMG, зачем ты позволил людям создать язык программирования, который допускает такие ребусы?
— Мыкола, як москали жаву кличут?
— Як?
— йава!
— Повбывав бы.
— Мыкола, як москали жаву кличут?
— Як?
— йава!
— Повбывав бы.
в тексте 4 привидения **-()
Стыдно сказать, но правильно решил 3/10.
А ведь считаю себя неплохим спецом по JS %)
Ну, справедливости ради, стоит сказать, что ни один из приведенных примеров в реальной боевой обстановке не применяется, и не должен применяться.
Но все равно, как человеку, читавшему спеку стандарта, стыдно.
А ведь считаю себя неплохим спецом по JS %)
Ну, справедливости ради, стоит сказать, что ни один из приведенных примеров в реальной боевой обстановке не применяется, и не должен применяться.
Но все равно, как человеку, читавшему спеку стандарта, стыдно.
Вы просто издеваетесь над стариком Крокфордом. Он столько времени и сил потратил на то, чтобы приучить людей «не кидаться едой» (где еда — это JavaScript, а «кидаться» — это сравнивать наны с фолсами и складывать объекты с массивами), а употреблять ее по назначению активно пользуясь ножом и вилкой (такими как JSLint и использование только «безопасного» подмножества языка), ан нет — детвора не только продолжает развлекаться, но и устраивает из этого соревнования и меряется тем, кто дальше докинет :c
«Why am I a » + typeof + "";
«Who am I a number»
JS еще и наречие меняет?
Ох, как же набили оскомину статьи подобного рода… Удивительно даже, что здесь нет
Или вот ещё недавно всплыло в твитере:
И если с sort, map и parseInt/parseFloat понять ещё можно — люди привыкли, что в других языках действует так, то другие вещи просто удивляют как степенью непонимания данного кода автором, так и бессмысленностью написанного выражения.
Ну вот зачем, например, сравнивать массивы? Давайте ещё функции сравним по приколу, это тоже возможно (навскидку, сравниваются их .length, которые равны арности функции), но какой в этом практический смысл?
Но вообще — читайте документацию и многие нелогичности окажутся просто другим поведением и обретут смысл…
[1, 10, 5].sort
или [1, 2, 3].map(parseInt)
Или вот ещё недавно всплыло в твитере:
var a,b=[1,2,3][1,2,3];
a === b;
И если с sort, map и parseInt/parseFloat понять ещё можно — люди привыкли, что в других языках действует так, то другие вещи просто удивляют как степенью непонимания данного кода автором, так и бессмысленностью написанного выражения.
Ну вот зачем, например, сравнивать массивы? Давайте ещё функции сравним по приколу, это тоже возможно (навскидку, сравниваются их .length, которые равны арности функции), но какой в этом практический смысл?
Но вообще — читайте документацию и многие нелогичности окажутся просто другим поведением и обретут смысл…
[1, 2, 3].map(parseInt)
внимательное чтение документации от такого спасет. Хотя лучше всего увидеть такой пример в статье, обратить внимание и, если не запомнить, то в следующий раз знать куда копать.
Для тех кто не знает в чем проблема. Map передает обработчику несколько аргументов. В частности вторым передает номер элемента в массиве. У функции parseInt, есть второй, необязательный, аргумент, который указывает на систему счисления. Таким образом в указанной записи второй и третий элемент массива будут обработаны в единичной и двоичной системе счисления и parseInt от них вернет NaN.
Или вот ещё недавно всплыло в твитере:
var a,b=[1,2,3][1,2,3]; a === b;
так массивы создавать нельзя, поэтому в обоих переменных лежит undefined, который равен сам себе.
Давайте ещё функции сравним по приколу, это тоже возможно (навскидку, сравниваются их .length, которые равны арности функции)
Функции это объекты, поэтому их можно сравнивать только на больше\меньше и тогда перед сравнением они будут приводится к строке. В случае проверки на равенство все так же как для массивов в пункте 3:
В случае оператора “==”” привидение к примтивам осуществляется только если один из аргументов примитив, а во всех остальных случаях оператор “==” возвращает true только если оба аргумента ссылаются на один объект (пункт 11.9.3 “Алгоритм сравнения абстрактного равенства”).
про набили оскомину я косвенно ответил в начале комментария. Глупо отрицать, что в JS есть грабли, но по-моему лучше узнавать их месторасположения из таких статей, а не на боевом коде.
Я привёл пример про var a,b=[1,2,3][1,2,3] для того, чтобы показать, что люди уже даже в обычных конструкциях уже почему-то подразумевают магию…
Я понимаю, что некоторые вещи в js неочевидны, но это столько раз было обсуждено, что это я не знаю, как написать статью про -тся/-ться, хотя по этому поводу даже сайты отдельные есть.
Ну и грабли есть во всех языках. Как-то адепты perl ещё не подтянулись, чтобы показать всю суть магии…
Я понимаю, что некоторые вещи в js неочевидны, но это столько раз было обсуждено, что это я не знаю, как написать статью про -тся/-ться, хотя по этому поводу даже сайты отдельные есть.
Ну и грабли есть во всех языках. Как-то адепты perl ещё не подтянулись, чтобы показать всю суть магии…
UFO just landed and posted this here
Может быть я не так вас понял, но вы думаете, что так
можно создавать массивы? И что будет лежать в переменной а?
var a = [1,2,3][1,2,3];
можно создавать массивы? И что будет лежать в переменной а?
Ну вот зачем, например, сравнивать массивы?
Например, есть массив на входе и есть несколько захардкоженных массивов-образцов, соответствие с одним из которых надо найти. Пока задачи оптимизации не стоит — самое простое решение: полный перебор и сравнение массивов целиком.
Но вообще — читайте документацию и многие нелогичности окажутся просто другим поведением и обретут смысл…
Одно дело, когда пытаешься разобраться как работает работающий код и совсем другое, когда пишешь код, который вроде бы должен работать, но на некоторых данных ведёт себя не логично (исходя из здравого смысла, а не спецификаций) и точное место фэйла неизвестно. Тут ещё большое значение имеет, что для многих (и проектов, и разработчиков) JavaScript не является основным языком и делаются на нём второстепенные задачи типа предварительной валидации или получение данных с сервера и их визуализации.
Ну, разработчикам, для которых JavaScript не основной язык, я бы посоветовал использовать готовые решения, благо их более чем достаточно
Я вот ни разу не читал стандартов полностью и очень редко натыкался на какие-то сильные нелогичности в js, которые прям непонятно-непонятно, почему оно так…
Может быть есть какие-то реальные примеры? Напимер, можно было бы описать «магическую» конструкцию и случай, как на неё наткнулся в реальном коде
Я вот ни разу не читал стандартов полностью и очень редко натыкался на какие-то сильные нелогичности в js, которые прям непонятно-непонятно, почему оно так…
Может быть есть какие-то реальные примеры? Напимер, можно было бы описать «магическую» конструкцию и случай, как на неё наткнулся в реальном коде
Вот как раз с готовыми решениями основные проблемы и возникают. Даже если как-то умудришься его найти, то, как правило, оно универсальное, делает больше чем требуется, и на изучение нюансов (по сути на реверс кода) уходит уйма времени.
А вообще я скорее не про «наткнулся», а про «написал». А из наткнувшегося долго, например, стопорили работу конструкции типа if (!~smth1.indexOf(smth2)) — пока разберешься во всех крайних случаях, составишь таблицу истинности, учтешь два неявных преобразования типа и т. п. — быстрее самому написать по ощущениям, что это значит.
А вообще я скорее не про «наткнулся», а про «написал». А из наткнувшегося долго, например, стопорили работу конструкции типа if (!~smth1.indexOf(smth2)) — пока разберешься во всех крайних случаях, составишь таблицу истинности, учтешь два неявных преобразования типа и т. п. — быстрее самому написать по ощущениям, что это значит.
так можно же загуглить) ну, или стараться не использовать у себя такие либы, действия которых сложно понять
!~, конечно, зло. Обычно такими вещами балуются люди недавно прочитавшие про них и не понимающие, что уменьшать объем кода — это работа минификатора, а человек должен писать читабельный код. Про такие вещи было хорошее обсуждение в комментариях к статье про "|0", к сожалению, не смог её сейчас найти.
Одно дело, когда пытаешься разобраться как работает работающий код и совсем другое, когда пишешь код, который вроде бы должен работать, но на некоторых данных ведёт себя не логично (исходя из здравого смысла, а не спецификаций) и точное место фэйла неизвестно.
Вот от такого меня периодическое решение примеров с wtfjs и спасает. При решении обычных рабочих задач мне в голову не придет читать спецификации. Документацию по новому или редко используемому API — да, но не спецификацию. И у многих думаю похожая ситуация. Решая задачки на неочевидные моменты языка я во-первых, проникаюсь логикой языка, во-вторых, создаю в голове некую «карту граблей». И это позволяет мне в рабочем коде намного быстрее понять причины нелогичного поведения на некоторых данных.
С разработчиками, которые пишут на JavaScript относясь к нему как к второстепенному языку, вообще печальная картина. Мало того, что в результате получается макаронное месиво на JQuery и шутки про «как мне сложить два числа на JS? используй плагин JQuery», так получившееся творение течет и тормозит. И многие считают это нормальным. Почему-то никому не придет в голову отправить JS-ника «по быстрому накидать валидацию» на C++, т.к. понимают какого качестве в большинстве случаев будет полученный код, а вот джависта отправить костылять на JavaScript — это почему-то нормально.
alert((([][[]]+[])[+!+[]])+((!![]+[])[+!+[]+!+[]+!+[]])+((!![]+[])[+!+[]])+(([][[]]+[])[+!+[]+!+[]])+((![]+[])[+!+[]+!+[]+!+[]]));
Запустил из консоли. Не понял. Как? о_О
jsfuck.com
Основная идея в том, что берем какую-то штуку которая возвращает, скажем, undefined, теперь чтоб получить 'n' нужно сделать 'undefined'[1], а чтоб получить единичку играемся с приведениями и пишем +!+[]. Остальные буквы получаем таким же образом и конкатенируем это все. Ну и в конце еще дописываем всякой фигни для того чтоб заклинание было пострашнее.
В прошлом году в одном из постов на хабре была лекция по Java. Так вот там у лектора я узнал термин, которым он назвал чрезмерное усложнение кода: Job Security Index. Т.е. пишем код так, чтоб его было чрезвычайно трудно сопровождать/рефакторить кому-либо кроме вас.
Используйте вышеперечисленные примеры в своём коде — и вас никогда не уволят! (Конечно если вы программист в госучреждении/большом предприятии)
Используйте вышеперечисленные примеры в своём коде — и вас никогда не уволят! (Конечно если вы программист в госучреждении/большом предприятии)
К сожалению, существуют намного более простые способы писать неподдерживаемый код обеспечивая себе Job Security.
Конечно если вы программист в госучреждении/большом предприятии
Странное уточнение. Как раз большие предприятия могут позволить себе нанять хоть несколько человек, которые будут разбираться в написанном таким специалистом коде и приводить его к человеческому виду.
Вы видимо не работали на таком предприятии
Работал. И меня плавно заменили, когда не сошлись с начальством в вопросе о том должен ли программист выполнять обязанности первой линии техподдержки.
Как так «заменили»?
Наняли человека, который несколько месяцев читал и переписывал мой код. Потом сказали «спасибо, прощайте».
Интересно бы узнать — переписывал ли он по стандартам или так чтоб его увольняли дольше Вас?
> Потом сказали «спасибо, прощайте».
Я не знаю в какой стране это было, но в России такое не возможно.
Я не знаю в какой стране это было, но в России такое не возможно.
Это ещё почему?
По ТК
По соглашению сторон возможно. Мне описали «прелести» работы по ТК (строгий график, отсутствие заданий или иногда самые легкие (читай — неинтересные) при невозможности заниматься своими делами, голый оклад и т. п.), которые меня ждут, я подумал и согласился. Даже не столько из-за «прелестей», сколько из-за нежелания «работать» (если это можно назвать работой) у людей, которые не хотят, чтобы я у них работал.
«Сначала JavaScript пробует преобразовать аргумент к числу с помощью метода valueOf и только когда это не удается приводит к строковому типу»
Не совсем так ведь. Сначала JavaScript вызывает valueOf, а если его нет, то вызывает toString, но как бы это странно не звучало toString() не обязательно должен преобразовывать в строку. Если вы переопределите метод toString(), то результат может поменяться, поэтому это слегка неточно говорить, что массив преобразуется в строку. Это результат при нормальных условиях, а не правило. А правило именно — вызов метода toString(), когда отсутствует valueOf()
Вот попробуйте это
Array.prototype.toString = function(){
return 42;
}
console.log([1, 2, 3] < [1, 2, 4]);
Не совсем так ведь. Сначала JavaScript вызывает valueOf, а если его нет, то вызывает toString, но как бы это странно не звучало toString() не обязательно должен преобразовывать в строку. Если вы переопределите метод toString(), то результат может поменяться, поэтому это слегка неточно говорить, что массив преобразуется в строку. Это результат при нормальных условиях, а не правило. А правило именно — вызов метода toString(), когда отсутствует valueOf()
Вот попробуйте это
Array.prototype.toString = function(){
return 42;
}
console.log([1, 2, 3] < [1, 2, 4]);
Да, есть такое дело. Сам иногда даю на собеседованиях задачки на переопределение прототипа. Например, такую:
Дан код:
Что нужно добавить в начало программы, чтобы rx1 и rx2 стали идентичными? Сами строчки трогать нельзя.
Дан код:
var rx1 = RegExp(/regular/ + /expression/);
var rx2 = /regularexpression/;
Что нужно добавить в начало программы, чтобы rx1 и rx2 стали идентичными? Сами строчки трогать нельзя.
Все правильно в статье. Если значение не примитив, будет вызван метод valueOf. Если значение, которое вернет valueOf, не примитив, тогда будет вызван метод toString для исходного обьекта. Так как массив не имеет собственного метода valueOf, он наследует Object.prototype.valueOf, который по умолчанию возвращает ссылку на исходный обьект.
9.1 ToPrimitive
8.12.8 [[DefaultValue]] (hint)
var arr = [];
arr.valueOf() === arr; // true
9.1 ToPrimitive
8.12.8 [[DefaultValue]] (hint)
Мне штука с NaN != NaN запомнилась, не помню правда из какой статьи.
Есть только одна вещь, которую я боюсь что никогда не пойму… Не уверен насчет того есть ли это в стандарте. Во всех движках захардкодено.
NaN === Infinity //false разумеется
а дальше интереснее:
NaN == Infinity //false
NaN > Infinity //false
NaN < Infinity //false
Поясните мне, ну как это?!
NaN === Infinity //false разумеется
а дальше интереснее:
NaN == Infinity //false
NaN > Infinity //false
NaN < Infinity //false
Поясните мне, ну как это?!
Вообще результат undefined, но он приводится к false.
P.S. похожая ситуация в SQL с NULL.
P.S. похожая ситуация в SQL с NULL.
NaN === NaN // => false
0 === -0 // => true
Любое сравнение с NaN возвращает false, так как NaN — не число, и что за ним стоит — неизвестно — +'foo' — NaN, как и +{} — NaN. Так же основные алгоритмы сравнения не видят разницы между +0 и -0, хотя, например,
1 / 0 === Infinity
, а 1 / -0 === -Infinity
, см. спецификацию.Есть внутренний алгоритм сравнения SameValue, в ECMAScript 6 вынесенный наружу как Object.is. Для этого алгоритма сравнения NaN равен NaN, +0 и -0 различаются, а в остальном результаты соответствуют оператору ===.
Object.is(NaN, NaN) // => true
Object.is(0, -0) // => false
Object.is(42, 42) // => true
Object.is(42, '42') // => false
Sign up to leave a comment.
JavaScript: проверьте свою интуицию