Lorsque vous lisez des articles sur un framework open source, ce que vous souhaitez le plus apprendre, ce sont les idées de conception et les techniques de mise en œuvre.
Pas grand chose de bêtises, l'analyse jquery est mauvaise depuis tant d'années, je l'ai lu il y a longtemps,
Cependant, j'ai travaillé sur des terminaux mobiles ces dernières années et j'ai toujours utilisé zepto. Récemment, j'ai pris le temps de scanner à nouveau jquery
.Je ne traduirai pas le code source selon le script, alors veuillez le lire en fonction de votre propre expérience réelle !
La dernière en date sur github est jquery-master, qui a rejoint la spécification AMD je ferai référence à la dernière version officielle 2.0.3
.Structure globale
Le cœur du framework jQuery est de faire correspondre les éléments des documents HTML et d'effectuer des opérations sur eux,
Par exemple :
Au moins 2 problèmes peuvent être trouvés à partir de l'écriture ci-dessus
1. Comment les objets jQuery sont construits
2. Comment appeler la méthode jQuery
Analyse 1 : la nouvelle construction libre de jQuery
JavaScript est un langage fonctionnel. Les fonctions peuvent implémenter des classes. Les classes sont le concept le plus basique de la programmation orientée objet
.var a = new aQuery();
a.name();
C'est l'usage normal. Il est évident que jQuery ne fonctionne pas comme ça
jQuery n'utilise pas l'opérateur new pour instancier l'affichage jQuery, ou appelle directement sa fonction
Suivez la méthode d'écriture de jQuery
Pour y parvenir, jQuery doit être considéré comme une classe, alors $() doit renvoyer une instance de la classe
Alors changez le code :
Alors, comment renvoyer une instance correcte ?
Instance ceci en javascript est uniquement lié au prototype
Ensuite, vous pouvez utiliser la classe jQuery comme méthode d'usine pour créer des instances, et mettre cette méthode dans le prototype jQuery.prototye
Évidemment, aQuery() renvoie une instance de la classe aQuery, donc ceci dans init pointe en fait vers une instance de la classe aQuery
Le problème est que this dans init pointe vers la classe aQuery. Si la fonction init est également utilisée comme constructeur, comment gérer le this interne ?
Dans ce cas, quelque chose ne va pas, car cela pointe uniquement vers la classe aQuery, vous devez donc concevoir une portée indépendante
Traitement de portée séparé du framework JQuery
Évidemment, grâce à la fonction d'instance init, un nouvel objet d'instance init est construit à chaque fois pour séparer cela et éviter toute confusion d'interaction
Donc comme ce n'est pas le même objet, un nouveau problème doit surgir
Par exemple :
//Uncaught TypeError : l'objet [object Object] n'a pas de méthode 'name'
console.log(aQuery().name())
Une erreur est générée et cette méthode est introuvable, il est donc évident que l'init de new est séparé de celui de la classe jquery
Comment accéder aux propriétés et méthodes sur le prototype de la classe jQuery ?
Comment pouvons-nous non seulement isoler la portée mais également utiliser la portée de l'objet prototype jQuery ? Pouvons-nous également accéder à l'objet prototype jQuery dans l'instance renvoyée ?
Points clés de mise en œuvre
Résolvez le problème en passant le prototype, transmettez le prototype jQuery à jQuery.prototype.init.prototype
En d’autres termes, l’objet prototype de jQuery remplace l’objet prototype du constructeur init
Parce qu'elle est passée par référence, il n'y a pas lieu de s'inquiéter du problème de performances de cette référence circulaire
Baidu a emprunté une photo à un internaute pour une compréhension facile et directe :
Expliquez fn, en fait, ce fn n'a pas de signification particulière, c'est juste une référence à jQuery.prototype
Analyse 2 : Appel en chaîne
Gestion des appels de chaîne DOM :
1. Enregistrez le code JS.
2. Le même objet est renvoyé, ce qui peut améliorer l'efficacité du code
Réalisez des appels en chaîne entre navigateurs en étendant simplement la méthode prototype et en la renvoyant.
Utilisez le modèle d'usine simple sous JS pour spécifier la même instance pour toutes les opérations sur le même objet DOM.
Ce principe est super simple
En décomposant le code, il est évident que la condition de base pour réaliser le chaînage est l'existence de l'instance this, et c'est pareil
Nous n'avons donc besoin d'y accéder que dans une méthode chaînée, car elle renvoie ceci de l'instance actuelle, afin que nous puissions accéder à notre propre prototype
Avantages : économisez la quantité de code, améliorez l'efficacité du code et le code semble plus élégant
Le pire est que toutes les méthodes objet renvoient l'objet lui-même, ce qui signifie qu'il n'y a pas de valeur de retour, ce qui peut ne convenir à aucun environnement.
Javascript est un langage non bloquant, donc il ne bloque pas, mais il ne peut pas bloquer, il doit donc être piloté par les événements et effectuer de manière asynchrone certaines opérations qui doivent bloquer le processus. Ce traitement n'est qu'une chaîne synchrone, et une chaîne asynchrone jquery Promise a été introduite depuis la version 1.5, et jQuery.Deferred sera discuté plus tard.
Analyse 3 : Interface du plug-in
Le framework principal de jQuery est comme ceci, mais selon les habitudes du concepteur général, si vous souhaitez ajouter des méthodes de propriété à jQuery ou au prototype jQuery, et si vous souhaitez fournir aux développeurs des extensions de méthodes, doivent-elles être fournies à partir du perspective d'encapsulation ? Une interface est la bonne, et on peut comprendre littéralement qu'il s'agit d'une extension de la fonction, plutôt que de modifier directement le prototype Interface utilisateur conviviale,
.jQuery prend en charge ses propres propriétés étendues. Cela fournit une interface externe, jQuery.fn.extend(), pour ajouter des méthodes aux objets
Comme vous pouvez le voir dans le code source de jQuery, jQuery.extend et jQuery.fn.extend sont en fait des références différentes pointant vers la même méthode
jQuery.extend étend les propriétés et les méthodes de jQuery lui-même
jQuery.fn.extend étend les propriétés et méthodes de jQuery.fn
La fonction extend() peut être utilisée pour étendre facilement et rapidement des fonctions sans détruire la structure du prototype de jQuery
jQuery.extend = jQuery.fn.extend = function(){...}; C'est consécutif, c'est-à-dire deux points pointant vers la même fonction, comment peuvent-ils réaliser des fonctions différentes ? C'est le pouvoir de ça !
Pour fn et jQuery sont en fait deux objets différents, comme décrit précédemment :
Lorsque jQuery.extend est appelé, cela pointe vers l'objet jQuery (jQuery est une fonction et un objet !), donc l'extension ici est sur jQuery.
Lorsque jQuery.fn.extend est appelé, cela pointe vers l'objet fn, jQuery.fn et jQuery.prototype pointent vers le même objet, et étendre fn consiste à étendre l'objet prototype jQuery.prototype.
Ce qui est ajouté ici, c'est la méthode prototype, qui est la méthode objet. Par conséquent, l'API jQuery fournit les deux fonctions d'extension ci-dessus.
Implémentation d'étendre
// 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 : {};
}
// NE JAMAIS DÉPLACER les objets originaux, les cloner
Target [name] = jquery.extEnd (deep, clone, copy);
}
}
}
// Renvoie l'objet modifié
return target;
Résumé :
L'objet ainsi construit continue donc toutes les méthodes définies par le prototype jQuery.fn