对于操作 DOM 来说,jQuery 是非常方便的一个库,虽然如今随着 React, Vue 之类框架的流行,jQuery 用得越来越少了,但是其中很多思想还是非常值得我们学习的,这篇文章将介绍如何从零开始实现一个简化版 jQuery

在这里,我把这个库命名为 Clus(class 的谐音),下面以 $ 符号代替。

首先需要声明一个构造函数并做一些初始化操作:


function $(selector) {
return new $.fn.init(selector);
} $.fn = $.prototype = {
contructor: $,
init,
};

可以看到,该构造函数返回一个 $.fn.init 的实例,这样做的好处就是在使用的时候不要每次都 new 一个构造函数就可以创建一个新的实例了,可以看出来,整个核心都在 init 函数上了:


function init(selector) {
const fragmentRE = /^\s*<(\w+|!)[^>]*>/;
const selectorType = $.type(selector);
const elementTypes = [1, 9, 11]; let dom; if (!selector) {
dom = [],
dom.selector = selector;
} else if (elementTypes.indexOf(selector.nodeType) !== -1 || selector === window) {
dom = [selector],
selector = null;
} else if (selectorType === 'function') {
return $(document).ready(selector);
} else if (selectorType === 'array') {
dom = selector;
} else if (selectorType === 'object') {
dom = [selector],
selector = null;
} else if (selectorType === 'string') {
if (selector[0] === '<' && fragmentRE.test(selector)) {
dom = $.parseHTML(selector),
selector = null;
} else {
dom = [].slice.call(document.querySelectorAll(selector));
}
} dom = dom || [];
$.extend(dom, $.fn);
dom.selector = selector; return dom;
}

可以很清楚的看到,根据传入的参数类型的不同进行一些不同的操作,比如传入的是函数的话,则该函数里的操作的都是 DOM Ready 之后的操作了;再比如传入的是字符串的话,并且如果是标签的话,则会把这段标签字符串解析成 DOM Fragment,如果是普通字符串,则会调用 document.querySelectorAll() 方法来查找 DOM。

相信大家都能很容易的看明白上面的代码,不过有一点值得一提的是 $.extend(dom, $.fn); 这段代码,其含义是把实例上的所有方法都添加到 dom 这个数组对象中,这样做的目的就是为了可以直接链式调用某个实例的方法,比如 $('.clus').addClass('hello'),这个 addClass() 方法就是在 $.fn 上实现的。因此所有在 $.fn 实现的方法都可以通过 $(selector).method() 这种方式来调用了。

至于 extend() 方法我认为是除了 init() 方法以外,整个库中最核心的一个方法了,代码如下:


