Как стать автором
Обновить

Комментарии 79

Нужно быть очень умным и сильным программистом, чтобы хорошо знать javascript.
javascript это язык для браузеров, которые у миллиардов людей.
Миллиарды людей могут принести деньги бизнесу.
Очень умные и сильные программисты получают много денег за свой скилл.
Всё равно бизнесу хорошо, поэтому система живёт дальше.
НЛО прилетело и опубликовало эту надпись здесь

Тем, что автор показывает косяки, а так было бы на косяк меньше.
Как и с arguments vs. spread, один из которых не рекомендуется использовать, а второй совсем новый и не везде поддерживается.

А! Дайте ссылку на то, что arguments не рекомендуется использовать! Помню, что что-то такое видел, но обыскался.

Наследование может быть, но не от Object.


var fake = Object.create(null);

fake.key_a = "value_a";

fake.hasOwnProperty = function() {
    return true;
}

var obj = Object.create(fake);

Object.prototype.isPrototypeOf(obj); // => false
obj.hasOwnProperty("key_a"); // => true
Object.prototype.hasOwnProperty.call(obj, "key_a"); // => false
"key_a" in obj; // => true
Object.keys(obj); // => []

Поправьте, если ошибся.

НЛО прилетело и опубликовало эту надпись здесь

Пример в статье был приведен для случая с IE.
С другой стороны, в него закралась ошибка: пример справедлив для всех случаев, когда объект создается через Object.create().
Исправил.


Спасибо за развернутые замечания.

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

Так вот совет «так не делать» имеет очень ограниченное применение из-за особенной способности человеческого мозга ошибаться. Именно поэтому в тренде строгая типизация (а ведь можно было бы просто не передавать неправильные типы, правда?), чистые функции (а ведь можно было бы просто не гадить в универсум и не менять свойства объекта глубоко изнутри функции вывода лога) и так далее.

Я легко могу себе представить джуниора, который по запарке объявит локальную переменную `undefined`. Да, не пройдет CR. Да, нынче надо `var`, `let`, или что там впереди написать. Но это не убирает грабли, а маскирует их.

В очередной раз наткнулся на void 0. И снова стал терзать меня старый вопрос ― а для чего этот самый void вообще задумывался. Ну всяко же не для получения настоящего undefined-а. Лучшее что я пока накопал, это использование в рамках какого-нибудь однострочника, который что-нибудь делает, но так, чтобы результат однострочника был undefined. Т.е. грубо говоря a => void (some = a) вместо a => { some = a; }. Какой-то уж совсем бредовый use case. Не находите? Не могли же его и в самом деле придумать ради href="javascript:void(anything)"?

Скорее для навешивания обработчиков через аттибуты.

НЛО прилетело и опубликовало эту надпись здесь
Есть два типа языков программирования: те, которые люди постоянно ругают, и те, которыми никто не пользуется.
НЛО прилетело и опубликовало эту надпись здесь
«Похволяет писать плохо» != «плохой язык»
Строгие сравнения и явные приведения избавляют от множества багов связанных с вышеописанным)
В то время как обычное равенство при наличии null в сравнении всегда возвращает false.
Вы случайно не спутали с NaN?
null == null // true
null === null // true
NaN == NaN //false


Насчет for-in цикла, использовать его, вообще плохая практика, кроме описанных Вами проблем, он еще и отключает оптимизатор во всех актуальных сегодня компиляторах (v8, SpiderMonkey, Chakra)
Вместо него лучше использовать
//es2015 simple
for(let key of Object.getOwnPropertyNames(object)) {}

//es5
for(var keys = Object.getOwnPropertyNames(object), i = 0, key = keys[0]; i < keys.length; i++, key = keys[i]) {}

//es2015 object iterator
function* objectIterator(object) {
  for(let key of Object.getOwnPropertyNames(object)) {
    yield [key, object[key]];
  }
}
for(let [key, value] of objectIterator(object)) {}


Любые манипуляции с arguments кроме чтения по индексам выключает оптимизацию в v8 и SpiderMonkey (про Chakra точно сказать не могу)
То же истинно для переменных-аргументов функции
То же истинно для передачи arguments во внешние функции или сохранения его в другую переменную, но есть исключение — 2 аргумент нативного Function.prototype.apply
То есть так — func.apply(this, arguments) — нормально

Хак для получения global объекта в strict режиме:
function getGlobal() {
  return (new Function('return this;'))();
}


