Javascript-паноптикум

За время, что мне довелось писать на Javascript, у меня сложился образ, что js и его спецификация это шкатулка с потайным дном. Иногда кажется, что ничего секретного в ней нет, как вдруг магия стучит в ваш дом: шкатулка раскрывается, оттуда выскакивают черти, по-домашнему исполняют блюз и резво скрываются обратно в шкатулке. Позднее вы узнаете причину: стол повело и шкатулку наклонило на 5 градусов, что вызвало чертей. С тех пор вы не знаете, это фича шкатулки, или лучше все-таки покрепче замотать её изолентой. И так до следующего раза, пока шкатулка не подарит новую историю.


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


«Сумма пустот»


При сливании массива в строку используя метод .join(), некоторые пустые типы: null, undefined, массив с нулевой длиной — конвертируются в пустую строку. И справедливо это только для случая когда они расположены в массиве.


[void 0, null, []].join("") == false // => true
[void 0, null, []].join("") === "" // => true

// Не работает при сложении со строкой.
void 0 + "" // => "undefined"
null + "" // => "null"
[] + "" // => ""

На практике такое поведение можно использовать для отсева действительно пустых данных


var isEmpty = (a, b, c) => {
    return ![a, b, c].join("");
}

var isEmpty = (...rest) => {
    return !rest.join("");
}

isEmpty(void 0, [], null) // => true
isEmpty(void 0, [], null, 0) // => false
isEmpty(void 0, [], null, {}) // => false. С пустым объектом такой трюк не проходит

// Или так, в случае если аргумент один
var isEmpty = (arg) => {
    return !([arg] + "");
}

isEmpty(null) // => true
isEmpty(void 0) // => true
isEmpty(0) // => false

«Странные числа»


Попытка определить типы для NaN и Infinity при помощи оператора typeof как результат вернет "number"


typeof NaN // => "number"
typeof Infinity// => "number"
!isNaN(Infinity) // => true

Юмор в том, что NaN — это сокращение от "Not-A-Number", а бесконечность (Infinity) сложно назвать числом.


Как вообще тогда определять числа? Проверить их конечность!


function isNumber(n) {
    return isFinite(n);
}

isNumber(parseFloat("mr. Number")) // => false
isNumber(0) // => true
isNumber("1.2") // => true
isNumber("abc") // => false
isNumber(1/0) // => false

«Для отстрела ноги возьмите объект»


Для javascript Object — одна из самых первых структур данных и в тот же момент, на мой взгляд, — король хитросплетений.


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


В противном случае, в итерацию могут попасть свойства из расширения прототипа.


Object.prototype.theThief = "Альберт Спика";
Object.prototype.herLover = "Майкл";

var obj = {
    theCook: "Ричард Борст",
    hisWife: "Джорджина"
};

for (var prop in obj) {
    obj[prop]; // Цикл обойдет: "Ричард Борст", "Джорджина", "Альберт Спика", "Майкл"

    if (!obj.hasOwnProperty(prop)) continue;

    obj[prop]; // Цикл обойдет: "Ричард Борст", "Джорджина"
}

Между тем, Object можно создать и без наследования прототипа.


// Несложная инструкция по прострелу ноги
var obj = Object.create(null);
obj.key_a = "value_a";
obj.hasOwnProperty("key_a") // => Выбросит ошибку.

"Эй, кэп, а зачем это нужно?"


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


И так как в таком случае вы не можете контролировать вводимые данные, необходим универсальный способ проверять собственные ключи в объекте.


Способ первый. Можно получить все ключи. Неоптимальный, если выполнять indexOf внутри цикла: лишний обход массива.


Object.keys(obj); // => ["key_a"]

Способ второй. Вызывать метод hasOwnProperty с измененным контекстом


Object.prototype.hasOwnProperty.call(obj, "key_a") // => true

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


// Выполнять в IE

// Создать объект без прототипа
var obj = Object.create(null);
obj[0] = "a";
obj[1] = "b";
obj[2] = "c";

Object.prototype.hasOwnProperty.call(obj, 1); // => false
Object.prototype.hasOwnProperty.call(obj, "1"); // => false
Object.keys(obj); // => ["0", "1", "2"]

obj.a = 1;

Object.prototype.hasOwnProperty.call(obj, 1); // => true
Object.prototype.hasOwnProperty.call(obj, "1"); // => true

// Случай когда объект создается с прототипом от Object
obj = Object.create(Object.prototype);

obj["2"] = 2;
obj.hasOwnProperty("2"); // => false

obj.a = "a";
obj.hasOwnProperty("2"); // => true

delete obj.a;

obj.hasOwnProperty("2"); // => false

Вам не показалось, IE действительно отказывается проверять цифровые ключи в объектах созданный через Object.create(), до тех пор, пока в нем не появится хотя бы один строчный.