export default function extend() {
let options, name, clone, copy, source, copyIsArray,
target = arguments[0] || {},
i = 1,
length = arguments.length,
deep = false; if (typeof target === 'boolean') {
deep = target;
target = arguments[i] || {};
i++;
} if (typeof target !== 'object' && $.type(target) !== 'function') {
target = {};
} if (i === length) {
target = this;
i--;
} for (; i < length; i++) {
//
if ((options = arguments[i]) !== null) {
// for in source object
for (name in options) { source = target[name];
copy = options[name]; if (target == copy) {
continue;
} // deep clone
if (deep && copy && ($.isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) {
// if copy is array
if (copyIsArray) {
copyIsArray = false;
// if is not array, set it to array
clone = source && Array.isArray(source) ? source : [];
} else {
// if copy is not a object, set it to object
clone = source && $.isPlainObject(source) ? source : {};
} target[name] = extend(deep, clone, copy);
} else if (copy !== undefined) {
target[name] = copy;
}
}
}
} return target;
}

可以看到,和 jQuery 的实现一毛一样,没错就是从那儿 copy 过来的当然一样。

下面以 addClass() 方法为例介绍如何操作 DOM 的:


function addClass(cls) {
let classes, clazz, el, cur, curValue, finalValue, j, i = 0; if (typeof cls === 'string' && cls) {
classes = cls.match(rnotwhite) || []; while((el = this[i++])) {
curValue = getClass(el);
cur = (el.nodeType === 1) && ` ${curValue} `.replace(rclass, ' '); if (cur) {
j = 0; while((clazz = classes[j++])) {
// to determine whether the class that to add has already existed
if (cur.indexOf(` ${clazz} `) == -1) {
cur += clazz + ' ';
}
finalValue = $.trim(cur);
if ( curValue !== finalValue ) {
el.setAttribute('class', finalValue);
}
}
}
}
} return this;
} $.fn.addClass = addClass;

值得一提的就是在实例方法中,this 关键字可以访问到根据选择器所查询到的所有元素的集合,在这里是通过 while 循环来对每个元素进行操作。要实现类似 $(selector).addClass().removeClass() 这样的链式操作,只需要在每个实例方法中返回一个 this 即可。要实现其他实例方法比如 hasClass() 之类的也是类似的方法。

其实每个实例方法都是通过 this 关键字来获取查询到的元素,然后遍历这些元素来针对每个元素进行具体的操作,在举一个栗子:


function append(DOMString) {
let el, i = 0,
fregmentCollection = $.parseHTML(DOMString),
fregments = Array.prototype.slice.apply(fregmentCollection); while((el = this[i++])) {
fregments.map(fregment => {
el.appendChild(fregment);
});
} return this;
} $.fn.append = append;

上面是 append() 的实现,首先先解析 DOMStringfregment,然后就是遍历查询到的元素(通过 this 关键字)并针对每个元素去进行 appendChild() 的操作,从而把 DOM 插入到匹配到的所有元素中。

其他实例方法也是通过类似的方式实现的,这里就不一一细说了,想更详细的查看其他方法的实现可以直接到 Clus 中查看源码。

来源:https://segmentfault.com/a/1190000017750847

如何实现一个简化版的 jQuery的更多相关文章

  1. 一个强大的jquery分页插件

    点击这里查看效果 这个分页插件使用方便,引用keleyidivpager.js和keleyidivpager.css文件,然后在htm(或者php,aspx,jsp等)页面中对分页总数,参数名,前缀后 ...

  2. 推荐一个内容滚动jquery插件

    myslider是一个内容滚动jquery插件,版本0.1.2的每次滚动内容是一行内容,可以是文字,可以是一个链接,还可以是图片. 官方网址:http://keleyi.com/jq/myslider ...

  3. WebForms UnobtrusiveValidationMode 需要“jquery”ScriptResourceMapping。请添加一个名为 jquery (区分大小写)的 ScriptResourceMapping。

    WebForms UnobtrusiveValidationMode 需要“jquery”ScriptResourceMapping.请添加一个名为 jquery (区分大小写)的 ScriptRes ...

  4. (转)要“jquery”ScriptResourceMapping。请添加一个名为 jquery (区分大小写)的 ScriptResourceMapping。”的解决办法。

    要“jquery”ScriptResourceMapping.请添加一个名为 jquery (区分大小写)的 ScriptResourceMapping.”的解决办法. 1.先将aspnet.scri ...

  5. 编写一个简单的Jquery插件

    1.实现内容 定义一个简单的jquery插件,alert传递进来的参数 2.插件js文件(jquery.showplugin.js) (function ($) { //定义插件中的方法 var me ...

  6. 一个简单的jQuery插件开发实例

    两年前写的一个简单的jQuery插件开发实例,还是可以看看的: <script type="text/javascript" src="jquery-1.7.2.m ...

  7. 从零实现一个简易的jQuery框架之二—核心思路详解

    如何读源码 jQuery整体框架甚是复杂,也不易读懂.但是若想要在前端的路上走得更远.更好,研究分析前端的框架无疑是进阶路上必经之路.但是庞大的源码往往让我们不知道从何处开始下手.在很长的时间里我也被 ...

  8. WebForms UnobtrusiveValidationMode 需要“jquery”ScriptResourceMapping 异常详细信息: System.InvalidOperationException: WebForms UnobtrusiveValidationMode 需要“jquery”ScriptResourceMapping。请添加一个名为 jquery (区分大小写)的

    WebForms UnobtrusiveValidationMode 需要“jquery”ScriptResourceMapping.请添加一个名为 jquery (区分大小写)的 ScriptRes ...

  9. 制作一个简洁的jquery插件

    原文:http://mp.weixin.qq.com/s?__biz=MzAxMzgwNDU3Mg==&mid=401571467&idx=1&sn=08cb00963e6ef ...

随机推荐

  1. 谷歌protocolbuff使用说明步骤

    Protocolbuff 目录 1       Protocolbuff定义和作用... 1 2       Protocolbuff的使用步骤... 1 3       .proto编写格式... ...

  2. gcd表(欧几里得定理)

    题目:http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=797 gcd表 时间限制:1000 ms  |  内存限制:65535 KB 难度:3 ...

  3. mysql 日常操作-DDL

    1 修改字段类型 需求修改表字段类型 alter table  表名    modify column 修改的字段(列名) 类型(修改的类型) ALTER TABLE sys_entry_item m ...

  4. 2、记录代码----Ajax

    $.ajax({ url:'/content-engine/index.php/tracker/confirmSendEmail', async: false, //默认为true,同意异步传输 da ...

  5. Ngrinder 源码之Maven 项目

    Ngrinder支持Maven结构的测试脚本.使用ScriptHandlerFactory来个脚本选择处理器handler,目前有JythonScriptHandler, GroovyScriptHa ...

  6. 用Java实现一个二叉树

    介绍 使用Java实现一个int值类型的排序二叉树 二叉树 二叉树是一个递归的数据结构,每个节点最多有两个子节点. 通常二叉树是二分查找树,每个节点它的值大于或者等于在它左子树节点上的值,小于或者等于 ...

  7. JAVA静态数据的初始化;

    ①:Java首次会自动对变量进行初始化,其顺序优于构造器: ②:如果一个域是静态的的基本类型域,且也没有对它进行初始化,那么它就会获得基本类型的标准初值,如果它是一个对象引用,那么他的默认初始化值为n ...

  8. MySQL 存储过程 函数 routine 权限

    MySQL 存储过程 函数 routine 权限 Table of Contents 1. mysql存储过程/函数权限 1.1. 相关对象操作权限检查 1.2. 执行权限 1 mysql存储过程/函 ...

  9. SVN图标详解

    蓝色的加号 : 把这个文件已经添加到版本控制软件内 绿色的对勾 : 客户端和服务器端的代码一致 红色的叹号 : 客户端和服务器端两边的代码不一致 黄色的叹号 : 文件冲突 蓝色的问号 : 这个文件不在 ...

  10. VSCODE开发VUE.JS前端插件

    VUE前端插件.转载自:https://www.cnblogs.com/karthuslorin/p/8577224.html vscode是微软开发的的一款代码编辑器,就如官网上说的一样,vscod ...