前端开发系列073-JQuery篇之源码核心
本文介绍jQuery 源码的主体结构和关键细节。
jQuery是前端开发中绕不开的一个框架,在React和Vue等框架出现前,jQuery无疑是前端开发主流技术栈中不可或缺的框架。它为我们提供了强大的DOM操作、可靠的事件处理机制、简单的动画特效、完善的Ajax网络请求、好用的工具方法以及超前的链式调用等能力,阅读jQuery框架的源码是必要的。
总体来说,jQuery框架的源码并不复杂,主要几个版本的代码都保持在1W行左右的水准,下面列出jQuery框架最核心的结构。
/* 001-外层是闭包(立即执行函数) */
(function() {
"use strict"; /* 默认开启严格模式 */
let version = "1.0.0"; /* 设置当前框架的版本号 */
/* 002-声明jQuery工厂函数 */
let jQuery = function(selector) {
/* 构造函数:jQuery.fn.init */
/* 返回实例:jQuery.fn.init 构造函数创建出来的实例对象 */
return new jQuery.fn.init(selector);
}
/* 003-设置jQuery的原型对象 */
jQuery.fn = jQuery.prototype = {
constructor: jQuery,
init: function() {},
eq() {},
get() {},
first() {},
last() {},
toArray() {}
}
/* 004-设置原型对象共享(为了让init的实例对象可以访问jQuery原型成员) */
jQuery.fn.init.prototype = jQuery.fn;
/* 005-插件方法和插件机制 */
jQuery.fn.extend = jQuery.extend = function(o) {
for (let key in o) {
this[key] = o[key];
}
}
jQuery.extend({});
jQuery.fn.extend({});
/* 006-把jQuery和$暴露出去 */
window.jQuery = window.$ = jQuery;
})();
这里再给出jQuery框架中核心入口函数init方法实现的简单版本以及插件机制和核心方法的简单实现。
/* 001-外层是闭包(立即执行函数) */
(function() {
/* 002-默认开启严格模式 */
"use strict";
/* 003-设置当前框架的版本号 */
let version = "1.0.0";
/* 004-声明jQuery工厂函数 */
let jQuery = function(selector) {
/* 构造函数:jQuery.fn.init */
/* 返回:jQuery.fn.init构造函数创建出来的实例对象 */
return new jQuery.fn.init(selector);
}
/* 005-设置jQuery的原型对象 */
jQuery.fn = jQuery.prototype = {
constructor: jQuery,
init: function(selector) {
/* 如果参数为假 */
if (!selector) {
return this;
}
/* 如果参数是函数 */
if ($.isFunction(selector)) {
document.addEventListener("DOMContentLoaded", selector)
}
/* 如果参数是字符串 */
else if ($.isString(selector)) {
/* 标签字符串 "<div>测试</div>" "<a>"*/
if ($.isHTMLSting(selector)) {
let ele = document.createElement("div");
ele.innerHTML = selector;
[].push.apply(this, ele.children)
}
/* 选择器字符串 "div" ".box"*/
else {
[].push.apply(this, document.querySelectorAll(selector));
}
}
/* 如果参数是数组或者是伪数组 */
else if ($.isArray(selector) || $.isLikeArray(selector)) {
//把selector中所有的元素依次添加到this中并且更新length
[].push.apply(this, selector);
} else {
this[0] = selector;
this.length = 1;
}
},
eq(index) {
return $(this.get(index));
},
get(index) {
return index >= 0 ? this[index] : this[this.length + index];
},
first() {
return this.eq(0);
},
last() {
return this.eq(-1);
},
toArray() {
return [].slice.call(this);
}
}
/* 工具方法的处理 */
/* jQuery插件机制 */
jQuery.fn.extend = jQuery.extend = function(o) {
for (let key in o) {
this[key] = o[key];
}
}
jQuery.extend({
isString: function(str) {
return typeof str == "string";
},
isFunction: function(fn) {
return typeof fn == "function";
},
isObject: function(obj) {
return typeof obj == "object" && obj != null;
},
isHTMLSting: function(htmlStr) {
return htmlStr.charAt(0) == "<" && htmlStr.charAt(htmlStr.length - 1) == ">" && htmlStr.length >= 3
},
isArray: function(arr) {
if (Array.isArray) {
return Array.isArray(arr);
} else {
return Object.prototype.toString.call(arr) == "[object Array]";
}
},
isLikeArray: function(likeArr) {
return typeof $.isObject(likeArr) && "length" in likeArr && likeArr.length - 1 in likeArr;
}
});
jQuery.extend({
xxxx() {
console.log("----");
}
})
jQuery.fn.extend({
css: function() {},
text: function() {},
html: function() {}
});
jQuery.fn.extend({
on: function(type, handler) {
// $("button").on("click", function() {
// console.log("click", this);
// })
/* > 给所有选中的标签(this)都添加指定类型的事件,当事件触发的时候要执行事件处理函数 */
for (let i = 0, len = this.length; i < len; i++) {
this[i].addEventListener(type, handler);
}
},
click: function(handler) {
this.on("click", handler);
},
mouseenter: function(handler) {
this.on("mouseenter", handler);
},
mouseleave: function(handler) {
this.on("mouseleave", handler);
}
});
jQuery.fn.extend({
css() {},
attr() {},
addClass() {},
removeClass() {},
toggleClass() {},
append() {}
})
/* 006-设置原型对象共享(为了让init的实例对象可以访问jQuery原型成员) */
jQuery.fn.init.prototype = jQuery.fn;
/* 007-把jQuery和$暴露出去 */
window.jQuery = window.$ = jQuery;
})();
浙公网安备 33010602011771号