И этот факт портит весь праздник.


UPD:
Решение предложенное Дмитрием Коробкиным


UPD:
bingo347 справедливо заметил, что если не писать скрипты для "динозавров", то перебор собственных свойств целесообразней выполнять при помощи Object.keys(obj) и Object.getOwnPropertyNames(obj)


Но, следует иметь ввиду ньюанс, что getOwnPropertyNames возвращает все собственные ключи, даже те, что неитерабельны.


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

«лже-undefined»


Часто разработчики проверяют переменные на undefined прямым сравнением


((arg) => {
    return arg === undefined; // => true
})();

Аналогично поступают и с присваиванием


(() => {
    return {
        "undefined": undefined
    }
})();

"Засада" кроется в том, что undefined можно переопределить


((arg) => {
    var undefined = "Happy debugging m[\D]+s!";
    return {
        "undefined": undefined,
        "arg": arg,
        "arg === undefined": arg === undefined, // => false
    };
})();

Эти знания лишают сна: получается, что можно сломать весь проект, просто переопределив undefined внутри замыкания.


Но есть пара надежных способов сравнить или назначить undefined — это использовать оператор void или объявить пустую переменную


((arg) => {
    var undefined = "Happy debugging!";
    return {
        "void 0": void 0,
        "arg": arg,
        "arg === void 0": arg === void 0 // => true
    };
})();

((arg) => {
    var undef, undefined = "Happy!";
    return {
        "undef": undef,
        "arg": arg,
        "arg === undef": arg === undef // => true
    };
})();

«Сравнение Шрёдингера»


Однажды коллеги поделились со мной интересной аномалией.


0 < null; // false
0 > null; // false
0 == null; // false
0 <= null; // true
0 >= null // true

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


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


Если принять во внимание, что null после приведения в число становится +0, внутри компилятора сравнение приблизительно выглядит так:


0 < 0; // false
0 > 0; // false
0 == null; // false
0 <= 0; // true
0 >= 0 // true

Сравнение чисел с Boolean


-1 == false; // => false
-1 == true; // => false

В javascript при сравнении Number с Boolean, последний приводится к числу, после производится сравнение Number == Number.


И, так как, false приводится к +0, а true приводится к +1, внутри компилятора сравнение обретает вид:


-1 == 0 // => false
-1 == 1 // => false

Однако.


if (-1) "true"; // => "true"
if (0) "false"; // => undefined
if (1) "true"; // => "true"

if (NaN) "false"; // => undefined
if (Infinity) "true" // => "true"

Потому что 0 и NaN всегда приводятся к false, все остальное true.


Проверка на массив


В JS Array наследуются от Object и, по сути, являются объектами с числовыми ключами


typeof {a: 1}; // => "object"
typeof [1, 2, 3]; // => "object"
Array.isArray([1, 2, 3]); // => true

Штука в том, что Array.isArray() работает только начиная с IE9+


Но есть и другой способ


Object.prototype.toString.call([1, 2, 3]); // => "[object Array]"

// Соответственно
function isArray(arr) {
    return Object.prototype.toString.call(arr) == "[object Array]";
}

isArray([1, 2, 3]) // => true

Вообще используя Object.prototype.toString.call(something) можно получить много других типов.


UPD:
boldyrev_gene задал, на мой взгляд, хороший вопрос: почему не использовать instanceof?


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


var 
    iframe = document.querySelector("iframe"),
    IframeArray = iframe.contentWindow.Array;

new IframeArray() instanceof Array; // => false
Array.isArray(new IframeArray()); // => true
Object.prototype.toString.call(new IframeArray()); // => "[object Array]"

arguments — не массив


Настолько часто забываю об этом, что решил даже выписать.


(function fn() {
    return [
        typeof arguments, // => "object"
        Array.isArray(arguments), // => false
        Object.prototype.toString.call(arguments) // => "[object Arguments]";
    ];
})(1, 2, 3);

А так как arguments — не массив, то в нем недоступны привычные методы .push(), .concat() и др. И в случае если нам необходимо работать с arguments как с коллекцией, существует решение:


(function fn() {
    arguments = Array.prototype.slice.call(arguments, 0); // Превращение в массив
    return [
        typeof arguments, // => "object"
        Array.isArray(arguments), // => true
        Object.prototype.toString.call(arguments) // => "[object Array]";
    ];
})(1, 2, 3);

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


(function fn(...rest) {
    return Array.isArray(rest) // => true. Oh, wait...
})(1, 2, 3);

Поймать global. Или определяем среду выполнения скрипта


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


И так как в JS не предусмотрен механизм определения среды выполнения на уровне стандартной библиотеки, можно сделать финт используя особенность поведения указателя внутри анонимных функций в нестрогом режиме.


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


