2016-03-22 1 views
3

Ich habe das schon erwägt, von underscorejs:Wie optimizeCb in Unterstrichen funktioniert?

var optimizeCb = function(func, context, argCount) { 
    if (context === void 0) return func; 
    switch (argCount == null ? 3 : argCount) { 
     case 1: return function(value) { 
     return func.call(context, value); 
     }; 
     case 2: return function(value, other) { 
     return func.call(context, value, other); 
     }; 
     case 3: return function(value, index, collection) { 
     return func.call(context, value, index, collection); 
     }; 
     case 4: return function(accumulator, value, index, collection) { 
     return func.call(context, accumulator, value, index, collection); 
     }; 
    } 
    return function() { 
     return func.apply(context, arguments); 
    }; 
    }; 

Also, anscheinend diese Optimierung gilt nur für Rückruf, der this Wert setzt (hier context genannt). Wie unterscheidet sich das von Call call direkt zum Callback? Wie kann dies die Leistung verbessern?

Es ist in Ordnung, wenn die Optimierung nur für ältere JS-Engines gültig ist. Ich will es nur wissen.

Bearbeiten

Ich war wahrscheinlich unklar in der Frage. Das ist was ich meine. Nehmen wir ein Beispiel nehmen, wo optimizeCb verwendet wird:

_.each = _.forEach = function(obj, iteratee, context) { 
    iteratee = optimizeCb(iteratee, context); //REMOVE this 
    var i, length; 
    if (isArrayLike(obj)) { 
     for (i = 0, length = obj.length; i < length; i++) { 
     iteratee(obj[i], i, obj); 
     //REPLACE with iteratee.call(context, obj[i], i, obj); 
     } 
    } else { 
     var keys = _.keys(obj); 
     for (i = 0, length = keys.length; i < length; i++) { 
     iteratee(obj[keys[i]], keys[i], obj); 
     } 
    } 
    return obj; 
    }; 

die 2 Kommentare Siehe: iteratee = optimizeCb(iteratee, context); //REMOVE this und iteratee(obj[i], i, obj); //REPLACE with iteratee.call(context, obj[i], i, obj);. Ich verstehe Argumente ist langsam und anwenden ist langsam. Aber ich sehe nicht, wie Argumente und Anruf vs gelten hier ins Spiel kommen? Ich sehe keinen Unterschied zwischen zwei Ansätzen.

Ich denke, das Schlüsselproblem ist, dass, wenn ein Callback an eine bestimmte Unterstreichungsmethode übergeben wird, dann ist die Signatur bereits bekannt. Zum Beispiel muss der Rückruf, der an _.each übergeben wurde, function(value, index, collection) haben. Diese Beobachtung wird in der Art bestätigt, wie optimizeCb aufgerufen wird: Wenn der Aufrufer von optimizeCb den Parameter argCount bereitstellen kann (leer lassen bedeutet, dass er 3 ist), weiß er, welche Signatur er ist.

Kann jemand weiter ausarbeiten? Danke vielmals!

+1

"Anruf" ist nur viel schneller https://jsperf.com/call-apply-segu. Ich denke, weil es auf Argumentenarray nicht zugreifen muss –

+0

@Moogs: Der Test, mit dem Sie verbunden sind, vergleicht Äpfel nicht mit Äpfeln; Ich denke, das tut es: http://jsperf.com/call-apply-segu/53 Es gibt jedoch noch einen klaren Vorteil, wenn man 'apply' aufruft. –

Antwort

2

Drei Gründe, warum sie das tun könnte:

  1. Zugriff auf den arguments pseudo-Array waren auf älteren JavaScript Motoren teuer. Wirklich teuer. Wie, ein paar Größenordnungen teuer. :-)

    Ich glaube nicht, dass es auf modernen Engines, vor allem im strikten Modus, keine großen Kosten mehr gibt, wodurch die Live-Verbindung zwischen arguments und formalen Funktionsargumenten entfernt wird.

  2. Als Moogs weist in einem comment ist apply langsamer als call. Basierend auf this test, sieht es so aus, als wäre es irgendwo zwischen halb und doppelt so teuer. Also nicht in der gleichen Liga wie die Order-of-Magnitude-oder zwei von arguments (zurück in den Tag), aber immer noch, schneller.

  3. Wenn contextundefined ist, kehren sie die Funktion unverändert (das ist die anfängliche if (context == void 0) return func;), so gibt es keine .calloder.apply beteiligt überhaupt, wenn es aufgerufen wird.

Was dies tut, ist ein doppelter:

A) Wenn der Rückruf keine bestimmtes this benötigt, verwendet es den Rückruf direkt mit einem einfachen Funktionsaufruf.Wenn der Rückruf eine spezifische this benötigt, erstellen sie eine Funktion, die sie auf die gleiche Weise aufrufen können, die das richtige this verwenden wird, das die Weitergabe des Arguments context spart und den aufrufenden Code vereinfacht.

B) Sie vermeiden arguments Zugriff und apply für gemeinsame Anzahl von Argumenten durch benutzerdefinierte Anpassung der Wrapper-Funktion: Rückrufe nehmen 1-4 Argumente die Kosten, die Rückrufe mit 0 oder mehr als 4 Argumente entstehen, die Kosten zu vermeiden bekommen.

+0

Hallo, bitte sehen Sie meine Bearbeitung – Boyang

+0

@CharlesW .: Hinzugefügt # 3 oben. –

+0

Ah, der Punkt um Kontext ist sehr wahr. Gute Antwort – Boyang