2013-01-01 11 views
6

Here's an example einer Situation, in der eine einfache JS-Schleife sich nicht wie erwartet verhält, da sich die Schleifenvariable nicht in einem separaten Bereich befindet.Javascript-Funktion für Loops

Die Lösung oft präsentiert ist ein unangenehm aussehend bisschen Schleife Code zu erstellen, die wie folgt aussieht:

for (var i in obj) { 
    (function() { 
     ... obj[i] ... 
     // this new shadowed i here is now no longer getting changed by for loop 
    })(i); 
} 

Meine Frage ist, könnte dies verbessert werden? Konnte ich das verwenden:

Object.prototype.each = function (f) { 
    for (var i in this) { 
     f(i,this[i]); 
    } 
}; 

// leading to this somewhat more straightforward invocation 
obj.each(
    function(i,v) { 
     ... v ... 
     // alternatively, v is identical to 
     ... obj[i] ... 
    } 
); 

wenn ich feststelle, dass ich eine "begrenzte Schleife" brauche? Es ist etwas sauberer und sollte eine ähnliche Performance wie die normale For-Schleife haben (da es die gleiche Art verwendet).

Update: Es scheint, dass Dinge mit Object.prototype zu tun ist ein riesiges No-No, weil es ziemlich viel alles bricht. Hier

ist eine weniger aufdringliche Umsetzung:

function each (obj,f) { 
    for (var i in obj) { 
     f(i,obj[i]); 
    } 
} 

Der Aufruf ändert sich sehr leicht auf

each(obj, 
    function(i,v) { 
     ... v ... 
    } 
); 

Also ich denke, ich habe meine eigene Frage beantwortet, wenn jQuery es auf diese Weise der Fall ist, kann geht wirklich nicht schief. Alle Probleme, die ich übersehen habe, würden jedoch eine Antwort rechtfertigen.

+1

