Wenn Sie über ein Open-Source-Framework lesen, möchten Sie vor allem die Designideen und Implementierungstechniken erfahren.
Nicht viel Unsinn, die JQuery-Analyse ist seit so vielen Jahren schlecht, ich habe es vor langer Zeit gelesen,
Allerdings habe ich in den letzten Jahren an mobilen Endgeräten gearbeitet und immer zepto verwendet. Vor kurzem habe ich mir wieder etwas Zeit genommen, um jquery zu scannen
Ich werde den Quellcode nicht gemäß dem Skript übersetzen, also lesen Sie ihn bitte basierend auf Ihrer eigenen tatsächlichen Erfahrung!
Das Neueste auf Github ist jquery-master, das der AMD-Spezifikation beigetreten ist. Ich verweise auf die neueste offizielle Version 2.0.3
Gesamtstruktur
Der Kern des jQuery-Frameworks besteht darin, Elemente aus HTML-Dokumenten abzugleichen und Operationen an ihnen auszuführen,
Zum Beispiel:
Mindestens 2 Probleme können dem obigen Text entnommen werden
1. Wie jQuery-Objekte aufgebaut sind
2. So rufen Sie die jQuery-Methode auf
Analyse 1: Die neufreie Konstruktion von jQuery
JavaScript ist eine funktionale Sprache. Klassen können das grundlegendste Konzept der objektorientierten Programmierung sein
a.name();
jQuery verwendet den neuen Operator nicht, um die jQuery-Anzeige zu instanziieren, oder ruft seine Funktion direkt auf
Folgen Sie der Schreibmethode von jQuery
Also ändern Sie den Code:
Wie kann man also eine korrekte Instanz zurückgeben?
Diese Instanz in Javascript bezieht sich nur auf den Prototyp
Dann können Sie die jQuery-Klasse als Factory-Methode zum Erstellen von Instanzen verwenden und diese Methode in den jQuery.prototye-Prototyp einfügen
Offensichtlich gibt aQuery() eine Instanz der aQuery-Klasse zurück, sodass dies in init tatsächlich auf eine Instanz der aQuery-Klasse verweist
Das Problem besteht darin, dass this in init auf die aQuery-Klasse verweist. Wenn die init-Funktion auch als Konstruktor verwendet wird, wie geht man dann mit dem internen this um?
In diesem Fall geht etwas schief, da dies nur auf die aQuery-Klasse verweist, sodass Sie einen unabhängigen Bereich entwerfen müssen
Separate Bereichsverarbeitung des JQuery-Frameworks
Natürlich wird durch die Instanz-Init-Funktion jedes Mal ein neues Init-Instanzobjekt erstellt, um dies zu trennen und Interaktionsverwechslungen zu vermeiden
Da es sich also nicht um dasselbe Objekt handelt, muss ein neues Problem entstehen
Zum Beispiel:
//Uncaught TypeError: Object [object Object] has no method 'name'
console.log(aQuery().name())
Es wird ein Fehler ausgegeben und diese Methode kann nicht gefunden werden. Daher ist es offensichtlich, dass die Init-Initialisierung von new von dieser der JQuery-Klasse getrennt ist
Wie greife ich auf die Eigenschaften und Methoden des jQuery-Klassenprototyps zu?
Wie können wir nicht nur den Bereich isolieren, sondern auch den Bereich des jQuery-Prototypobjekts nutzen? Können wir auch auf das jQuery-Prototypobjekt in der zurückgegebenen Instanz zugreifen?
Wichtige Punkte der Umsetzung
Lösen Sie das Problem durch Prototypenübergabe und übergeben Sie den jQuery-Prototyp an jQuery.prototype.init.prototype
Mit anderen Worten: Das Prototypobjekt von jQuery überschreibt das Prototypobjekt des Init-Konstruktors
Da es als Referenz übergeben wird, besteht kein Grund zur Sorge über das Leistungsproblem dieser Zirkelreferenz
Baidu hat sich zum einfachen und direkten Verständnis ein Bild von einem Internetnutzer ausgeliehen:
Erklären Sie fn. Tatsächlich hat dieser fn keine besondere Bedeutung, sondern ist nur ein Verweis auf jQuery.prototype
Analyse 2: Kettenanruf
Behandlung von DOM-Kettenaufrufen:
1. JS-Code speichern.
2. Das gleiche Objekt wird zurückgegeben, was die Effizienz des Codes verbessern kann
Erzielen Sie browserübergreifende Kettenaufrufe, indem Sie einfach die Prototyp-Methode erweitern und diese zurückgeben.
Verwenden Sie das einfache Factory-Muster unter JS, um dieselbe Instanz für alle Vorgänge am selben DOM-Objekt anzugeben.
Dieses Prinzip ist super einfach
Wenn man den Code aufschlüsselt, ist es offensichtlich, dass die Grundvoraussetzung für die Realisierung der Verkettung die Existenz der Instanz this ist, und das ist dasselbe
Wir müssen also nur in einer verketteten Methode darauf zugreifen, da diese die aktuelle Instanz zurückgibt, sodass wir auf unseren eigenen Prototyp zugreifen können
Vorteile: Sparen Sie die Menge an Code, verbessern Sie die Effizienz des Codes und der Code sieht eleganter aus
Das Schlimmste ist, dass alle Objektmethoden das Objekt selbst zurückgeben, was bedeutet, dass es keinen Rückgabewert gibt, der möglicherweise in keiner Umgebung geeignet ist.
Javascript ist eine nicht blockierende Sprache, also nicht blockierend, aber nicht blockierend, daher muss es ereignisgesteuert sein und einige Vorgänge, die den Prozess blockieren müssen, asynchron ausführen. Diese Verarbeitung ist nur eine synchrone Kette. und eine asynchrone Kette jquery Promise wurde seit 1.5 eingeführt, und jQuery.Deferred wird später besprochen.
Analyse 3: Plug-in-Schnittstelle
Das Hauptgerüst von jQuery sieht so aus. Wenn Sie jedoch Eigenschaftenmethoden zu jQuery oder einem jQuery-Prototyp hinzufügen und Entwicklern Methodenerweiterungen zur Verfügung stellen möchten, sollten diese gemäß den allgemeinen Gewohnheiten des Designers bereitgestellt werden Perspektive der Kapselung? Eine Schnittstelle ist die richtige, und es kann wörtlich verstanden werden, dass es sich um eine Erweiterung der Funktion handelt, anstatt den Prototyp einer benutzerfreundlichen Oberfläche direkt zu modifizieren
jQuery unterstützt seine eigenen erweiterten Eigenschaften. Dies stellt eine externe Schnittstelle, jQuery.fn.extend(), zum Hinzufügen von Methoden zu Objekten bereitWie Sie dem jQuery-Quellcode entnehmen können, sind jQuery.extend und jQuery.fn.extend tatsächlich unterschiedliche Referenzen, die auf dieselbe Methode verweisen
jQuery.fn.extend erweitert die Eigenschaften und Methoden von jQuery.fn
jQuery.extend = jQuery.fn.extend = function(){...}; Dies ist aufeinanderfolgend, dh zwei Punkte zeigen auf dieselbe Funktion. Wie können sie unterschiedliche Funktionen erreichen? Das ist die Kraft davon!
Für fn und jQuery handelt es sich tatsächlich um zwei verschiedene Objekte, wie zuvor beschrieben:
Wenn jQuery.extend aufgerufen wird, zeigt dies auf das jQuery-Objekt (jQuery ist eine Funktion und ein Objekt!), daher befindet sich die Erweiterung hier auf jQuery.
Wenn jQuery.fn.extend aufgerufen wird, zeigt dies auf das fn-Objekt, jQuery.fn und jQuery.prototype zeigen auf dasselbe Objekt, und durch Erweitern von fn wird das jQuery.prototype-Prototypobjekt erweitert.
Was hier hinzugefügt wird, ist die Prototypmethode, bei der es sich um die Objektmethode handelt. Daher stellt die jQuery-API die beiden oben genannten Erweiterungsfunktionen bereit.
// Handle a deep copy situation
if ( typeof target === "boolean" ) { // 如果第一个参数为true,即 jQuery.extend( true, obj1, obj2 ); 的情况
deep = target; // 此时target是true
target = arguments[1] || {}; // target改为 obj1
// skip the boolean and the target
i = 2;
}
// Handle case when target is a string or something (possible in deep copy)
if ( typeof target !== "object" && !jQuery.isFunction(target) ) { // 处理奇怪的情况,比如 jQuery.extend( 'hello' , {nick: 'casper})~~
target = {};
}
// extend jQuery itself if only one argument is passed
if ( length === i ) { // 处理这种情况 jQuery.extend(obj),或 jQuery.fn.extend( obj )
target = this; // jQuery.extend时,this指的是jQuery;jQuery.fn.extend时,this指的是jQuery.fn
--i;
}
for ( ; i < length; i++ ) {
// Only deal with non-null/undefined values
if ( (options = arguments[ i ]) != null ) { // 比如 jQuery.extend( obj1, obj2, obj3, ojb4 ),options则为 obj2、obj3...
// Extend the base object
for ( name in options ) {
src = target[ name ];
copy = options[ name ];
// Prevent never-ending loop
if ( target === copy ) { // 防止自引用,不赘述
continue;
}
// Recurse if we're merging plain objects or arrays
// 如果是深拷贝,且被拷贝的属性值本身是个对象
if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
if ( copyIsArray ) { // 被拷贝的属性值是个数组
copyIsArray = false;
clone = src && jQuery.isArray(src) ? src : [];
} else { 被拷贝的属性值是个plainObject,比如{ nick: 'casper' }
clone = src && jQuery.isPlainObject(src) ? src : {};
}
// Originalobjekte NIEMALS VERSCHIEBEN, sondern klonen
Target [name] = jquery.extEnd (deep, clone, copy); // Rekursiv ~
// Das geänderte Objekt zurückgeben
return target;
Zusammenfassung:
Erstellen Sie ein neues Objekt über new jQuery.fn.init() mit der Prototyp-Prototyp-Objektmethode des Init-Konstruktors.
Durch Ändern der Ausrichtung des Prototyp-Zeigers kann dieses neue Objekt auch auf den Prototyp des Prototyps zeigen jQuery-Klassenprototyp
Das auf diese Weise konstruierte Objekt setzt also alle durch den jQuery.fn-Prototyp definierten Methoden fort