Ну и напоследок, нестрогие сравнения — дурной тон, мало того что работают в разы медленнее, так еще подвержены ошибкам
«нестрогие сравнения — дурной тон, мало того что работают в разы медленнее»
подскажите, где найти подробности об этом?
Про конкретное время работы не скажу, но строгие сравнения не приводят типы, поэтому они быстрее.
НЛО прилетело и опубликовало эту надпись здесь
Ни разу не видел ошибок из-за нестрого сравнения

У меня строго наоборот. Пример из жизни. Код, в котором изначально забили на строгое сравнение привёл к тому, что расплодились местами String-ые id-ки, а местами Number-ие. Всё это работало на mock-сервере. Стоило подключить backend со Scala-й и Cache, как мне пришлось выискивать и исправлять множество багов, ибо на сервере в лучшем случае всё дохло от несоответствия типов модели. А это ещё попробуй всё отследи, если кодовая база уже достаточно большая.


А ещё тот же lodash сверяет всё строго, и можно часами отлавливать какой-нибудь идиотский баг где _.filter(collection, { field: value }) не будет работать именно из-за этого. Оно ведь не упадёт. Оно просто ничего не найдёт. Полагаю библиотечные indexOf и прочие методы тоже дадут прикурить.


Суть — это всё ведёт к беспорядку. И на большой кодовой базе может стоить много денег. Стараюсь всегда когда это возможно использовать строгое равенство.

НЛО прилетело и опубликовало эту надпись здесь

Так вот оно в чём дело. Что же вы раньше то не сказали? Код который вы пишете не пахнет. Поэтому и проблемы нестрогих неравенств нет. Как и любых других проблем нет. Это у нас просто руки кривые. Теперь понятно. Ну извиняйте нас болезных, грешны мы.

НЛО прилетело и опубликовало эту надпись здесь
На ваши данные это не повлияло бы никак

Глупости то какие. if('2' !== 2); и if('2' != 2) это разные вещи. В случае нестрогих неравенств ваш код будет жить в тех ситуациях, когда нормальный код поломается. Это как в c++ можно выйти за пределы массива указав избыточный индекс элемента, исправить там что-нибудь, а потом ловить баги в совершенно неожиданных местах (потому что чёрт знает что вы там изменили в этой куче, то).


По поводу "хуже читаться" пассажа я не понял. Что за нелепая вкусовщина.

НЛО прилетело и опубликовало эту надпись здесь
что 2 и '2' совсем разные сущности

А должно быть иначе?


Это приводит к тому … везде по коду будут раскиданы … if (Number(sample.id) === id)

Пардон, не углядел Number(). Ок. Ну если вы целенаправлено будете убегать от проблемы такими вот хаками, то я не удивлюсь, если у вас всё развалится.


А теперь немного магии. Вы изначально пишете: if(a === b) и все счастливы. А если возникает необходимость приводить типы к друг другу, то вытаскиваете себя из этого болота за волосы, и переходите, наконец, на content-type: json.

> > что 2 и '2' совсем разные сущности

> А должно быть иначе?

Ну вот, сошлись, коса и камень. Нет никакого «должно быть». Если это API для ваших дорогих клиентов — то да, должно быть иначе. Если это внутренний микросервис, который нагорячую фигачит в базу — «иначе» нельзя ни в коем случае. Если это какой-то ваш внутренний кухонный комбайн, то должно быть так, как удобнее в 80% вариантов использования.

Я, например, когда имплементирую внутреннее API, всегда принимаю и 2 и '2' (а часто и «два» и `Два.new` и `id_of_2`), проверяю, что это такое к нам пожаловало и реагирую соответственно. Так что надо сначала обговорить предметную область обсуждения, а потом уже решать, можно ли считать 2 и '2' — одним и тем же. Подсказка: при обработке консольного ввода хоть у вас там наистрожайшая типизация, придется считать их одним и тем же.

Даже если я по какой-то неведомой причине буду писать js-backend сервис с API для дорогих клиентов, которые не могут совладать с типами в своих "json-запросах", поддержку их кривых запросов я буду делать на уровне адаптера. А вовсе не на уровне строгих-нестрогих неравенств в коде.


Для того, чтобы использовать == вместо === необходима веская причина. Второе ведёт к прилежному коду, ранним ошибкам (вместо поздних трудно вылавливаемых). Первое иногда может привести к доп. удобствам. Но только лишь иногда. Скажем if(!some) куда удобнее чем if(some === undefined || some === null || some === ''). Такие вещи действительно читаемее. Но и тут не стоит забывать про 0 :)

Не-не-не, вы меня неверно поняли. Я согласен с вами целиком и полностью, я лишь отреагировал на фразу [вне контекста, что зря] «А должно быть иначе?».