Überprüfen Sie JS-Bibliotheken, wie sie das 'Object.each()' Shim implementieren. Wie für die Leistung und Optimierung, testen Sie Ihren Code mit [JSPerf] (http://jsperf.com) und Sie könnten über [CodeReview] (http://codereview.stackexchange.com/) – Joseph

+0

Ja fragte ich mich 'jQuery.each()' gerade jetzt und es sieht so aus, als ob meine Implementierung einfach ihre Argumentreihenfolge umdreht und weniger (keine, tatsächlich) prüft. –

Antwort

1

Ihre Antwort deckt es ziemlich, aber ich denke, eine Änderung in Ihrer ursprünglichen Schleife ist erwähnenswert, da es sinnvoll ist, eine normale for-Schleife zu verwenden, wenn die each() Funktion aus irgendeinem Grund nicht handlich ist.

Aktualisierung: Geändert, um ein Beispiel zu verwenden, das dem example referenced by the question ähnlich ist, um die verschiedenen Ansätze zu vergleichen. Das Beispiel musste angepasst werden, da die Funktion each() ein ausgefülltes Array benötigt, um darüber zu iterieren.

die folgende Konfiguration angenommen wird:

var vals = ['a', 'b', 'c', 'd'], 
    max = vals.length, 
    closures = [], 
    i; 

das Beispiel von der Frage Verwendung, endet die ursprüngliche Schleife 2n Funktionen zu schaffen bis (wobei n die Anzahl von Iterationen ist), weil zwei Funktionen während jeder Iteration erzeugt werden:

for (i = 0; i < max; i++) { 
    closures[i] = (function(idx, val) { // 1st - factoryFn - captures the values as arguments 
     return function() {    // 2nd - alertFn - uses the arguments instead 
      alert(idx + ' -> ' + val); //     of the variables 
     }; 
    })(i, vals[i]); 
} 

Dies kann nur durch die Schaffung der Funktion Fabrik einmal n + 1 Funktionen zur Schaffung reduziert werden, bevor die Schleife begonnen wird, und dann wiederverwendet:

var factoryFn = function(idx, val) { 
    return function() { 
     alert(idx + ' -> ' + val); 
    }; 
}; 

for (i = 0; i < max; i++) { 
    closures[i] = factoryFn(i, vals[i]); 
} 

Dies ist fast gleichbedeutend mit, wie die each()-Funktion in dieser Situation verwendet werden könnte, die auch zu einer Gesamtzahl von n + 1 Funktionen führen würde. Die Factory-Funktion wird einmalig erstellt und sofort als Argument an each() übergeben.

each(vals, function(idx, val) { 
    closures[idx] = function() { 
     alert(idx + ' -> ' + val); 
    }; 
}); 

FWIW, ich glaube, ein Vorteil each() zur Verwendung der Code ist etwas kürzer und die Schaffung der Fabrik Funktion ist richtig, da es eindeutig in die each() Funktion übergebenen zeigt diese Verwendung seiner nur ist.Ein Vorteil der for Loop-Version, IMO, ist der Code, der die Schleife ist genau dort, so ist es Natur und Verhalten ist völlig transparent, während die each() Funktion in einer anderen Datei, geschrieben von jemand anderem, etc.

+0

Ich denke, das macht die Schleife unnötig asynchron. Es kann zu Unstimmigkeiten führen, wie dies bei Rennen der Fall ist, wenn dies eindeutig vermieden werden kann. –

+0

Wegen der Verwendung von 'setTimeout()'? Ich benutze das als ein Beispiel; Ich habe das Beispiel, mit dem du verlinkt hast, nicht gesehen ... Keine Ahnung, wie ich es geschafft habe, es zu verpassen. Ich habe die Antwort überarbeitet, um etwas mehr zu verwenden, als das, was Sie in der Frage erwähnt haben. Oder bezieht sich Ihr Kommentar auf etwas außer 'setTimeout()'? – tiffon

+0

Ja. TBH Ich glaube nicht, dass ich wirklich nachgesehen habe, warum du es benutzt hast, ich benutze 'setTimeout()', um eine Menge cooler Dinge zu tun, aber es ist auch wahr, dass es eine gute Idee ist, es zu vermeiden, wenn es nicht ist. t notwendig und das war der Grund, warum ich den Kommentar geschrieben habe, aber wahrscheinlich hätte ich Ihre Antwort genauer lesen sollen. –

1
definiert werden kann

Global Scope

Wenn etwas global ist, bedeutet dies, dass es von überall in Ihrem Code zugänglich ist. Nimm das zum Beispiel:

var affe = "Gorilla";

function greetVisitor() { 

return alert("Hello dear blog reader!"); 
} 

Wenn dieser Code in einem Web-Browser ausgeführt wurde, würde die Funktionsumfang Fenster sein, wodurch es

, um alles in diesem Web-Browser-Fenster ausgeführt wird.

lokaler Bereich

In Bezug auf die globale Reichweite gegenüber, ist der lokale Geltungsbereich wenn etwas nur in einem definierten und zugänglich

bestimmten Teil des Codes, wie eine Funktion. Zum Beispiel;

Funktion talkDirty() {

var saying = "Oh, you little VB lover, you"; 
return alert(saying); 
} 

alert(saying); // Throws an error 

Wenn Sie einen Blick auf den Code oben zu nehmen, ist die Variable Spruch nur innerhalb der talkDirty

Funktion. Außerhalb davon ist es überhaupt nicht definiert. Hinweis: Wenn Sie ohne

das vorangehende var-Schlüsselwort deklarieren, wird es automatisch zu einer globalen Variablen.

Was dies bedeutet auch, dass, wenn Sie verschachtelte Funktionen haben, die innere Funktion Zugriff auf die

Funktionen enthält, Variablen und Funktionen haben:

Funktion Savename (firstname) {

Funktion capitalizeName() {

return firstName.toUpperCase(); 

} var aktiviert = capitalizeName();

return capitalized; 

}

alert(saveName("Robert")); // Returns "ROBERT" 

Wie Sie gerade gesehen haben, die innere Funktion capitalizeName keine Parameter in gesendet brauchte, hatte aber kompletten

Zugriff auf die Parameter Vornamen in der äußeren Funktion Savenamen .Aus Gründen der Klarheit, lassen Sie uns einen anderen

Beispiel nehmen:

Funktion Geschwister() {

var siblings = ["John", "Liza", "Peter"]; 

Funktion siblingCount() {

var siblingsLength = siblings.length; 

return siblingsLength; 

}

Funktion joinSiblingNames() {

return "I have " + siblingCount() + " siblings:\n\n" + siblings.join("\n"); 

}

return joinSiblingNames(); 

}

alert(siblings()); // Outputs "I have 3 siblings: John Liza Peter" 

Wie Sie gerade gesehen haben, beide inneren Funktionen Zugriff auf das Geschwister-Array in der enthaltenden Funktion haben, und

jede innere Funktion Zugang haben zu den anderen inneren Funktionen auf der gleichen Ebene (in diesem Fall

joinSiblingNames kann auf Geschwisterzahl zugreifen). Die Variable siblingsLength in der Geschwisterzahl ist jedoch nur innerhalb dieser Funktion, d. H. Dieses Bereichs, verfügbar.