封装函数
尝试将一个功能,封装为一个函数,利用函数的参数,将数据抽象化后,函数便拥有了更高的适用性。例如来封装一个获取某个节点的所有兄弟节点的函数:
1 | function getSibilings(node) { |
上面的示例中,把要找兄弟节点的那个节点,当作参数传入函数内,然后获取到它的父节点,再获取父节点的所有子元素集合,循环这个集合,排除目标节点后,将其余的保存为一个对象并返回。
命名空间
为了防止全局变量污染造成的麻烦,就要尽量少用全局变量,那么用什么办法能让要全局使用的属性不是全局变量呢?可以使用一个全局变量,把需要统一的全局属性,挂载到这个全局变量中即可,这就是命名空间。
简易 jQuery
在某个对象的原型上增加一个方法,那么这个对象的实例都会拥有这个方法。
修改原型一般是不推荐的,因为你不知道是否有别人是否会修改同名属性;这时可以新添加一个全局属性来补充原型。例如为
Node
对象的某个实例增加方法: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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47window.NodeUp = function (nodeOrSelector) {
var nodes = {length: 0};
if (typeof nodeOrSelector === 'string') {
nodeOrSelector = document.querySelectorAll(nodeOrSelector);
for (var i = 0; i < nodeOrSelector.length; i++) {
nodes[i] = nodeOrSelector[i];
nodes.length++;
}
} else if(nodeOrSelector instanceof Node) {
nodes[0] = nodeOrSelector;
nodes.length++;
}
nodes.addClass = function (classes) {
var isArr = Array.isArray(classes);
for (var i = 0; i < nodes.length; i++) {
if (!isArr) {
nodes[i].classList.add(classes);
} else {
for (var j = 0; j < classes.length; j++) {
nodes[i].classList.add(classes[j]);
}
}
}
};
nodes.setText = function (text) {
if (text) {
if (!(typeof text === 'string')) {
return;
}
for (var i = 0; i < nodes.length; i++) {
nodes[i].textContent = text;
}
} else {
var str;
for (var j = 0; j < nodes.length; j++) {
str += nodes[j].textContent;
}
return str;
}
}
return nodes;
};
window.$ = NodeUp;上面的简单例子中,如果把
NodeUp
改成jQuery
,可以吗?当然……参数接收一个字符串选择器或者 DOM 对象,就会返回一个新的对象,这个新对象函数中已经被添加了新的属性和方法;当传入某个参数使其实例化后,这个实例对象,就可以使用这些新属性和方法了。如果简化了代码可能会觉得哪里不对,其实是因为忽略了实例化时传入参数的过程,例如下面的简化例子:
1
2
3
4
5
6
7
8
9
10
11
12
13window.NodeUp = function (node) {
return {
next: function () {
return node.nextSibling;
},
addClass: function (class) {
node.classList.add(class);
}
}
}
var nodeUp = NodeUp(testObj);
nodeUp.addClass('test');上面代码中的
NodeUp
返回的是一个拥有两个方法的对象,那么这个对象是包装后的 DOM 对象吗?不是。当NodeUp
实例化时,不管有没有参数传入,实例化对象都可以使用这两个方法;只不过这两个方法又依赖于传进来的参数,也就是需要一个 DOM 对象,所以实例化后,就看起来好像是传入的这个 DOM 对象拥有了这两个方法,其实并不是,只是通过参数来告诉函数,调用这两个方法的时候,要施加给谁;而前面较为复杂的那段代码,才是真正的把方法添加给了包装后的 DOM 对象。要想证明这点很简单,上个例子中的对象可以剥离出 DOM 对象,而这个例子中的对象是不行的。jQuery 中的 set 和 get 两种类型的方法,大都是根据是否传参来判断执行何种操作的。
课后拾遗
jQuery 文件有三种,分别是:
以 .js 结尾:这种是未压缩的文件,又称为“开发版”;这种版本与源码没什么区别,注释、各种参数介绍都很齐全,是在开发的时候方便开发者“追根溯源”的,一旦有需要查看源代码,便可以找到相应的位置和注释。
以 .min.js 结尾:这种是压缩过的文件,又称为“线上版”或“生产版”;这种版本将代码中能缩短的标识符尽量缩短以精简代码,并删除了注释等信息,从而将文件体积压缩到最小,使下载时消耗更少的流量,下载速度更快。
以 .min.map 结尾:这种是索引文件(source map),一般与第一种配合使用,文件中记录了代码转换和压缩前的状态和位置信息,如果压缩后的文件运行时发生了错误,可以依靠这个文件进行排错;这个功能需要浏览器支持。
jQuery 对象和 DOM 对象的联系和区别:
联系:说是联系,其实是转换方法。将 DOM 对象传入 jQuery,可以把 DOM 对象变成 jQuery 对象,例如:
$(div)
,此时 jQuery 会把 DOM 对象“包装”(封装)成一个 jQuery 对象;而使用 jQuery 提供的get()
方法,或直接使用下标,就可以把 jQuery 对象变成 DOM 对象,例如:$div.get(0)
或$div[0]
,由于 jQuery 对象是一个伪数组,所以要通过下标 0 来获取第一个元素,即便其中只有一个元素;虽然只有一个元素时get()
方法不传参也能获取到,但这样获取到的和原对象是不一样的。区别:其实很简单,就是原型链的不同,所以它们拥有不同的 API,也因此不能混用。
拥有
id
属性的标签,在 JavaScript 中默认成为window
对象的属性,相当于一个全局变量;也正因为如此,所以id
的值不要与window
对象已有的属性重名;这也是尽量不用全局变量的原因之一。