Jak naprawić Array indexOf () w JavaScript dla przeglądarek Internet Explorer

295

Jeśli pracujesz z JavaScriptem na dowolnej długości, wiesz, że Internet Explorer nie implementuje funkcji ECMAScript dla Array.prototype.indexOf () [w tym Internet Explorer 8]. Nie jest to duży problem, ponieważ możesz rozszerzyć funkcjonalność swojej strony o następujący kod.

Array.prototype.indexOf = function(obj, start) {
     for (var i = (start || 0), j = this.length; i < j; i++) {
         if (this[i] === obj) { return i; }
     }
     return -1;
}

Kiedy powinienem to wdrożyć?

Czy powinienem owinąć go na wszystkich moich stronach następującym sprawdzeniem, które sprawdza, czy funkcja prototypu istnieje, a jeśli nie, śmiało i rozszerzać prototyp Array?

if (!Array.prototype.indexOf) {

    // Implement function here

}

A może przeglądarka sprawdza, a jeśli jest to Internet Explorer, to po prostu go wdrażasz?

//Pseudo-code

if (browser == IE Style Browser) {

     // Implement function here

}
Bobby Borszich
źródło
W rzeczywistości Array.prototype.indexOfnie jest częścią ECMA-262 / ECMAScript. Zobacz ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf Może myślisz String.prototype.indexOf...
Crescent Fresh
5
To rozszerzenie, nie będące częścią oryginalnego standardu. Powinien jednak zostać zaimplementowany jako część Javascript 1.6 (czego nie robi IE) developer.mozilla.org/en/New_in_JavaScript_1.6
Josh Stodola
1
@Josh: właśnie odnosił się do „IE nie implementuje funkcji ECMAScript ...”
Crescent Fresh
4
Twoje wdrożenie Array.indexOfnie bierze pod uwagę ujemnych wskaźników początkowych. Zobacz sugestię Mozilli dotyczącą zatrzymania luki tutaj: developer.mozilla.org/en/JavaScript/Reference/Global_Objects/…
nickf
3
Zaktualizowałem pytanie, aby używać „===”, ponieważ obawiam się, że ludzie skopiują je za pomocą „==” i byłoby to złe - poza tym jest w porządku. Zobacz odpowiedź Eli Grey.
joshcomley,

Odpowiedzi:

213

Zrób to tak ...

if (!Array.prototype.indexOf) {

}

Zgodnie z zalecaną kompatybilnością przez MDC .

Ogólnie rzecz biorąc, kod wykrywający w przeglądarce jest dużym nie-nie.

Josh Stodola
źródło
Nie mam wystarczającej liczby przedstawicieli, aby edytować pytanie, ale mogę usunąć żargon ECMAScript i zastąpić go odpowiednim sformułowaniem.
Jeszcze
12
Uważaj, jeśli używasz tego rodzaju wykrywania. Inna biblioteka może zaimplementować tę funkcję przed jej przetestowaniem i może nie być zgodna ze standardami (prototyp zrobił to jakiś czas temu). Gdybym pracował w nieprzyjaznym środowisku (wiele innych programistów korzystających z wielu odrębnych bibliotek), nie ufałbym żadnym z nich ...
Pablo Cabrera,
Kolumna „Połączone” ---> jest naprawdę przydatna! Uwielbiam odpowiedź tutaj: stackoverflow.com/questions/1744310/…
Gordon
Czy musi być zawinięty w każdy plik js?
rd22
Kim dokładnie jest MDC?
Ferrybig
141

Alternatywnie możesz użyć funkcji inArray jQuery 1.2 , która powinna działać w różnych przeglądarkach:

jQuery.inArray( value, array [, fromIndex ] )
Mojżesz Lee
źródło
„IndexOf” jest rodzimym kodem (po prawej), więc czy jQuery „inArray ()” będzie tak szybkie, jak na przykład użycie natywnego, gdy jest dostępne, i wielokrotnego wypełniania, gdy nie?
Jeach
10
Ok, aby odpowiedzieć na mój komentarz (powyżej), właśnie go zaimplementowałem, a w Chrome jest tak szybki, jak wtedy, gdy korzystałem z „indexOf ()”, ale w IE 8 jest on bardzo, bardzo wolny ... więc przynajmniej wiemy że „inArray ()” używa natywnego, gdy może.
Jeach
78

Pełny kod byłby więc następujący:

if (!Array.prototype.indexOf) {
    Array.prototype.indexOf = function(obj, start) {
         for (var i = (start || 0), j = this.length; i < j; i++) {
             if (this[i] === obj) { return i; }
         }
         return -1;
    }
}

Aby uzyskać naprawdę dokładną odpowiedź i kod na to, a także inne funkcje tablicowe, sprawdź pytanie Przepełnienie stosu Naprawianie funkcji tablicy JavaScript w Internet Explorerze (indexOf, forEach itp . ) .

Luis Perez
źródło
2
dziękuję, że masz wszystko. Odwiedzam tę stronę często, gdy potrzebuję wieloplatformowego indeksu w nowym projekcie, a Twój fragment jest jedynym z pełnym kodem. :) Te kilka sekund naprawdę się sumuje, gdy odwiedza się tę stronę.
dylnmc
16

