jQuery对于大家而言并不陌生,因此关于它是什么以及它的作用,在这里我就不多言了,而本篇文章的目的是想通过对源码简单的分析来讨论 jQuery 的核心架构设计,以及jQuery 是如何利用javascript中的高级特性来构建如此伟大的javascript库。
1 初识jQuery
从核心功能来看,jQuery仅仅做了一件简单而又平凡的事:查询。它的语法如此简洁明了,以致于很多人在不知道javascript是什么的时候就已经会用jQuery了,用一个词形容就是:大道至简。
从设计层面来看,我们可以将jQuery提供方法分为两大类:静态方法和实例方法。静态方法就是直接通过$访问的方法,这些方法一般不对dom元素操作,而是提供了一些常用的工具,比如ajax请求、以及对字符串的一些常用操作,除此之外,jQuery还提供了对自身的扩展机制,你可以通过extend方法来编写你需要的组件。而实例方法和静态方法不一样,它是用来对jQuery查询的DOM元素进行操作,jQuery执行$()会构建一个jQuery对象,这个对象以数组的方法存储查询出的所有DOM元素,然后在这个对象的原型链上实现了对这些DOM操作的方法,比如each()方法就是用来遍历每一个DOM元素的。
你可能会注意到,我刚说这个对象“以数组的方式”存储,那就是说,jQuery构建的这个对象不是数组,那这个对象到底是什么? 其实这个对象就是jQuery的核心,也被称作“jQuery对象”。因此,本文的重点就是对jQuery对象进行分析和讨论。
2 jQuery对象
一般情况下,我们会这样使用jQuery:
$('div').each(function(index){ //this ... });
$('div')执行完后回返回一个jQuery对象,each()方法是对这个对象中的DOM元素进行遍历,我们先看看$('div')的执行过程(本文源码摘自jQuery 3.0):
jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context ); }
这个方法就是$('div')的入口方法,$是jQuery的简写,就相当于jQuery('div') ,可以看出,这个方法只做了一件事,那就是返回jQuery.fn.init()函数的实例对象,那jQuery.fn.init 又是什么呢,我们再看下面的代码:
init = jQuery.fn.init = function( selector, context, root ) { //... return this; } init.prototype = jQuery.fn;
jQuery.fn.init和init引用了同一个方法,这个方法根据selector查询出符合条件的DOM元素,并返回,可你会发现,返回的是this,这个this是什么呢?我们待会分析,先看下面的这句话:
init.prototype = jQuery.fn;
这句话是什么意思呢,这句话是让init方法的prototype对象指向了jQuery.fn对象,那jQuery.fn又是什么鬼? 我们继续看代码:
jQuery.fn = jQuery.prototype = { // The current version of jQuery being used jquery: version, constructor: jQuery, // The default length of a jQuery object is 0 length: 0, // Execute a callback for every element in the matched set. each: function( callback ) { return jQuery.each( this, callback ); }, splice: arr.splice };
为了节省篇幅,我省略了其中一些代码,从这里可以看出,jQuery.fn 其实就是jQuery的原型对象,这个原型对象中定义了一些对this对象进行操作的方法。到这里,你是不是感觉到有点绕,不要着急,我们来梳理一下思路:jQuery首先定义了一个init方法,然后在init的原型对象prototype上定义了一系列操作方法。最后将init方法的实例对象返回。所以上面的过程可以简化如下(伪代码表示):
var init = function(selector,context,root){ //... return this; } init.prototype = { length:0, each:function(callback){ //... }, splice:[].splice } jQuery = function(selector,context,root){ return new init(selector,context,root); }
那么问题来了,jQuery.fn中的方法为什么不直接定义在init的prototype上,而要定义在jQuery的原型对象上?
其实,这样做的目的是为了提高jQuery的查询效率,如果直接定义在init的prototype对象上,那么每执行一次查询,就会在内存中创建这样一个庞大的prototype对象,而如果把这个对象定义在jQuery的prototype上,在jQuery加载时,这个对象就会被初始化并一直存在于内存中,以后每次执行$()时,只需要将init中的prototype指向这个对象就可以了,而不用每次都去创建一遍相同的对象。
我们再来看看 init 函数中返回的 this 到底是什么,我在之前的博客中讲过,函数中的this总是指向运行期的调用者,那init的调用者是谁呢?在上面代码中似乎找不到调用者,这时我们就需要深入的理解new运算符的运行机制了,借用我之前在博客中对new运算符的描述,我们对new init()的执行过程进行如下分解:
new init(selector,context,root) = { var obj = {}; obj.__proto__ = init.prototype; init.call(obj,selector,context,root); return typeof result === 'obj'? result : obj; }
Wie aus dem obigen Zerlegungsprozess ersichtlich ist, erstellt JavaScript beim Erstellen eines Instanzobjekts über new zunächst ein gewöhnliches Objekt obj und verweist dann das interne Attribut __proto__ von obj auf das Prototypobjekt von init, also obj Das Die Prototypenkette wird geändert und Schritt 3 verwendet die Call-Methode zum Aufrufen von init(), sodass sich dies in init hier auf das obj-Objekt bezieht.
Nachdem init() ausgeführt wurde, werden alle übereinstimmenden DOM-Objekte in Form eines Arrays in diesem Objekt gespeichert und zurückgegeben, dh das obj-Objekt wird zurückgegeben und der neue Operator wird schließlich zurückgegeben Dieses Objekt wird als neues Instanzobjekt zurückgegeben. Daher weist das vom neuen Operator zurückgegebene Instanzobjekt zwei Merkmale auf: Erstens enthält es die DOM-Abfrageergebnismenge, und zweitens erbt seine Prototypkette den Prototyp von init, und der Prototyp von init zeigt auf das jQuery.fn-Objekt Das Instanzobjekt verfügt ebenfalls über diese Betriebsmethoden.
jQuery erstellt jedes Mal ein jQuery-Objekt, wenn eine Abfrage ausgeführt wird, und in derselben Anwendung verwenden alle jQuery-Objekte dasselbe jQuery-Prototypobjekt. Daher enthält das jQuery-Objekt nicht nur die DOM-Abfrageergebnismenge, sondern erbt auch die Operationsmethoden für das jQuery-Prototypobjekt. Auf diese Weise können Sie nach der Abfrage direkt Methoden aufrufen, um diese DOM-Elemente zu bearbeiten. Dies ist das Kernarchitekturdesign von jQuery, einfach, bequem und praktisch!
Wenn Sie die obige Erklärung immer noch nicht verstehen, machen Sie sich keine Sorgen, ich habe ein komplettes kleines Projekt jDate gemäß den Designideen von jQuery geschrieben, das Sie vergleichen und verstehen können! Das jDate-Projekt wurde auf GitHub hochgeladen. Sie können hier klicken, um den vollständigen Code anzuzeigen: jDate. Wenn Sie anderer Meinung sind, können Sie gerne diskutieren.
3 Mängel von jQuery
Durch die Analyse der Kernarchitektur von jQuery werden wir feststellen, dass jQuery jedes Mal, wenn eine Abfrage ausgeführt wird, ein komplexes jQuery-Objekt im Speicher erstellen muss Obwohl jedes jQuery-Objekt denselben jQuery-Prototyp verwendet, ist der Abfrageprozess von jQuery weitaus komplizierter als Sie denken. Er muss verschiedene übereinstimmende Bezeichner und die Kompatibilität verschiedener Browser berücksichtigen. Wenn Sie nur einige einfache Vorgänge im DOM ausführen, wird empfohlen, die native Methode querySelector anstelle von jQuery zu verwenden. Bei Verwendung der nativen Methode müssen Sie jedoch möglicherweise einige Kompatibilitätsarbeiten für verschiedene Anwendungsszenarien durchführen Lernen Sie, Kompromisse einzugehen und es nicht zu übertreiben. Hängt von jQuery ab!
Das Obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, er wird alle dazu inspirieren, JQuery zu lernen. Weitere verwandte Tutorials finden Sie im jQuery-Video-Tutorial.