Подтвердить что ты не робот

В JS, что быстрее: оператор "in" или индекс массива?

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

corporatePlan = [
    'canDeAuthorize',
    'hasGmailSupport',
    'canShareReports',
    'canSummonKraken',
    'etc'
]

Итак, когда пользователь пытается вызвать kraken, я сделаю corporatePlan.indexof('canSummonKraken') != -1, чтобы узнать, может ли он.

Сотрудник предполагает, что быстрее хранить его как объект:

"Corporate Plan" = {
    'canDeAuthorize' : null,
    'hasGmailSupport' : null,
    'canShareReports' : null,
    'canSummonKraken' : null,
    'etc' : null
}

И просто сделайте что-нибудь вроде 'canSummonKraken' in corporatePlan, чтобы проверить, содержит ли этот план этот ключ. Это имеет смысл в классическом смысле CS, поскольку, конечно, "содержит" - это постоянное время на карте и линейное по массиву. Означает ли это, что массивы и объекты реализованы под капотом в JS?

В нашем конкретном случае, с менее чем 100 элементами, скорость не имеет большого значения. Но для большего массива, какой путь будет быстрее при доступе?

4b9b3361

Ответ 1

В JavaScript вы обычно имеете дело с широким спектром реализаций (если вы не используете его в контролируемой среде, такой как сервер, на котором вы выбираете движок), и поэтому ответ на конкретные вопросы производительности, как правило, "это зависит, проверьте его на двигателях, которые вы собираетесь использовать". Что быстрее всего на одной реализации может быть медленнее на другой, и т.д. http://jsperf.com удобен для такого рода вещей.

Тем не менее, я ожидаю, что in станет явным победителем здесь. Array#indexOf должен получить доступ к индексам массива в поиске, а индексы массива - это свойства, подобные любому другому свойству. Поэтому, обращаясь к индексу массива 0, чтобы узнать, требует ли требуемая строка для поиска 0, как и для другого, требуется поиск свойства "canSummonKraken" (а затем он должен выполнить сравнение строк после этого). (Да, индексы массива являются свойствами. Массивы в JavaScript вообще не являются массивами. И indexOf может потребоваться доступ к нескольким свойствам во время его поиск, тогда как in будет иметь доступ только к нему. Но, опять же, вам нужно проверить это в своих целевых средах, чтобы убедиться, что некоторые реализации могут оптимизировать массивы, которые имеют смежные диапазоны индексов (но самые медленные из них определенно не делают, и, конечно, если вы беспокоитесь о скорости, вы "беспокоится о том, что самое быстрое на самых медленных двигателях, например, в IE).

Также обратите внимание, что не все механизмы JavaScript даже имеют Array#indexOf. Большинство из них, но есть еще несколько старых, которые пинают (я смотрю на вас, Microsoft), которые этого не делают.

У вас также есть вопрос, следует ли использовать in или hasOwnProperty. Использование in имеет то преимущество, что это оператор, а не вызов функции; использование hasOwnProperty имеет то преимущество, что оно будет рассматривать только конкретный экземпляр объекта, а не его прототип (и его прототип и т.д.). Если у вас не очень унаследованная иерархия (а вы не в своем примере), я ставлю на победу in, но полезно помнить, что она проверяет иерархию.

Кроме того, помните, что "canSummonKraken" in obj будет истинным в примере объекта, который вы показали, потому что объект имеет свойство, даже если значение свойства равно null. У вас не должно быть свойство для возврата false. (Вместо in вы можете просто использовать true и false и искать его как obj.canSummonKraken.)

Итак, ваши варианты:

  • Ваш метод массива:

    corporatePlan = [
        'canDeAuthorize',
        'hasGmailSupport',
        'canShareReports',
        'canSummonKraken',
        'etc'
    ];
    
    console.log(corporatePlan.indexOf("canSummonKraken") >= 0);  // true
    console.log(corporatePlan.indexOf("canDismissKraken") >= 0); // false
    

    ... который я бы не рекомендовал.

  • Метод in:

    corporatePlan = {
        'canDeAuthorize'  : null,
        'hasGmailSupport' : null,
        'canShareReports' : null,
        'canSummonKraken' : null,
        'etc'             : null
    };
    
    console.log("canSummonKraken" in corporatePlan);  // true
    console.log("canDismissKraken" in corporatePlan); // false
    

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

    Здесь я должен отметить две вещи:

    • in также проверяет прототип объекта, поэтому, если у вас есть такие настройки, как toString или valueOf, вы получите ложные срабатывания (так как это свойства, которые почти все объекты получают от Object.prototype). В браузере с поддержкой ES5 вы можете избежать этой проблемы, создав свой объект прототипом null: var corporatePlan = Object.create(null);

    • Возможно, потому, что он проверяет прототипы, оператор in на некоторых двигателях неожиданно медленный.

    Обе эти проблемы могут быть решены с помощью hasOwnProperty вместо:

    console.log(corporatePlan.hasOwnProperty("canSummonKraken"));  // true
    console.log(corporatePlan.hasOwnProperty("canDismissKraken")); // false
    

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

  • Метод flags:

    corporatePlan = {
        'canDeAuthorize'   : true,
        'hasGmailSupport'  : true,
        'canShareReports'  : true,
        'canSummonKraken'  : true,
        'canDismissKraken' : false,
        'etc'              : true
    };
    
    console.log(corporatePlan.canSummonKraken);  // "true"
    console.log(corporatePlan.canDismissKraken); // "false"
    
    // or using bracketed notation, in case you need to test this
    // dynamically
    console.log(corporatePlan["canSummonKraken"]);  // "true"
    console.log(corporatePlan["canDismissKraken"]); // "false"
    
    // example dynamic check:
    var item;
    item = "canSummonKraken";
    console.log(corporatePlan[item]);  // "true"
    item = "canDismissKraken";
    console.log(corporatePlan[item]);  // "false"
    

    ..., что было бы довольно обычным способом, вероятно, быстрее, чем in, и, вероятно, по крайней мере так же быстро, как hasOwnProperty. (Но см. Мой вступительный абзац: Тест в вашей среде.:-))

Ответ 2

Я протестировал его: http://jsperf.com/array-indexof-vs-object-s-in-operator/4

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

Но тогда я использовал вариант с оператором typeof, который намного быстрее, чем оба:

if (typeof obj['that'] !== "undefined") {
  // found
}

Ответ 3

Вот эталон http://jsperf.com/array-indexof-vs-object-keys. В хроме и в firefox проверка наличия ключа в объекте на 100% быстрее, чем сканирование массива.

results

Но если вы учитываете время инициализации, разница отменяется, объекты занимают больше времени для инициализации, чем массивы.

enter image description here