function getEnv() {
    return (function() {
        var type = Object.prototype.toString.call(this);

        if (type == "[object Window]")
            return "browser";

        if (type == "[object global]")
            return "nodejs";
    })();
};

Однако в строгом режиме this является undefined, что ломает способ. Этот способ актуален в случае если global или window объявлен вручную и глобально — защита от "хитрых" библиотек.




Спасибо за внимание! Надеюсь, кому-нибудь эти заметки пригодятся и послужат пользой.

Similar posts

Ads
AdBlock has stolen the banner, but banners are not teeth — they will be back

More

Comments 79

    –3
    Нужно быть очень умным и сильным программистом, чтобы хорошо знать javascript.
    javascript это язык для браузеров, которые у миллиардов людей.
    Миллиарды людей могут принести деньги бизнесу.
    Очень умные и сильные программисты получают много денег за свой скилл.
    Всё равно бизнесу хорошо, поэтому система живёт дальше.
    • UFO just landed and posted this here
        0

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

        +1

        Наследование может быть, но не от 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); // => []

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

        • UFO just landed and posted this here
            0

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


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

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

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

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

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

            0

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

            0
            А разгадка одна.
              +11
              Есть два типа языков программирования: те, которые люди постоянно ругают, и те, которыми никто не пользуется.
                –5
                Есть языки программирования, которые нужно тщательно изучить чтобы ругать их, а есть такие, с которыми достаточно поверхностного знакомства, чтобы грязно выругаться.
                0
                «Похволяет писать плохо» != «плохой язык»
                Строгие сравнения и явные приведения избавляют от множества багов связанных с вышеописанным)
                +1
                В то время как обычное равенство при наличии 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;'))();
                }
                


                Ну и напоследок, нестрогие сравнения — дурной тон, мало того что работают в разы медленнее, так еще подвержены ошибкам
                  0
                  «нестрогие сравнения — дурной тон, мало того что работают в разы медленнее»
                  подскажите, где найти подробности об этом?
                    +1
                    Про конкретное время работы не скажу, но строгие сравнения не приводят типы, поэтому они быстрее.
                  • UFO just landed and posted this here
                      0
                      Ни разу не видел ошибок из-за нестрого сравнения

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


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


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

                      • UFO just landed and posted this here
                          0

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

                          • UFO just landed and posted this here
                              0
                              На ваши данные это не повлияло бы никак

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


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

                              • UFO just landed and posted this here
                                  0
                                  что 2 и '2' совсем разные сущности

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


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

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


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

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

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

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

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

                                      +2

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


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

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

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

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

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

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


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

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

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

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

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

                            0

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

                            0
                            В то время как обычное равенство при наличии 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]

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

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

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

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

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


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

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

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

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

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

                                        Свойства

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

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

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

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

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

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


                                            5 * 0 === 10 * 0

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

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

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

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

                                                    –1

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

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

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

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

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

                                                        1/0 === -1/0  //false
                                                        
                                                          –1
                                                          А я и не говорил, что тут NaN. Я говорил, что JS ведет себя дегенеративно.

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

                                                          ∞ ≠ ∞

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

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

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


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

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

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

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

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

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

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

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

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

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

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

                                        –1
                                        Ну если уж на то пошло, null — тоже объект)
                                        +2
                                        Ну вот, опять скука.
                                        Правила приведения?
                                        На самом деле они абсолютно верны — информация не теряется. Т.е.
                                        Number(Boolean(34)) == 34 // => false
                                        Boolean(Number(true)) == true // => true
                                        

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

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

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

                                          не знаете в каких браузерах может не работать?
                                            +5
                                            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 решение

                                              0
                                              Спасибо за информацию. Хотя конечно никогда не приходилось работать с фреймами.
                                                0
                                                + к Вашему комменту добавлю, что в node.js разные контексты VM приведут к такому же результату, а так же это истинно для любых объектов, а не только массивов
                                                Все что делает оператор instanceof — проверяет, что в цепочке прототипов объекта слева от него присутствует ссылка на прототип конструктора справа
                                                  +1
                                                  Можно добавить в статью — вообще неочевидный нюанс, я вот не знал, хотя при всей своей природной скромности не могу назвать себя начинающим джисером.
                                                0

                                                (комментарий был удален)

                                                  +1
                                                  NaN и Ifninity — это не типы, а специальные значения типа Number. И это не только в ДжС, а во всех типа х всех языков, полностью поддерживающих стандарт представления чисел с плавающей запятой IEEE… не помню.
                                                    0
                                                    Похоже на осеннее обострение аллергии на javascript. Ну, ничего, к зиме пройдёт.
                                                      0

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


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

                                                              Only users with full accounts can post comments. Log in, please.