如何实现一个简化版的 jQuery
对于操作 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()
的实现,首先先解析 DOMString
为 fregment
,然后就是遍历查询到的元素(通过 this
关键字)并针对每个元素去进行 appendChild()
的操作,从而把 DOM
插入到匹配到的所有元素中。
其他实例方法也是通过类似的方式实现的,这里就不一一细说了,想更详细的查看其他方法的实现可以直接到 Clus 中查看源码。
来源:https://segmentfault.com/a/1190000017750847
如何实现一个简化版的 jQuery的更多相关文章
- 一个强大的jquery分页插件
点击这里查看效果 这个分页插件使用方便,引用keleyidivpager.js和keleyidivpager.css文件,然后在htm(或者php,aspx,jsp等)页面中对分页总数,参数名,前缀后 ...
- 推荐一个内容滚动jquery插件
myslider是一个内容滚动jquery插件,版本0.1.2的每次滚动内容是一行内容,可以是文字,可以是一个链接,还可以是图片. 官方网址:http://keleyi.com/jq/myslider ...
- WebForms UnobtrusiveValidationMode 需要“jquery”ScriptResourceMapping。请添加一个名为 jquery (区分大小写)的 ScriptResourceMapping。
WebForms UnobtrusiveValidationMode 需要“jquery”ScriptResourceMapping.请添加一个名为 jquery (区分大小写)的 ScriptRes ...
- (转)要“jquery”ScriptResourceMapping。请添加一个名为 jquery (区分大小写)的 ScriptResourceMapping。”的解决办法。
要“jquery”ScriptResourceMapping.请添加一个名为 jquery (区分大小写)的 ScriptResourceMapping.”的解决办法. 1.先将aspnet.scri ...
- 编写一个简单的Jquery插件
1.实现内容 定义一个简单的jquery插件,alert传递进来的参数 2.插件js文件(jquery.showplugin.js) (function ($) { //定义插件中的方法 var me ...
- 一个简单的jQuery插件开发实例
两年前写的一个简单的jQuery插件开发实例,还是可以看看的: <script type="text/javascript" src="jquery-1.7.2.m ...
- 从零实现一个简易的jQuery框架之二—核心思路详解
如何读源码 jQuery整体框架甚是复杂,也不易读懂.但是若想要在前端的路上走得更远.更好,研究分析前端的框架无疑是进阶路上必经之路.但是庞大的源码往往让我们不知道从何处开始下手.在很长的时间里我也被 ...
- WebForms UnobtrusiveValidationMode 需要“jquery”ScriptResourceMapping 异常详细信息: System.InvalidOperationException: WebForms UnobtrusiveValidationMode 需要“jquery”ScriptResourceMapping。请添加一个名为 jquery (区分大小写)的
WebForms UnobtrusiveValidationMode 需要“jquery”ScriptResourceMapping.请添加一个名为 jquery (区分大小写)的 ScriptRes ...
- 制作一个简洁的jquery插件
原文:http://mp.weixin.qq.com/s?__biz=MzAxMzgwNDU3Mg==&mid=401571467&idx=1&sn=08cb00963e6ef ...
随机推荐
- Pycharm查看源代码的问题
linear = torch.nn.Linear() # 无法查看Linear的源代码 from torch.nn import Linear linear = Linear() # 可以查看Line ...
- PL/SQL中文乱码问题
本人的使用情况是: 笨既没有安装Oracle,配置了PLSQL,在查询含有中文数据的时候遇到了查询的数据中有中文乱码的问题,中文显示为???,并且软件的某些提示也会出现. 先进行查询确认编码: ...
- tensorflow源码分析——BasicLSTMCell
BasicLSTMCell 是最简单的LSTMCell,源码位于:/tensorflow/contrib/rnn/python/ops/core_rnn_cell_impl.py.BasicLSTMC ...
- rocketmq的linux搭建环境
3.3. 上传解压[两台机器] # 上传 apache-rocketmq.tar.gz 文件至/usr/local # tar -zxvf apache-rocketmq.tar.gz -C /usr ...
- DP&图论 DAY 5 上午
DP&图论 DAY 5 上午 POJ 1125 Stockbroker Grapevine 有 N 个股票经济人可以互相传递消息,他们之间存在一些单向的通信路径.现在有一个消息要由某个人开 ...
- [windows菜鸟]C#中调用Windows API的技术要点说明
在.Net Framework SDK文档中,关于调用Windows API的指示比较零散,并且其中稍全面一点的是针对Visual Basic .net讲述的.本文将C#中调用API的要点汇集如下,希 ...
- Android Stuido中断点调试和高级调试
写一个简单的调试程序 import android.os.Bundle; import android.support.v7.app.AppCompatActivity; public class M ...
- 【巨坑】springmvc 输出json格式数据的几种方式!
最近公司项目需要发布一些数据服务,从设计到实现两天就弄完了,心中窃喜之. 结果临近部署时突然发现..... 服务输出的JSON 数据中 date 类型数据输出格式要么是时间戳,要么是 {&quo ...
- python3.6安装jpype1后引入jpype报“ImportError: numpy.core.multiarray failed to import”问题
jpype是调用java接口的第三方库,通过该库,python可以运行java程序,从而解决一些调用java的问题,比如:java开发的接口,测试时, 有java的加密算法就不用python写一遍重复 ...
- openstack共享组件--rabbitmq消息队列(1)
一.MQ 全称为 Message Queue, 消息队列( MQ ) 是一种应用程序对应用程序的通信方法.应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们. 消息传 ...