Maison  >  Article  >  interface Web  >  jQuery 2.0.3 noyau d'analyse du code source (1) architecture globale_jquery

jQuery 2.0.3 noyau d'analyse du code source (1) architecture globale_jquery

WBOY
WBOYoriginal
2016-05-16 16:46:401064parcourir

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 :

Copier le code Le code est le suivant :

$().find().css ()
$().hide().html('....').hide().

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

.

Copier le code Le code est le suivant :

var aQuery = function(sélecteur, contexte) {
//Constructeur
}
aQuery.prototype = {
//Prototype
nom:fonction(){},
âge:fonction(){}
}

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

Copier le code Le code est le suivant :

$().ready()
$( ).noConflict()

Pour y parvenir, jQuery doit être considéré comme une classe, alors $() doit renvoyer une instance de la classe

Alors changez le code :

Copier le code Le code est le suivant :

var aQuery = function(sélecteur, contexte) { Renvoyer un nouveau aQuery();
}
aQuery.prototype = {
name:function(){},
age:function(){}
}

Grâce au nouveau aQuery(), même si une instance est renvoyée, nous pouvons toujours voir un problème évident, une boucle infinie !

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

Copier le code Le code est le suivant :
var aQuery = function(sélecteur, contexte) {
Return aQuery.prototype.init();
}
aQuery.prototype = {
init:function(){
return this;
}
name:function (){ },
âge:fonction(){}
}

Instance renvoyée lors de l'exécution de aQuery() :

É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 ?

Copier le code Le code est le suivant :
var aQuery = function(sélecteur, contexte) {
Retourner aQuery.prototype.init();
}
aQuery.prototype = {
init: function() {
this.age = 18
renvoyer ceci;
},
nom : fonction() {},
âge : 20
}
aQuery().age //18


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

Copier le code Le code est le suivant :

jQuery = function( selector, context ) {
// L'objet jQuery n'est en fait que le constructeur d'initialisation 'amélioré'
return new jQuery.fn.init( selector, context, rootjQuery );
},

É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 :

Copier le code Le code est le suivant :

var aQuery = function(sélecteur, contexte) {
Renvoie un nouveau aQuery.prototype.init();
}
aQuery.prototype = {
init: function() {
this.age = 18
renvoie ceci ;
} ,
nom : fonction() {},
âge : 20
}

//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

Copier le code Le code est le suivant :

// Donner à la fonction init le prototype jQuery pour une instanciation ultérieure
jQuery.fn.init.prototype = jQuery.fn;

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

Copier le code Le code est le suivant :

var aQuery = function(sélecteur, contexte) {
Renvoyer le nouveau aQuery.prototype.init();
}
aQuery.prototype = {
init: function() {
Renvoyer ceci;
},
nom : function() {
return this.age
},
âge : 20
}
aQuery.prototype.init.prototype = aQuery.prototype;
console.log(aQuery ().nom()) //20

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

Copier le code Le code est le suivant :

aQuery().init().name ()
Décomposer
a = aQuery();
a.init()
a.name()

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

Copier le code Le code est le suivant :

aQuery.prototype = {
init : function( ) {
renvoie ceci;
},
nom : function() {
renvoie ceci
}
}

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

Copier le code Le code est le suivant :

aQuery.init().name()

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

Copier le code Le code est le suivant :

jQuery.extend = jQuery.fn.extend = fonction( ) {

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

Copier le code Le code est le suivant :

jQuery.extend = jQuery.fn.extend = function() {
    var src, copyIsArray, copy, name, options, clone,
        target = arguments[0] || {},    // 常见用法 jQuery.extend( obj1, obj2 ),此时,target为arguments[0]
        i = 1,
        length = arguments.length,
        deep = false;

    // 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é :

Construisez un nouvel objet via new jQuery.fn.init(), avec la méthode prototype prototype object du constructeur init En changeant le pointage du pointeur du prototype, laissez ce nouvel objet pointer également vers le prototype du Prototype de classe jQuery

L'objet ainsi construit continue donc toutes les méthodes définies par le prototype jQuery.fn

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn