Das Wesen des Codes unterstreicht das Konzept von Sequenz und Reihenfolge, insbesondere in JavaScript – schließlich ist JavaScript eine Single-Threaded-Engine.
Javascript hat die Eigenschaften einer funktionalen Programmierung und aufgrund der JavaScript-Single-Threaded-Engine müssen unsere Funktionen immer in geordneter Weise ausgeführt werden. Hervorragender Code schneidet Funktionen häufig in ihre eigenen Module ein und führt sie dann unter bestimmten Bedingungen aus. Da diese Funktionen auf geordnete Weise ausgeführt werden, schreiben wir doch ein einheitliches Verwaltungsobjekt, das uns bei der Verwaltung dieser Funktionen hilft – also Callbacks (Rückruffunktionen). ) wurden geboren.
Was sind Rückrufe?
Javascript ist voll von funktionaler Programmierung. Das Traurigste ist, dass window.onload nur eine Funktion empfangen kann, wenn es mehrere Funktionen gibt, die Sie möchten in Onload ausführen, dann müssen wir den folgenden Code schreiben:
Die ursprüngliche Absicht der Rückruffunktion besteht darin, auf so etwas aufzubauen. Sie ermöglicht uns nicht mehr, diese Funktionen zu verstreuen, sondern diese Funktionen auf einheitliche Weise zu organisieren. Wie Sie sehen, möchten wir für ein Element in window.onload zwei Dinge tun: Zuerst die HTML-Struktur ändern und dann den Stil des HTML ändern. Die beiden Funktionen wirken auch auf ein Element, und die endgültige Ausführung dieser beiden Funktionen erfolgt der Reihe nach. Warum schreiben wir also nicht ein Objekt wie dieses, um diese Funktionen zu verwalten? Dies ist natürlich nur die grundlegendste Bedeutung der Callback-Funktion. Wir brauchen mehr als nur ein solches einfaches Callback-Funktionsobjekt, wir brauchen eine leistungsfähigere Callback-Funktion. Nun, das ist nur ein einfacher Anwendungsfall, also kann ich Ihnen sagen, was diese Rückruffunktion tun kann, außer Funktionen einzeln auszuführen.
Der Kern von Callbacks besteht darin, die ordnungsgemäße Ausführung von Funktionen zu steuern. Javascript ist eine Single-Threaded-Engine, was bedeutet, dass nur ein Code in JavaScript gleichzeitig ausgeführt wird – sogar Ajax und setTimeout. Diese beiden Funktionen scheinen asynchron zu sein, aber das ist nicht der Fall. Wenn der Browser Javascript-Code ausführt, werden diese Codes in geordneter Weise in eine Warteschlange verschoben Der Browser entnimmt den Code nacheinander aus der Codewarteschlange, wenn er den JavaScript-Code verarbeitet – Rückrufe, die dieser Single-Thread-Engine gerecht werden.
Natürlich wollen wir mehr als nur ein so einfaches Toolobjekt – im jQuery-Quellcode stellen Callbacks die grundlegende Verwaltung einer Reihe von Funktionen bereit, bilden die Grundlage für Deferred (asynchrone Warteschlange) und dienen auch der Warteschlange (Synchronisierungswarteschlange). Deferred wird verwendet, um die Pyramidenprogrammierung zu glätten/abzuflachen (eine große Anzahl verschachtelter Rückruffunktionen, z. B. Code in Ajax, der basierend auf dem Anforderungsrückgabecode ausgeführt werden muss); und Queue steuert jQuery.animate (Animations-Engine).
Dann schreiben wir einen Rückruf.
Rückrufmodell
Array:
Da unsere Callbacks eine Reihe von Funktionen akzeptieren möchten, müssen wir einen Container haben. Wir können ein Array verwenden und jede Funktion in das Array verschieben. Wenn sie ausgeführt werden muss, führen Sie eine Schleife durch die auszuführenden Array-Elemente durch.
Arbeitsmodell:
Diese Rückrufe müssen sehr leistungsfähig sein. Es ist nicht nur so einfach, eine Funktion zu pushen und dann auszuführen. Diese Rückrufe sollten ein gutes Ausführungsmodell haben.
einmal: Alle Funktionen im aktuellen Callbacks-Objekt werden nur einmal ausgeführt und nach der Ausführung freigegeben. Wir können Benutzern, die das Callbacks-Objekt verwenden, eine stabile und effektive Lösung bieten, um sicherzustellen, dass die Funktion nur einmal ausgeführt wird erneut ausgeführt werden, was die Threads dieser Funktionen stabilisiert.
auto: Dies ist ein interessantes Modell. Beispielsweise hängt die Ausführung der Funktion b von der Funktion a ab: Nach der ersten Ausführung dieses Rückrufs. Jedes Mal, wenn die Funktion die Rückrufe erreicht, führt sie automatisch die in der Vergangenheit hinzugefügten Funktionen aus und übergibt die letzten Parameterdaten an die früheren Funktionen. Dadurch entfällt die Notwendigkeit einer wiederholten Auslösung zwischen diesen abhängigen Funktionen ein interessantes Modell.
Once&Auto: Wir können es leistungsfähiger machen und gleichzeitig mit den Once- und Auto-Modellen arbeiten, das heißt: Jedes Mal, wenn eine Funktion zu Callbacks hinzugefügt wird, werden die früheren Funktionen ausgeführt und dann werden diese früheren Funktionen freigegeben. und Funktionen werden beim nächsten Mal weiterhin hinzugefügt. Zu diesem Zeitpunkt werden diese Funktionen in der Vergangenheit nicht mehr ausgeführt, da das einmalige Modell sie freigegeben hat.
API:
add(function) – Fügen Sie eine (oder mehrere) Funktionen zum Callbacks-Objekt hinzu: Wenn Sie keine Funktionen hinzufügen und einfach nur neugierig sind, einen Blick auf Callbacks zu werfen, lassen wir Sie natürlich weiterhin Ihren Spaß genießen - Wir werden keine Ausnahme auslösen, weil wir darin nicht gut sind.
remove(function) – entfernt eine Funktion in einem Callbacks: Nachdem wir sie hinzugefügt haben, sollten wir auch einen Plan bereitstellen, um sie zu bereuen. Wir sind so zugänglich und tolerieren alles, was andere in der Vergangenheit getan haben.
has(function) – Bestimmt, ob Callbacks eine Funktion enthalten: Oh? Sie sind sich nicht sicher, ob Sie diese Funktion einbinden sollen, aber Sie haben sie von Anfang an eingebaut! Warum bist du so nachlässig? Aber da Sie mich gefragt haben, werde ich Ihnen trotzdem sagen, ob Callbacks diese Funktion enthält. Ich weiß, dass Sie sehr beschäftigt sind und sich nicht an alles erinnern und es bestimmen können.
empty() – Leere Rückrufe: Haben diese Funktionen für Sie ihre Bedeutung verloren? Was? Sie möchten es nach der Ausführung nicht mehr? Du wünschst dir also, du könntest es klären? Nun, der Erinnerung halber toleriere ich Ihre Forderung immer noch.
disable() – Callbacks deaktivieren: Um eine stabile Existenz mit dem Code anderer Leute aufrechtzuerhalten, habe ich mich für Selbstaufopferung entschieden – ja, diese Methode kann Callbacks deaktivieren, sie vollständig deaktivieren, als ob sie vorher nicht existiert hätte.
disabled() – Stellt fest, ob die Callbacks deaktiviert wurden: Wenn Sie immer noch nicht glauben, ob die Callbacks wirklich aufopferungsvoll sind, kann Ihnen diese Methode Sicherheit geben.
lock(boolean) – Sperren Sie dieses Callbacks-Objekt: Sie befürchten, dass es instabil ist, aber Sie möchten es nicht aufgeben. Es ist eine gute Methode, um anzugeben, ob das Objekt gesperrt werden muss Natürlich gibt es keine Parameter. Damit können Sie bestimmen, ob Rückrufe gesperrt sind.
fire(data) – Führen Sie die Funktion in diesen Rückrufen aus: Ist nicht alles, was wir tun, in diesem Moment für das Schicksal der Ausführung bestimmt? Die Parameter werden zu Parametern dieser Funktionen, die ausgeführt werden müssen.
fireWith(context,data) – Führen Sie die Funktion in Callbacks aus und geben Sie den Kontext an. In fire() ist der Kontext aller Funktionen Callbacks-Objekte, und fireWidth() ermöglicht es Ihnen, den Kontext dieser auszuführenden Funktionen neu zu definieren. Wie frei die Programmierung ist, berücksichtigt Callbacks alles für Sie.
fired() – Stellen Sie fest, ob dieser Callbacks in der Vergangenheit ausgeführt wurde: Wir glauben, dass Sie die meiste Zeit nicht wissen, was Sie in der Vergangenheit getan haben, aber wir zeichnen alles auf, was Sie tun, wenn Sie dieses Callbacks-Objekt ausgeführt haben die Vergangenheit, dann können Sie es nie leugnen, denn wir wissen, ob Sie diese Rückrufe in der Vergangenheit ausgeführt haben.
Grundlegende Modulimplementierung
Einfache Implementierung:
Lassen Sie uns zunächst einfach einen Rückruf implementieren:
Öffnen Sie die Browserkonsole und wir können sehen, dass die Laufergebnisse normal sind.
einmalige und automatische (Speicher-)Implementierung
einmal:
Once lässt die Funktion in diesem Rückruf einmal ausführen und dann nicht erneut ausführen. Das Prinzip ist sehr einfach. Im obigen Code können wir sehen, dass es eine Variablenliste gibt, die die Funktionsliste übernimmt, sodass wir nur die Codes löschen müssen, die in der Vergangenheit ausgeführt wurden. Wir verwenden eine globale Variable, um das aktuelle Ausführungsmodell zu speichern. Wenn es sich um ein einmaliges Modell handelt, machen Sie die Liste einfach in fireWith() ungültig:
automatisch:
Das Auto-(Speicher-)Modell ist nach dem Speicher in jQuery benannt. Nachdem ich mir die Verwendung genau angesehen hatte, entschied ich mich, es in „Auto“ zu ändern – seine Funktion ist „nach dem ersten Feuer()“. Die nachfolgende Funktion „add() wird automatisch ausgeführt“ kann in den folgenden Situationen verwendet werden: Nachdem Sie eine Reihe von Funktionen zu Callbacks hinzugefügt haben und vorübergehend eine Funktion hinzufügen müssen, führen Sie die neu hinzugefügte Funktion sofort aus Benutzerfreundlichkeit. Dieses Muster ist etwas schwer zu verstehen. Die Implementierung besteht darin, während add() festzustellen, ob es sich um ein automatisches Modell handelt. Wenn es sich um ein automatisches Modell handelt, führen Sie diese Funktion aus. Allerdings müssen wir es nach dem ersten fire() automatisch ausführen. Rückrufe, die nicht fire() wurden, sollten nicht automatisch ausgeführt werden. Außerdem müssen nach jeder automatischen Ausführung die zuletzt verwendeten Parameter an diese automatisch ausgeführten Funktionen übergeben werden.
Vielleicht fällt Ihnen der folgende Code ein:
Aber in jQuery wird auch eine wunderbarere Verwendung übernommen. Der Autor von jQuery ist auch stolz auf diese Verwendung, daher hat er dieses Modell „Speicher“ genannt – das heißt, die obige Variable stellt nicht nur den aktuellen automatischen Ausführungsmodus dar, sondern dient auch als letzter Parametercontainer, der sowohl Auto als auch Speicher darstellt. (Der folgende Code ist kein jQuery und basiert auf Ideen für jQuery-Code, nicht auf Quellcode):
Während add() hat jQuery der Variablen auto(memory) keinen Wert zugewiesen, sondern sich dafür entschieden, auto(memory) in coreFire() einen Wert zuzuweisen, um sicherzustellen, dass es erst beim ersten Mal aktiviert wird fire(). Automatisch ausgeführt.
Wie oben erwähnt, sind die von coreFire() empfangenen Parameter tatsächlich ein Array. Der erste Parameter ist der Kontext und der zweite Parameter ist der von außen übergebene Parameter. Weisen Sie dieses Array gleichzeitig auto (Speicher) zu, sodass die Definition der Variablen auto (ob der Modus automatisch ausgeführt werden soll) zum Speicher (Speicher des zuletzt übergebenen Parameters) wird.
Was Once&Auto betrifft, werden nur diese beiden Codes kombiniert. Sie müssen in coreFire() nur festlegen, dass die Liste im automatischen Modus auf ein neues Array zurückgesetzt wird, andernfalls wird sie direkt auf undefiniert gesetzt.
Dieser Code wurde von mir handgeschrieben und entspricht einigen öffentlichen jQuery-Funktionen. Es handelt sich nicht um ein Codefragment, daher kann direkt darauf verwiesen und ausgeführt werden.
//Werkzeugfunktion
var isIndexOf = Array.prototype.indexOf, //Es6
toString = Object.prototype.toString, //Cache toString-Methode
TosLice = Array.prototype.slice, // Caches Slice-Methode
return "object" === typeof document.getElementById ?
isFunction = function (fn) {
//Es gibt ein Problem mit der Erkennung von DOM und BOM unter ie
versuchen Sie es mit {
Geben Sie /^s*bfunctionb/.test("" fn);
zurück
} Catch (x) {
Gibt false zurück
}
} :
isFunction = function (fn) { return toString.call(fn) === '[object Function]' };
})(),
//Der erste Parameter stellt das Array dar, das in einer Schleife ausgeführt werden soll, und der zweite Parameter ist die Funktion, die jedes Mal durch die Schleife
ausgeführt wird
If (arguments.length < 2 || !isFunction(arguments[1])) return;
//Warum ist Slice ungültig? ?
var list = toSlice.call(arguments[0]),
fn = arguments[1],
Artikel;
while ((item = list.shift())) {//Keine direkte Bestimmung der Länge, beschleunigen
// Warum hier „Call“ verwenden und „Apply“ nicht in Ordnung ist?
// Fertig – der zweite Parameter von apply muss ein Array-Objekt sein (es gibt keine Überprüfung, ob Array-like möglich ist, und für den Aufruf gilt diese Anforderung nicht)
//apply wird wie folgt beschrieben: Wenn argArray (der zweite Parameter) kein gültiges Array oder kein Argumentobjekt ist, wird ein TypeError verursacht.
fn.call(window, item);
}
},
inArray = function () { //Erkennen Sie, ob das Array ein Element enthält, und geben Sie den Index des Elements zurück
// Vorkompilierung
return isIndexOf ? function (array, elem, i) {
Wenn (Array)
return isIndexOf.call(array, elem, i);
return -1;
} : Funktion (elem, array, i) {
var len;
if (array) {
len = array.length;
ich = ich ? ich < 0 ? Math.max(0, len i) : i : 0;
für (; i < len; i ) {
if (i in array && array[i] === elem) {
zurück i;
}
}
}
return -1;
}
}();
var Callbacks = Funktion (Option) {
option = toString.call(option) === '[object Object]' ? option: {};
//Abschlüsse verwenden, da jeder neue Rückruf seinen eigenen Status hat
var list = [], var list = [], var list =
_list = [], // Wenn dieses Rückrufobjekt gesperrt ist, löschen Sie die Liste und fügen Sie die ursprüngliche Liste in _list
ein
wurden hingerichtet
fireStart, //Funktionsindex (Startpunkt), der von der aktuellen Callback-Funktionsliste
ausgeführt wird
FiringLength, // Array-Länge der Callback-Funktion
.
// Die Verwendung dieser Variablen ist sehr seltsam und scharfsinnig. Sie enthält nicht nur das Flag, ob die Ausführung angegeben werden soll, sondern zeichnet auch die Daten auf
//Dieses Auto ist einfach verrückt, wenn es mit Once verwendet wird: [Zum ersten Mal] wird es nach der Ausführung von Fire automatisch ausgeführt. Mit Once ist dies möglich: Nach der Ausführung wird kein Code angehängt oder später ausgeführt, um die Stabilität zu gewährleisten und Stabilität eines Satzes von Rückrufdaten
stack = !option.once && [], //Ein Callback-Array wird gerade ausgeführt und während der Ausführung wird eine neue Callback-Funktion hinzugefügt >
Firing = false, //Ob Rückrufe funktionieren/ausgeführt werden
//Rückruffunktion auslösen
Feuer = Funktion (Daten) {
//Beachten Sie, dass es sich bei diesen Daten um ein Array handelt. Wenn der Auto-Modus konfiguriert ist, ist auto niemals falsch, da auto ein Array ist
auto = option.auto && data //Wenn die Konfiguration das Speichern des letzten Parameters erfordert, dann merken Sie sich diesen Parameter (sehr scharfe Verwendung, rufen Sie die Daten direkt ab)
gefeuert = wahr;
FiringIndex = FiringStart ||. 0;
FiringStart = 0;//FiringStart löschen (wenn Sie es nicht löschen, wird es bei der nächsten Ausführung Probleme geben)
Firinglength = list.length; // Länge der Cache-Liste, auf die die Außenwelt zugreifen kann
Firing = true; // Rückruffunktion wird ausgeführt
für (; FiringIndex < FiringLength; FiringIndex) {
If (list[firingIndex].apply(data[0], data[1]) === false) {
// Hinweis: Wenn option.auto (automatische Ausführung) konfiguriert ist und sich eine Funktion im Stapel (Stapel) befindet, gibt es im add()-Code einen Codeabschnitt, der diese Methode zur automatischen Beurteilung direkt ausführt
//Wir wollen diesen Code blockieren, also setze auto auf false
auto = false;
Pause;
}//Wenn die Funktion false zurückgibt, beenden Sie die Ausführung der nachfolgenden Warteschlange
}
Firing = false; // Flag-Status wurde ausgeführt. Callback-Funktion [die Funktion im Stapel (Stack) wurde noch nicht ausgeführt]
//Wenn dieser Stapel nicht einmal konfiguriert wird, muss er [] sein, also muss es
geben
// Die Hauptfunktion besteht darin, dass der folgende Code abgefangen wird, wenn Once nicht konfiguriert ist. Wenn Once konfiguriert ist, werden die Daten nach der Ausführung des Codes gelöscht
if (stack) {
If (stack.length) // Fangen Sie zuerst den Code des folgenden Auflistungsstatus ab und bestimmen Sie dann, ob ein Stapel
vorhanden ist
Fire (stack.shift()); // Nimm es aus dem Kopf des Stapels und rekurriere die FIRE()-Methode
}
Sonst, wenn (auto) // Code hierher kam, beweist dies, dass er Option.once konfiguriert wurde (nur einmal ausgeführt), sodass die Liste klar ist
list = [];
Else // beweist, dass es keine AUTO-Konfiguration gibt, sondern ONCE konfiguriert ist, sodass das Opfer das ultimative Dafa ist und das Rückrufobjekt direkt abgeschafft wird
self.disable();
};
var self = {
add: function () {//Eine Rückruffunktion hinzufügen
if (Liste) {
var start = list.length;
(Funktion addCallback(args) {
every(args, function (item) {
If (isFunction(item)) {//Wenn es sich um eine Funktion handelt, drücken Sie die Rückrufliste
//Beachten Sie, dass typeof und Object.prototype.toString unterschiedlich sind
} else if (toString.call(item) === '[object Array]') {//Wenn es sich um ein Array handelt, wird es rekursiv in die Rückrufliste verschoben. Dieses Urteil wird arrayartig aufgegeben
addCallback(item);
}
});
})(Argumente);
}
Wenn (auslösend)//Wenn gerade eine Rückruffunktion ausgeführt wird, muss die Länge der aktuellen Rückruffunktionsliste aktualisiert werden, andernfalls wird die neu gepushte Rückruffunktion übergeben.
FiringLength = list.length;
else if (auto) {//Wenn die Rückruffunktion derzeit nicht ausgeführt wird und eine automatische Ausführung erforderlich ist
hat keinen Einfluss auf die Ausführungszeile des obigen Codes
FiringStart = Start;
//Führen Sie unsere neu hinzugefügten Partner aus
Feuer(automatisch);
}
gib dies zurück;
},
fire: function () {//Rückruffunktion auslösen
self.fireWith(this, arguments);
gib dies zurück;
},
fireWith: function (context, args) {//Lösen Sie die Rückruffunktion aus und geben Sie den Kontext an
//Wenn einmal konfiguriert ist, ist der Stapel undefiniert und es muss garantiert werden, dass er nur einmal ausgeführt wird. Wenn er also einmal ausgeführt wird, wird der Code hier nicht erneut ausgeführt
If (list && (!fired || stack)) {
//Korrekturparameter
//Hier ist der Kontextindex 0
//Der Parameterlistenindex ist 2
// Die Konvertierung in den Array-Zugriff liegt daran, dass die Objektdarstellung mehr Ressourcen verbraucht. Es gibt eine automatische Funktion [Speicherparameter, automatische Ausführung] im Fire()-Code der obersten Ebene. Wenn Objekte verwendet werden, wird mehr Speicher verbraucht
args = [context,
args.slice && args.slice()
];
fire(args);
}
gib dies zurück;
},
remove: function () {//Eine Rückruffunktion entfernen
if (Liste) {
every(arguments, function (item) {
var index;
// Es können mehrere Elemente vorhanden sein, der Index kann den Suchbereich in der Schleife darstellen und die zuvor gesuchten Elemente müssen nicht erneut durchsucht werden
While ((index = inArray(item, list, index)) > -1) {
list.splice(index, 1);
Wenn (feuernd) {
//Stellen Sie sicher, dass die oben in Fire ausgeführte Funktionsliste korrekt ausgeführt werden kann. Diese globalen Variablen werden in Fire gesetzt, damit sie asynchron entfernt werden können
If (index <= FiringLength)//Korrekturlänge
FiringLength--;
If (index <= FiringLength)//Korrekturindex
FiringIndex--;
}
}
});
}
gib dies zurück;
},
Hat: Funktion (fn) {//Ob es eine Rückruffunktion enthält
return fn ? inAr ray(fn, list) > -1 : list && list.length;
},
empty: function () {//Dieses Rückrufobjekt leeren
list = [];
FiringLength = 0;
gib dies zurück;
},
deaktivieren: Funktion () {// Zerstören Sie dieses Rückrufobjekt, und die nachfolgende Rückruffunktionsliste wird nicht mehr ausgeführt
list = stack = auto = undefiniert;
gib dies zurück;
},
deaktiviert: Funktion () {//Ob es deaktiviert wurde