用过Jquery的朋友应该都体会过他的链式调用的强大,实现多个功能只需要链式调用方法即可实现,方便而简洁,这篇博客就介绍一下实现链式调用的方式,这里我会讲不止一种书写方式,希望大家可以根据其中的差异学到其精髓

jquery链式调用实现一

先让我们来看一下jquery链式调用是怎么写的

1
2
$(".box").addClass("ball1").addClass("ball2").removeClass(".ball1");
// 以上功能找到类名为 box 的元素添加一个类名 ball1 > 添加一个类名 ball2 > 添加删除一个类名 ball1

分析:从上面的代码我们不难看出实现jquery链式调用的本质是每一个方法都返回一个对象,这个对象包含特定属性和所有对象共有的方法

根据上面的分析我们先来实现 $ 获取元素对象集合的方法

1
2
3
4
5
6
// $ 方法的作用是获取dom元素集合,这里我们不能够知道会获取一个还是多个dom,所以要统一就要一律使用集合
window.$ = function(selector){
var doms = document.querySelectorAll(selector); // 获取 DOM 集合这是一个类数组
var domsArr = Array.prototype.slice.call(doms,0); // 将类数组装换成数组
return domsArr; // 返回这个 dom 集合数组
}

现在我们已经实现了一个 $ 方法,这个方法返回的是一个数组,数组说白了也是一个对象,是对象就可以绑定属性和方法,我们之前说过每一个方法都要返回相同或者类似的对象才能实现链式调用,那么这 $ 方法返回的这个数组实例是需要包含所有的jquery方法的,那么这些方法要放在哪里才能让所有的数组实例都能访问到呢?

这个问题如果对javascript面向对象还算了解的同学应该能脱口而出:原型对象!

现在我们来实现一个 addClass 方法和 removeClass 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 实现 addClass 方法
Array.prototype.addClass = function(className){
this.forEach(dom => {
dom.classList.add(className);
});
return this;
}

// 实现 removeClass 方法
Array.prototype.removeClass = function(className){
this.forEach(dom => {
dom.classList.remove(className);
});
return this;
}

jquery 链式调用实现二

可以看到第一种实现的方式通过在原生数组对象的原型对象上添加方法来实现链式调用,功能我们的确是实现了,但是他有几个缺点,一个是通常来讲我们不建议在系统对象的原型上做修改,这样容易导致不可控的情况出现;第二就是jquery本身没有一个对象类型,这在做类型判断的时候是不好的,结构上所有的功能都寄生在原生数组对象上;

那么现在我们要做的就是使用一个专门的 jquery 对象作为每个方法的返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
window.$ = function(selector){
var doms = document.querySelectorAll(selector); // 获取 DOM 集合这是一个类数组
var domsArr = Array.prototype.slice.call(doms,0); // 将类数组装换成数组
return new Jquery(domsArr);
}

// 用来创建 JQuery 类型对象的构造函数
function Jquery(domsArr){
this.domsArr = domsArr;
}
// 向对象中添加方法
// 实现 addClass 方法
Jquery.prototype.addClass = function(className){
this.domsArr.forEach(dom => {
dom.classList.add(className);
});
return this;
}

// 实现 removeClass 方法
Jquery.prototype.removeClass = function(className){
this.domsArr.forEach(dom => {
dom.classList.remove(className);
});
return this;
}