Иногда — должно, вот все, что я хотел сказать. Так-то я сам всегда использую `===` и даже вот этот ужас внутри `if` (обернутый в `check(UNDEFINED, NULL)`).
Иногда — должно

С этим спорить не буду. Ситуации бывают разные.

мне лень изучить основы языка, на котором я пишу

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


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

Про передачу arguments в другие функции
https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#3-managing-arguments

For… in не просто работает с объектом, как с хэш-таблицей, пробегая ещё по родителям ища перечисляемые типы. getOwnPropertyNamies же ищет перечисляемые только для объекта, которые могут вполне храниться в отдельном векторе, что удобнее.

Правила приведения легко запоминаются, да не легко вспоминаются, когда происходит компановка. Так давно пишу на строгом сравнении, что не понимаю откуда в неговнокоде берутся разные типы. Это же на уровнях интерфейсов определяется раз и навсегда.
Так что это не только микрооптимизация, но и попросту аккуратный код и логика.
Любые манипуляции с arguments кроме чтения по индексам выключает оптимизацию в v8 и SpiderMonkey (про Chakra точно сказать не могу)

… но только в нестрогом режиме. В строгом режиме arguments — это обычный массив, только с другим прототипом.

Это в каком браузере он — обычный массив?

В то время как обычное равенство при наличии null в сравнении всегда возвращает false.

Согласен, это был странный пассаж, ведь речь шла о числах. Спасибо, исправил.


Относительно проверки собственный свойств.
По идее, метод getOwnPropertyNames должен быть действительно быстрее: он не обходит свойства прототипов.
К тому же, метод доступен IE9+


Но если продолжать тему "пальбы по ногами", следует упомянуть, что свойства объектов могут обладать разными параметрами дескрипторов.
Так, например, некоторые свойства могут быть неитерируемыми enumerable = false.


var obj = Object.create(
    {}, 
    {
        "enum-0": { 
            value: "1",
            enumerable: true
        },
        "not-enum-1": { value: "0" },
        "not-enum-2": {
            value: "1",
            enumerable: false
        }
    }
);

Object.keys(obj); // => ["enum-0"]
Object.getOwnPropertyNames(obj); // => ["enum-0", "enum-1", "enum-2"]
Object.getOwnPropertyNames([1, 2, 3]); // => ["0", "1", "2", "length"]

Как можно заметить, метод Object.getOwnPropertyNames() выводит все ключи без разбора. Вероятно, целесообразней использовать Object.keys(), но это зависит от требований к программе.


Ко всему прочему, мне показалось, что из обсуждения выпал случай, когда мы проверяем собственное свойство объекта вне циклов — выборочно.


Относительно строгости сравнения, не готов сейчас ступать на "тонкий лед": на вскидку, мне тяжело сейчас вспомнить случай, когда последний раз нестрогое сравнение ломало программу.
Но случай когда вовремя неприведенные к нужному типу данные — вчера.


// образный пример
var uid = this.getAttribute("data-uid");
stash[uid + 1] = true; // вместо 1 я получил "01"
// можно легко забыть сделать stash[+uid + 1]

Спасибо за развернутый комментарий.

Кстати
NaN == NaN // false
вполне логично, если подумать. И я уверен, что это не баг дизайна языка, а фича.

Это стандарт IEEEчтототам. Хотя, я логики в этом не вижу.

А логика очень простая.
Очевидно же, что на практике никто никогда не будет сравнивать два NaN напрямую, это не имеет смысла.
В реальности будут сравниваться некие переменные, которые должны быть числовыми, но периодически там по каким-то причинам могут проскакивать невалидные данные (которые и превращаются в коде в NaN). И при равенстве этих чисел должно что-то происходить.
Но если невалидные данные пришли сразу в 2 переменные — то это две ошибки. Грубо говоря, это два «каких-то хз каких значения». И то что они оба «хз» не делает их равными по смыслу.
И если бы два NaN были бы равны, это дало бы нам ложно-положительное срабатывание условия.

Совершенно не вижу логики почему тут всегда false.


Number( parameter ) === NaN
По вышеописанной. Замена == на === ситуацию не меняет. Потому и пришлось придумывать isNaN() — чтобы оградить от соблазна сравнивать два NaN-значения. Каждое их них нужно проверить отдельно. Повторюсь «хз что 1» не равно «хз что 2» и это вполне логично.

Ваша логика применима к любому преобразованию с потерями. В частности, 0 не должен быть равен 0. А то вдруг в промежуточных вычислениях мы обе части уравнения умножили на 0.

Не, тут разница в том, что 0 — это конкретная сущность. Она имеет понятное значение и прикладной смысл.
А NaN — это языковая условность для обозначения «нечта непонятного».
Ноль (нуль, от лат. nullus — никакой)

