这一节探讨jQuery的内部运行原理,揭开它如何以如此优雅的方式操作DOM、处理事件。理解其核心机理,将帮助你更高效、更自信地使用jQuery进行开发。
理解$(selector)
每次调用$(selector)时,jQuery在内部通过一种“工厂模式”自动创建并返回了一个jQuery对象实例,省去了使用new关键字的步骤。这是jQuery最基础的运行原理之一。
var jQuery = function(selector, context) {
// 关键点:返回的是 new操作符创建的实例,而非 this
return new jQuery.fn.init(selector, context);
};
// 初始化 jQuery 对象的核心构造函数
var init = jQuery.fn.init = function(selector, context, root) {
// ... 这里会根据 selector 的类型(如字符串、DOM元素等)进行不同处理
// ... 并将匹配到的 DOM 元素挂载到当前对象上(如 this[0] = element)
};这段代码揭示了第一个关键原理:$() 的返回值实际上是 new jQuery.fn.init() 的实例。
将$()进行自定义
在 jQuery 中,$ 符号只是一个指向 jQuery 对象的便捷别名。如果你希望将 $() 替换为自定义的符号,比如 WQ(),jQuery 提供了内置的机制来实现这一点。最常用的方法是使用 jQuery.noConflict() 方法。
jQuery.noConflict() 的作用是将 $ 标识符还原为其原始值(即还原为其他可能使用了 $ 的库,如 Prototype.js),并返回对 jQuery 对象的引用。你可以将这个返回值赋给你自定义的变量名,例如 WQ。
操作步骤如下:
- 引入 jQuery 库后,在后续代码中调用
var WQ = jQuery.noConflict();。 - 之后,你就可以用
WQ()来代替$()进行所有 jQuery 操作。
示例代码:
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
// 将 $ 的控制权让出,并将 jQuery 对象赋值给 WQ
var WQ = jQuery.noConflict();
// 现在使用 WQ 代替 $
WQ(document).ready(function() {
WQ('div').css('color', 'red');
});
</script>如果你想保留 $ 的同时,增加一个额外的别名 WQ,也可以直接赋值:var WQ = jQuery;。但这样做不会释放 $,如果页面中存在其他库也使用了 $,可能会引起冲突。
原理说明:
- jQuery 在加载时会占用全局变量
$和jQuery。 jQuery.noConflict(true)可以深度释放$和jQuery两个变量,但一般我们只需释放$,即调用无参形式jQuery.noConflict()。- 该方法返回的是 jQuery 对象本身,因此可以立即赋给新变量。
注意事项:
- 调用
noConflict()的时机必须在引入 jQuery 之后,且在使用$之前。 - 如果你希望在函数内部继续使用
$作为别名,同时又避免全局冲突,可以采用立即执行函数传参的方式:javascript复制下载(function($) { // 在此函数内部,$ 就是 jQuery 对象 $(document).ready(function() { … }); })(WQ); // 传入 WQ
通过这种方式,你可以自由地将 $() 替换为任何你喜欢的符号,使代码既符合个人习惯,又能与其他库和谐共存。
原型共享
jQuery通过一条巧妙的原型链赋值,将 jQuery.fn.init 的原型指向了 jQuery.fn,而 jQuery.fn 正是 jQuery.prototype 的引用。这个步骤是jQuery运行原理中的精髓,使得所有由 new jQuery.fn.init 创建出来的对象,都能自然地继承 jQuery.prototype 上的所有方法。
// 核心步骤:让 init 的原型 指向 jQuery 的原型
jQuery.fn.init.prototype = jQuery.fn;
// 而 jQuery.fn 就是 jQuery.prototype 的引用
jQuery.fn = jQuery.prototype = {
// 这里包含了 css, html, attr 等所有实例方法
css: function(key, value) { /* ... */ },
// ...
};这个过程可以简单概括为:调用 $() 得到的是一个 init 函数的实例,但这个实例的原型已经被改造成了 jQuery 的原型。
因此,这个实例自然而然地就拥有了 jQuery 原型上定义的所有方法。
链式调用
jQuery另一个广受欢迎的特性是链式调用,例如 $('div').css('color', 'red').show().html('内容')。
其实现原理非常直接:在每一个除“获取值”以外的实例方法中,方法的末尾都会返回当前对象本身(即 return this)。
jQuery.fn.extend({
html: function(value) {
// ... 执行设置 HTML 的操作
return this; // 返回当前实例,以便下一个方法继续调用
},
css: function(key, value) {
// ... 执行设置样式的操作
return this;
}
});当在一个实例上调用 html() 方法后,其返回的依然是这个实例,紧接着就能继续调用 css() 方法。
这种设计模式不仅让代码更简洁、可读性更强,也深刻体现了jQuery“写得更少,做得更多”的核心思想。
jQuery对象与DOM对象
理解了对象的构建过程,我们就能更清晰地认识jQuery对象与原生DOM对象的区别。jQuery运行原理的一个重要环节就是它始终在操作自己构建的类数组对象。
| 特性 | jQuery对象 | DOM对象 |
|---|---|---|
| 本质 | 一个包含了DOM元素引用的类数组对象 | 浏览器原生解析HTML生成的节点对象 |
| 表示方式 | $('div') 或 jQuery('div') | document.getElementById('id') 等 |
| 可用方法 | 只能使用jQuery提供的API(如 css(), html()) | 只能使用原生JavaScript API(如 innerHTML, style) |
| 互相转换 | $domObj[index] 或 $domObj.get(index) 转为DOM对象 | $(domObj) 转为jQuery对象 |
在jQuery内部,jQuery.fn.init 这个构造函数的一个重要任务,就是将选择器匹配到的DOM元素,以数字索引的形式存储到自身对象上(如 this[0] = divElement),并设置 length 属性,从而构建出一个“类数组”的jQuery对象。后续的所有jQuery方法都是针对这个类数组对象进行遍历和操作。