Biblioteka underscore.js ma funkcję indexOf, której można zamiast tego użyć:

_.indexOf([1, 2, 3], 2)
scotta7exander
źródło
4
Ta odpowiedź pozwala uniknąć bałaganu przy użyciu prototypu macierzy i przekazuje go do rodzimego indeksu indexOf, jeśli jest dostępny. Lubię to.
Brad Koch
Wydaje się, że jest to najłatwiejszy sposób, jeśli możesz dołączyć podkreślenie lub lodash
ChrisRich
10

Powinieneś sprawdzić, czy nie jest zdefiniowane za pomocą if (!Array.prototype.indexOf).

Ponadto implementacja indexOfjest niepoprawna. Musisz użyć ===zamiast ==w swoim if (this[i] == obj)oświadczeniu, w przeciwnym razie [4,"5"].indexOf(5)byłoby 1 zgodnie z implementacją, co jest niepoprawne.

Polecam użycie implementacji na MDC .

Eli Gray
źródło
9

Istnieje oficjalne rozwiązanie Mozilli: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf

(function() {
    /**Array*/
    // Production steps of ECMA-262, Edition 5, 15.4.4.14
    // Reference: http://es5.github.io/#x15.4.4.14
    if (!Array.prototype.indexOf) {
        Array.prototype.indexOf = function(searchElement, fromIndex) {
            var k;
            // 1. Let O be the result of calling ToObject passing
            //    the this value as the argument.
            if (null === this || undefined === this) {
                throw new TypeError('"this" is null or not defined');
            }
            var O = Object(this);
            // 2. Let lenValue be the result of calling the Get
            //    internal method of O with the argument "length".
            // 3. Let len be ToUint32(lenValue).
            var len = O.length >>> 0;
            // 4. If len is 0, return -1.
            if (len === 0) {
                return -1;
            }
            // 5. If argument fromIndex was passed let n be
            //    ToInteger(fromIndex); else let n be 0.
            var n = +fromIndex || 0;
            if (Math.abs(n) === Infinity) {
                n = 0;
            }
            // 6. If n >= len, return -1.
            if (n >= len) {
                return -1;
            }
            // 7. If n >= 0, then Let k be n.
            // 8. Else, n<0, Let k be len - abs(n).
            //    If k is less than 0, then let k be 0.
            k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
            // 9. Repeat, while k < len
            while (k < len) {
                // a. Let Pk be ToString(k).
                //   This is implicit for LHS operands of the in operator
                // b. Let kPresent be the result of calling the
                //    HasProperty internal method of O with argument Pk.
                //   This step can be combined with c
                // c. If kPresent is true, then
                //    i.  Let elementK be the result of calling the Get
                //        internal method of O with the argument ToString(k).
                //   ii.  Let same be the result of applying the
                //        Strict Equality Comparison Algorithm to
                //        searchElement and elementK.
                //  iii.  If same is true, return k.
                if (k in O && O[k] === searchElement) {
                    return k;
                }
                k++;
            }
            return -1;
        };
    }
})();
Will V King
źródło
1
Po prostu pedantyczny, ale MDN to nie tylko Mozilla. Jest to projekt kierowany przez społeczność, który zawiera pracowników Mozilli, ale także wolontariuszy, każdy może dołączyć i wnieść swój wkład.
ste2425
5

Poleciłbym to każdemu, kto szuka brakującej funkcjonalności:

http://code.google.com/p/ddr-ecma5/

Wnosi większość brakujących funkcji ecma5 do starszych przeglądarek :)

Josh Mc
źródło
** Chociaż zauważę, że miałem problemy z IE7 z tą biblioteką.
Josh Mc
2

To była moja realizacja. Zasadniczo dodaj to przed innymi skryptami na stronie. tj. w twoim master dla globalnego rozwiązania dla Internet Explorera 8. Dodałem również funkcję przycinania, która wydaje się być używana w wielu ramach.

<!--[if lte IE 8]>
<script>
    if (!Array.prototype.indexOf) {
        Array.prototype.indexOf = function(obj, start) {
            for (var i = (start || 0), j = this.length; i < j; i++) {
                if (this[i] === obj) {
                    return i;
                }
            }
            return -1;
        };
    }

    if(typeof String.prototype.trim !== 'function') {
        String.prototype.trim = function() {
            return this.replace(/^\s+|\s+$/g, '');
        };
    };
</script>
<![endif]-->
Glennweb
źródło
2

mi to pasuje.

if (!Array.prototype.indexOf) {
  Array.prototype.indexOf = function(elt /*, from*/) {
    var len = this.length >>> 0;

    var from = Number(arguments[1]) || 0;
    from = (from < 0)? Math.ceil(from) : Math.floor(from);
    if (from < 0)
    from += len;

    for (; from < len; from++) {
      if (from in this && this[from] === elt)
        return from;
    }
    return -1;
  };
}
Allen Wong
źródło
1

Z Underscore.js

var arr=['a','a1','b'] _.filter(arr, function(a){ return a.indexOf('a') > -1; })

sri_bb
źródło