jQuery 实现原理
手写一个简易版 jQuery,理解其设计思想
问题
jQuery 是如何实现的?它的链式调用、选择器、插件机制是怎么工作的?
解答
基本结构
(function(window) {
// jQuery 构造函数
function jQuery(selector) {
// 无需 new 即可创建实例
return new jQuery.fn.init(selector);
}
// 原型对象
jQuery.fn = jQuery.prototype = {
constructor: jQuery,
length: 0,
// 真正的构造函数
init: function(selector) {
// 处理空选择器
if (!selector) {
return this;
}
// 处理字符串选择器
if (typeof selector === 'string') {
const elements = document.querySelectorAll(selector);
for (let i = 0; i < elements.length; i++) {
this[i] = elements[i];
}
this.length = elements.length;
}
// 处理 DOM 元素
if (selector.nodeType) {
this[0] = selector;
this.length = 1;
}
return this;
}
};
// 关键:让 init 的实例能访问 jQuery.fn 上的方法
jQuery.fn.init.prototype = jQuery.fn;
// 暴露到全局
window.$ = window.jQuery = jQuery;
})(window);
链式调用
jQuery.fn.css = function(property, value) {
for (let i = 0; i < this.length; i++) {
this[i].style[property] = value;
}
// 返回 this 实现链式调用
return this;
};
jQuery.fn.addClass = function(className) {
for (let i = 0; i < this.length; i++) {
this[i].classList.add(className);
}
return this;
};
jQuery.fn.on = function(event, callback) {
for (let i = 0; i < this.length; i++) {
this[i].addEventListener(event, callback);
}
return this;
};
// 使用
$('.box').css('color', 'red').addClass('active').on('click', fn);
插件机制
// 实例方法扩展
jQuery.fn.extend = function(obj) {
for (let key in obj) {
this[key] = obj[key];
}
};
// 静态方法扩展
jQuery.extend = function(obj) {
for (let key in obj) {
this[key] = obj[key];
}
};
// 添加插件
$.fn.extend({
fadeIn: function(duration) {
// 淡入逻辑
return this;
}
});
// 添加工具方法
$.extend({
ajax: function(options) {
// ajax 逻辑
}
});
完整示例
(function(window) {
function jQuery(selector) {
return new jQuery.fn.init(selector);
}
jQuery.fn = jQuery.prototype = {
constructor: jQuery,
length: 0,
init: function(selector) {
if (!selector) return this;
if (typeof selector === 'string') {
// 处理 HTML 字符串
if (selector[0] === '<') {
const div = document.createElement('div');
div.innerHTML = selector;
this[0] = div.firstChild;
this.length = 1;
} else {
// 处理选择器
const elements = document.querySelectorAll(selector);
for (let i = 0; i < elements.length; i++) {
this[i] = elements[i];
}
this.length = elements.length;
}
} else if (selector.nodeType) {
this[0] = selector;
this.length = 1;
} else if (typeof selector === 'function') {
// $(function) 等同于 DOMContentLoaded
document.addEventListener('DOMContentLoaded', selector);
}
return this;
},
// 遍历方法
each: function(callback) {
for (let i = 0; i < this.length; i++) {
callback.call(this[i], i, this[i]);
}
return this;
},
// 获取/设置属性
attr: function(name, value) {
if (value === undefined) {
return this[0]?.getAttribute(name);
}
return this.each(function() {
this.setAttribute(name, value);
});
},
// 获取/设置内容
html: function(content) {
if (content === undefined) {
return this[0]?.innerHTML;
}
return this.each(function() {
this.innerHTML = content;
});
}
};
jQuery.fn.init.prototype = jQuery.fn;
window.$ = window.jQuery = jQuery;
})(window);
// 测试
$('.btn').attr('disabled', 'true').html('Loading...');
关键点
- 无 new 构造:通过
new jQuery.fn.init()内部创建实例,外部无需 new - 原型共享:
jQuery.fn.init.prototype = jQuery.fn让 init 实例能访问所有方法 - 链式调用:每个方法返回
this,支持连续调用 - 类数组对象:用数字索引存储 DOM 元素,配合 length 属性
- 插件扩展:通过
$.fn.extend添加实例方法,$.extend添加静态方法
目录