null, 0, undefined, NaN — всё это языковые условности, имеющие чёткие, определённые стандартом, значения и прикладной смысл.

NaN (англ. Not-a-Number) — одно из особых состояний числа с плавающей запятой. Используется во многих математических библиотеках и математических сопроцессорах. Данное состояние может возникнуть в различных случаях, например, когда предыдущая математическая операция завершилась с неопределённым результатом, или если в ячейку памяти попало не удовлетворяющее условиям число.

Свойства

  • NaN не равен ни одному другому значению (даже самому себе); соответственно, самый простой метод проверки результата на NaN — это сравнение полученной величины с самой собой.

И что вы хотели этим сказать?

Потому что, если NaN будет нормально сравниваться, вы получите, например:

Number(5 * 'foo') === Number(10 * 'bar');

Что, очевидно, совсем не так.

А если 0 будет нормально сравниваться, вы получите, например:


5 * 0 === 10 * 0

Что, очевидно, совсем не так.

Да нет, с 0 как раз очевидно, что так.

Почему же с умножением строки на число результат для вас не очевиден?

Потому что над полем вещественных чисел определена операция умножения, это настолько очевидно, что даже странно уточнять.

А над полем строк операция умножения не определена.

Даже если отвлечься от того, что понятие «поле строк» существует только в вашей голове (и, вероятно, в книжках Бредбери), неопределенная операция умножения в точности означает, что результат не определен. Даже не знаю, как еще прозрачнее это сформулировать. Неопределен — значит ничему не равен. В том числе и самому себе. С делением на ноль та же фигня.

В некоторых языках, наоборот, операция умножения строки на число определена. Вот в руби, например:
'foo' * 5
#⇒ "foofoofoofoofoo"

Вот тут сравнивайте в свое удовольствие.

Впрочем, js и с делением на ноль выступает дегенеративненько:
5.0 / 0 === 10.0 / 0
true
А я и не говорил, что тут NaN. Я говорил, что JS ведет себя дегенеративно.

И да, я прекрасно понимаю, почему. Что не делает такое решение сколько-нибудь адекватным. Очевидно, что и для вещественных, и для целых (а других тут и нет) всегда выполняется

∞ ≠ ∞

Хороший смайл получился, кстати.
Бесконечность

В программировании

Стандартная арифметика с плавающей запятой (IEEE 754-2008) содержит особые значения для +∞ и −∞: порядок состоит из одних единиц (11…11), мантисса из одних нулей (00…00). Положительная бесконечность больше любого конечного числа, отрицательная — меньше любого. Операции с бесконечностью определяются особо: (+∞) + x = +∞, +∞ + (+∞) = +∞, +∞ − ∞ = NaN, log (+∞) = +∞, sin (+∞) = NaN, и т. д.


+∞ = +∞
-∞ = -∞
+∞ ≠ -∞

Вы в следующий раз не на Вику, а сразу на пикабу ссылку давайте.

Я же сказал: я прекрасно понимаю, почему так. Потому что разработчики некоторых языков не очень образованы и/или умны. Стандартная арифметика с плавающей запятой и мантиссой умерла в Фортране, лет 30 тому назад. Если бы все пользовались теми представлениями, которые удобны машине, у нас бы такие фокусы с флоатами до сих пор были, что ах, и знаки одного популярного в узких кругах числа мы бы до сих пор считали на бумажке.

Но нет, люди с тех пор научились BigDecimal, Rational, и так далее. Я бы посмотрел на ваше лицо, если бы ваш банк считал все флоатами и вот такими бесконечностями, которые даже не консистентны.

Есть масса нормальных языков, пригодных для вычислений. И есть вот это недоразумение для «кнопки подвигать в браузере». И не нужно сюда, пожалуйста, притягивать за уши безумные реплики из середины прошлого века про IEEE 754-2008, ладно?

Т.е. вы хотите сказать что какой-то другой язык программирования может решить задачу 1/0 не используя бесконечности? Да хрен с ним языком. Эта задача вообще решается без бесконечностей?

Она и с бесконечностями не решается. То, что написано в IEEE — полнейшая глупость с точки зрения математики.

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

Я же хотел сказать, что ∞ = ∞ автоматически делает аксиоматику поля рациональных чисел противоречивой. Что, как бы, не совсем комильфо. Потому что вместе с ∞ + x = ∞ оно жить не может. Поэтому правильным является подход в котором 1 / 0 ≠ 1 / 0 ≠ 2 / 0 ≠ ∞. Только тогда _пользователь_ этого кода сможет не писать разлапистых ифов на каждую бесконечность.

