В JS, что быстрее: оператор "in" объекта или indexof массива? - PullRequest
35 голосов
/ 12 октября 2011

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

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

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

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

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

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

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

Ответы [ 3 ]

27 голосов
/ 12 октября 2011

В 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 будет истинным в показанном вами литерале объекта примера, потому что у объекта действительно есть свойство, даже если значение свойства равно нулю. Вы не должны иметь свойство вообще , чтобы in мог вернуть false. (Вместо in вы можете просто использовать true и false и искать его как obj.canSummonKraken.)

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

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

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

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

  2. Метод 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, но я бы это проверил. Полезно, если список может быть очень длинным, и если у вас будет много этих объектов, потому что для этого требуется только то, что «истинные» свойства существуют вообще. Пустой объект представляет собой план, в котором пользователь ничего не может сделать, и довольно мал.

    Я должен отметить две вещи здесь:

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

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

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

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

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

  3. Метод флагов:

    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. (Но см. Мой вступительный абзац: Test in your environment. :-))

15 голосов
/ 06 января 2013

Я проверял это: http://jsperf.com/array-indexof-vs-object-s-in-operator/4

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

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

if (typeof obj['that'] !== "undefined") {
  // found
}
11 голосов
/ 02 октября 2013

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

results

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

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...