Użyłem JSLint na moim pliku JavaScript. Zgłoszył błąd:
for( ind in evtListeners ) {
Problem w linii 41 znak 9: Ciało for in powinno być owinięte w instrukcję if, aby odfiltrować niepożądane właściwości z prototypu.
Co to znaczy?
Użyłem JSLint na moim pliku JavaScript. Zgłoszył błąd:
for( ind in evtListeners ) {
Problem w linii 41 znak 9: Ciało for in powinno być owinięte w instrukcję if, aby odfiltrować niepożądane właściwości z prototypu.
Co to znaczy?
Odpowiedzi:
Przede wszystkim nigdy nie używaj for in
pętli do wyliczania tablicy. Nigdy. Użyj starego dobrego for(var i = 0; i<arr.length; i++)
.
Przyczyna tego jest następująca: każdy obiekt w JavaScript ma specjalne pole o nazwie prototype
. Wszystko, co dodasz do tego pola, będzie dostępne dla każdego obiektu tego typu. Załóżmy, że chcesz, aby wszystkie tablice miały nową, fajną funkcję filter_0
, która odfiltruje zera.
Array.prototype.filter_0 = function() {
var res = [];
for (var i = 0; i < this.length; i++) {
if (this[i] != 0) {
res.push(this[i]);
}
}
return res;
};
console.log([0, 5, 0, 3, 0, 1, 0].filter_0());
//prints [5,3,1]
Jest to standardowy sposób rozszerzania obiektów i dodawania nowych metod. Robi to wiele bibliotek. Zobaczmy jednak, jak for in
teraz działa:
var listeners = ["a", "b", "c"];
for (o in listeners) {
console.log(o);
}
//prints:
// 0
// 1
// 2
// filter_0
Czy ty widzisz? Nagle myśli, że filter_0 to kolejny indeks tablicy. Oczywiście nie jest to tak naprawdę indeks liczbowy, ale for in
wylicza pola obiektowe, a nie tylko indeksy numeryczne. Wyliczamy teraz za pomocą każdego indeksu liczbowego i filter_0
. Ale filter_0
nie jest polem żadnego konkretnego obiektu tablicy, każdy obiekt tablicy ma teraz tę właściwość.
Na szczęście wszystkie obiekty mają hasOwnProperty
metodę, która sprawdza, czy to pole naprawdę należy do samego obiektu, czy też jest po prostu dziedziczone z łańcucha prototypów, a zatem należy do wszystkich obiektów tego typu.
for (o in listeners) {
if (listeners.hasOwnProperty(o)) {
console.log(o);
}
}
//prints:
// 0
// 1
// 2
Zauważ, że chociaż ten kod działa zgodnie z oczekiwaniami dla tablic, nigdy nie powinieneś, nigdy nie używaj for in
i for each in
dla tablic. Pamiętaj, że for in
wylicza pola obiektu, a nie indeksy tablic lub wartości.
var listeners = ["a", "b", "c"];
listeners.happy = "Happy debugging";
for (o in listeners) {
if (listeners.hasOwnProperty(o)) {
console.log(o);
}
}
//prints:
// 0
// 1
// 2
// happy
for in
do iteracji po tablicach, ponieważ język nie określa kolejności, w jakiej for in
będzie wyliczany w tablicy. Może nie być w kolejności numerycznej. Ponadto, jeśli użyjesz konstrukcji typu `for (i = 0; i <array.length; i ++), możesz być pewien, że iterujesz tylko indeksy liczbowe w kolejności i nie masz właściwości alfanumerycznych.
for-in
pętli (które są świetne, nawiasem mówiąc), powinniśmy edukować ich, jak działają (właściwie zrobione w tej odpowiedzi) i wprowadzić ich, aby Object.defineProperty()
mogli bezpiecznie rozszerzać prototypy, nie niszcząc niczego. Nawiasem mówiąc, rozszerzenie prototypów obiektów rodzimych nie powinno odbywać się bez Object.defineProperty
.
Douglas Crockford, autor jslint wiele razy pisał (i mówił) na ten temat. Na tej stronie jego witryny znajduje się sekcja, która obejmuje:
dla oświadczenia
Klasa A instrukcji powinna mieć następującą formę:
for (initialization; condition; update) { statements } for (variable in object) { if (filter) { statements } }
Pierwszej formy należy używać z tablicami i pętlami o określonej liczbie iteracji.
Druga forma powinna być używana z obiektami. Należy pamiętać, że elementy dodane do prototypu obiektu zostaną uwzględnione w wyliczeniu. Mądrze jest programować defensywnie za pomocą metody hasOwnProperty, aby rozróżnić prawdziwe elementy obiektu:
for (variable in object) { if (object.hasOwnProperty(variable)) { statements } }
Crockford ma także serię wideo na temat teatru YUI, w której mówi o tym. Seria filmów / rozmów Crockforda na temat javascript jest obowiązkowa, jeśli poważnie myślisz o javascript.
Odpowiedź Vavy jest na znaku. Jeśli używasz jQuery, $.each()
funkcja się tym zajmuje, dlatego bezpieczniej jest go używać.
$.each(evtListeners, function(index, elem) {
// your code
});
$.each
(lub underscore.js _.each
), jeśli można uniknąć for
pętli raw . jsperf ma kilka testów porównawczych, które warto uruchomić.
@all - wszystko w JavaScript jest obiektem (), więc stwierdzenia takie jak „używaj tego tylko na obiektach” są nieco mylące. Ponadto JavaScript nie jest mocno wpisany, więc 1 == „1” jest prawdziwe (chociaż 1 === „1” nie jest, Crockford jest na tym duży). Jeśli chodzi o progromatyczną koncepcję tablic w JS, pisanie jest ważne w definicji.
@Brenton - Nie trzeba być dyktatorem terminologii; „tablica asocjacyjna”, „słownik”, „skrót”, „obiekt”, wszystkie te koncepcje programowania dotyczą jednej struktury w JS. Jest to para nazwa-klucz (indeks, indeks), gdzie wartością może być dowolny inny obiekt (ciągi też są obiektami)
Więc
new Array()
jest taki sam jak[]
new Object()
jest z grubsza podobny do {}
var myarray = [];
Tworzy strukturę, która jest tablicą z takim ograniczeniem, że wszystkie indeksy (inaczej klucze) muszą być liczbą całkowitą. Pozwala także na automatyczne przypisywanie nowych indeksów przez .push ()
var myarray = ["one","two","three"];
Naprawdę najlepiej jest rozwiązać za pośrednictwem for(initialization;condition;update){
Ale co z:
var myarray = [];
myarray[100] = "foo";
myarray.push("bar");
Spróbuj tego:
var myarray = [], i;
myarray[100] = "foo";
myarray.push("bar");
myarray[150] = "baz";
myarray.push("qux");
alert(myarray.length);
for(i in myarray){
if(myarray.hasOwnProperty(i)){
alert(i+" : "+myarray[i]);
}
}
Być może nie najlepsze użycie tablicy, ale tylko ilustracja, że rzeczy nie zawsze są jednoznaczne.
Jeśli znasz swoje klucze, a na pewno nie są to liczby całkowite, jedyną opcją struktury podobnej do tablicy jest obiekt.
var i, myarray= {
"first":"john",
"last":"doe",
100:"foo",
150:"baz"
};
for(i in myarray){
if(myarray.hasOwnProperty(i)){
alert(i+" : "+myarray[i]);
}
}
Z pewnością jest to trochę ekstremalne
... nigdy nie używaj pętli for in, aby wyliczyć tablicę. Nigdy. Użyj starej dobrej dla (var i = 0; i <arr.length; i ++)
?
Warto podkreślić sekcję w wyciągu Douglasa Crockforda
... Druga forma powinna być używana z obiektami ...
Jeśli potrzebujesz tablicy asocjacyjnej (aka hashtable / dictionary), w której klucze są nazywane zamiast indeksowanych numerycznie, będziesz musiał zaimplementować to jako obiekt, np var myAssocArray = {key1: "value1", key2: "value2"...};
.
W takim przypadku myAssocArray.length
pojawi się zero (ponieważ ten obiekt nie ma właściwości „length”), a i < myAssocArray.length
nie zaprowadzisz cię daleko. Oprócz zapewnienia większej wygody spodziewałbym się, że tablice asocjacyjne będą oferować korzyści wydajnościowe w wielu sytuacjach, ponieważ klucze tablicy mogą być przydatnymi właściwościami (tj. Właściwością lub nazwą identyfikatora członka tablicy), co oznacza, że nie musisz iterować przez długie tablica wielokrotnie oceniająca, czy instrukcje, aby znaleźć pozycję tablicy, której szukasz.
W każdym razie, dzięki również za wyjaśnienie komunikatów o błędach JSLint, użyję teraz sprawdzania „isOwnProperty” podczas interakcji z moimi niezliczonymi tablicami asocjacyjnymi!
length
właściwości, ale można to zrobić w inny sposób:var myArr = []; myArr['key1'] = 'hello'; myArr['key2'] = 'world';
var myArr = []
, powinno być var myArr = {}
w PHP, są one tym samym, ale nie w JS.
Oznacza to, że należy filtrować właściwości evtListeners za pomocą metody hasOwnProperty .
Aby dodać do tematu for in / for / $. Każdy, dodałem przypadek testowy jsperf do używania $ .each vs for w: http://jsperf.com/each-vs-for-in/2
Różne przeglądarki / wersje radzą sobie z tym inaczej, ale wydaje się, że każdy z nich jest najtańszym rozwiązaniem pod względem wydajności.
Jeśli używasz in w celu iteracji przez asocjacyjną tablicę / obiekt, wiedząc, czego szukasz i ignorując wszystko inne, użyj $ .each, jeśli używasz jQuery, lub tylko dla in (a potem przerwa; po osiągnął, o czym wiesz, że powinien być ostatnim elementem)
Jeśli iterujesz po tablicy, aby wykonać coś z każdą parą kluczy, powinieneś użyć metody hasOwnProperty, jeśli NIE korzystasz z jQuery i użyj $ .each, jeśli używasz jQuery.
Zawsze używaj, for(i=0;i<o.length;i++)
jeśli nie potrzebujesz tablicy asocjacyjnej, chociaż ... lol chrome wykonał to 97% szybciej niż w przypadku w lub$.each
if (evtListeners.hasOwnProperty(ind))
aby ograniczyć przetwarzanie tylko do własnych (nie odziedziczonych) właściwości. Jednak w niektórych przypadkach naprawdę chcesz iterować wszystkie właściwości, w tym te odziedziczone. W takim przypadku JSLint zmusza cię do zawinięcia treści pętli w instrukcji if, aby zdecydować, które właściwości naprawdę chcesz. To zadziała i sprawi, że JSlint będzie szczęśliwy:if (evtListeners[ind] !== undefined)