javascript — это диагноз. Зачем сравнение с void 0, typeof(x) == «undefined» отменили?
короче запись =) А вообще в CS можно писать эти самые undefined он сам в void 0 трансформирует)
НЛО прилетело и опубликовало эту надпись здесь
var obj = Object.create(null);
obj.key_a = «value_a»;
obj.hasOwnProperty(«key_a») // => Выбросит ошибку.

Ошибка тут в другом месте. Вы наследуете от null, а hasOwnProperty находится в прототипе Object.

НЛО прилетело и опубликовало эту надпись здесь
Ну вот, опять скука.
Правила приведения?
На самом деле они абсолютно верны — информация не теряется. Т.е.
Number(Boolean(34)) == 34 // => false
Boolean(Number(true)) == true // => true

Другое из раздела — аналогично.
Возьмем факт, что arguments — не массив. И? String тоже не массив, и NodeList тем паче. И никого не смущает, потому, что они лишь преопределяют скобки.

Обиднее всего то, что в других языках не меньше неочевидностей, просто к ним все привыкли.
А JavaScript — он плохой
Штука в том, что Array.isArray() работает только начиная с IE9+

Простите, а код
[1, 2, 3] instanceof Array

не знаете в каких браузерах может не работать?
new iframe.contentWindow.Array() instanceof Array // => false
Array.isArray(new iframe.contentWindow.Array()) // => true
Object.prototype.toString.call(new iframe.contentWindow.Array()) // => "[object Array]"

Экземпляры массива созданные внутри фреймов и других окон будут иметь разные экземпляры конструкторов


Кстати говоря, MDN предлагает в качестве полифила toString решение

Спасибо за информацию. Хотя конечно никогда не приходилось работать с фреймами.
+ к Вашему комменту добавлю, что в node.js разные контексты VM приведут к такому же результату, а так же это истинно для любых объектов, а не только массивов
Все что делает оператор instanceof — проверяет, что в цепочке прототипов объекта слева от него присутствует ссылка на прототип конструктора справа
Можно добавить в статью — вообще неочевидный нюанс, я вот не знал, хотя при всей своей природной скромности не могу назвать себя начинающим джисером.
NaN и Ifninity — это не типы, а специальные значения типа Number. И это не только в ДжС, а во всех типа х всех языков, полностью поддерживающих стандарт представления чисел с плавающей запятой IEEE… не помню.
Похоже на осеннее обострение аллергии на javascript. Ну, ничего, к зиме пройдёт.

По поводу последнего, контекст можно ещё и так проверять, кажется:


if (typeof window !== 'undefined') {
  // browser
} else {
  // Node.js
}
Советы из анабиоза. Это всегда было плохим стилем, а теперь так вообще не актуально. Спецификация ES5 запрещает переопределять undefined. Для колекций лучше использовать Map или Set которые есть во всех актуальных версиях браузеров (и без проблем полифилятся массивами), а не насиловать браузеры вызывая деоптимизацию. Магии нет, есть спецификация и преобразование типов достаточно с ними разобраться и больше не смеяться с видео javascript WTF. Например массив с join не конвертирует пустые значения в пустую строку, а тупо их пропускает. Уже IE10 устаревший браузер, прекращайте поддерживать IE8 и всю эту китайщину. Если прямо нужно — лучше использовать shim.
this это же ссылка, в js нет указателей, не?
Еще одна статья в духе JavaScript — говно.
Лучше бы написали как на этом «говне» писать качественный код.
!isNaN(Infinity) // => true – ожидалось что-то иное?
Как вообще тогда определять числа? Проверить их конечность!

Почему не !isNaN(value)?


В таком хэше отсутствуют наследуемые ключи — только собственные (гипотетическая экономия памяти).

Никакой экономии, так как у объекта по прежнему есть свойство на прототип, только оно равно null. То есть объект занимает столько же памяти.
А если используя Object.key() или аналог, то под хранение ключей создается массив строк, который требует памяти, а после попадает в GC как мусор.


Казалось бы, вот он идеальный способ. Но, Internet Explorer.

Про это был большой разнос http://webreflection.blogspot.ru/2014/04/all-ie-objects-are-broken.html


arguments = Array.prototype.slice.call(arguments, 0); // Превращение в массив

В strict mode будет исключение – нельзя переопределять arguments.


а вот ...rest — массив

Все верно, происходит destructuring, то есть неявно итерируется arguments с получением массива в качестве результата.


В анонимных функциях указатель this ссылается на глобальный объект.

Совсем не факт. Но в комментариях уже писали, что наиболее правильный способ это new Function('return this')().

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории