jQuery原理(入口函数)
基本结构
jQuery的基本结构如下:
1 | (function (window, undefiend) { |
jQuery的本质是一个闭包
为了避免多个框架的冲突
jQuery如何让外界访问内部定义的局部变量
1
windows.xx = xxx
jQuery为什么要给自己传递一个window参数
- 为了方便后期压缩代码
- 为了提升查找的效率
jQuery为什么要给自己传递一个undefiend参数
- 为了方便后期压缩代码
- IE9以下的浏览器
undefined
可以被修改,为了保证内部使用的undefined
不被修改,所以需要接收一个正确的undefined
入口函数测试
传入
''
null
undefined
NaN
0
false
返回空的jQuery对象
1
2
3
4
5
6
7console.log($())
console.log($(''))
console.log($(null))
console.log($(undefined))
console.log($(NaN))
console.log($(0))
console.log($(false))字符串
代码片段
会将创建好的DOM元素存储到jQuery对象中返回
1
console.log($('<p>1</p><p>2</p><p>3</p>'));
选择器
会将找到的所有元素存储到jQuery对象中返回
1
console.log($('li'));
数组
会将数组中存储的元素依次存储到jQuery对象中立返回
1
2
3
4var arr = [1, 2, 3, 4, 5, 6];
console.log($(arr));
var likeArr = { 0: "lnj", 1: "33", 2: "male", length: 3 };
console.log($(likeArr));其他类型(对象、DOM元素、基本数据类型等)
会将传入的数据存储到jQuery对象中返回
1
2
3
4
5
6
7
8function Person() { }
console.log($(new Person()));
console.log($(document.createElement('div')));
console.log($(123));
console.log($(true));
入口函数-代码实现
接收参数的实现:在创建时接收一个参数,并且传递给init即可。这样init就可以接收这个参数。
1 | var kjQuery = function (selector) { |
接下来的方法只需要在init
中定义即可。
1 | kjQuery.prototype = { |
传入
''
null
undefined
NaN
0
false
1
2
3if (!selector) {
return this;
}处理函数
判断是否为函数,如果是则将传入的参数作为
ready
的回调函数传入。1
2
3isFunction: function (selector) {
return typeof selector === "function";
},1
2
3
4// 2. 方法处理
else if (kjQuery.isFunction(selector)) {
kjQuery.ready(selector);
}在
ready
函数中需要判断页面是否加载,由于IE不支持addEvem=ntListener
方法添加事件,因此判断页面是否加载只能使用readyState
判断页面加载状态。当页面加载完成后在进行添加事件的操作。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18ready: function (fn) {
// 判断DOM是否加载完毕
if (document.readyState == "complete") {
fn();
// 判断是否含有addEventListener方法
} else if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", function () {
fn();
});
} else {
// IE兼容
document.attachEvent("onreadystatechange", function () {
if ((document.readyState = "complete")) {
fn();
}
});
}
},字符串
在这里需要判断是否是字符串并且需要去掉字符串两端的空格。但判断是否为字符串后边可能也会用到,因此定义一个静态方法,通过类名调用。方便后边的操作
1
2
3kjQuery.isString = function (str) {
return typeof str === "string";
};去掉两端的空格在传入时去掉即可。同样定义一个静态方法
1
2
3
4
5
6
7
8
9
10
11
12
13// 0 去除字符串两端的空格
selector = kjQuery.trim(selector);
kjQuery.trim = function (str) {
if (!kjQuery.isString(str)) {
return str;
}
if (str.trim) {
return str.trim();
} else {
// IE兼容处理方案
return str.replace(/^\s+|\s+$/g, "");
}
};HTML代码
判断是否为HTML与判断字符串一样,后边也可能会用到,因此定义一个静态方法。
1
2
3
4
5
6
7kjQuery.isHTML = function (str) {
return (
str.charAt(0) == "<" &&
str.charAt(str.length - 1) == ">" &&
str.length >= 3
);
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17else if (kjQuery.isString(selector)) {
// 2.1 判断是否是代码片段
if (kjQuery.isHTML(selector)) {
// 1. 根据代码片段创建所有的元素
var temp = document.createElement("div");
temp.innerHTML = selector;
// 2. 创建好的一级元素添加到对象
for (var i = 0; i < temp.children.length; i++) {
this[i] = temp.children[i];
}
// 3. 给jQuery对象添加length属性
this.length = temp.children.length;
// 4. 返回加工好的this(jQuery)
return this;
}
// 2.2 判断是否是选择器
}其中第二三步可以修改为如下写法:
1
[].push.apply(this, temp.children);
通过apply改变push的this,这样就不会push到数组里,而是push到this(此时为kjQuery对象)中。
选择器
1
2
3
4
5
6
7
8
9// 2.2 判断是否是选择器
else {
// 1. 根据传入的选择器找到对应的元素
var res = document.querySelectorAll(selector);
// 2. 将找到的元素添加到kjQuery
[].push.apply(this, res);
// 3. 返回加工好的this
return this;
}
数组
注意:但凡将自定义数组转换为真数组或伪数组都先转换为真数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18else if (
typeof selector === "object" &&
"length" in selector &&
selector != window
) {
// 3.1 真数组
if ({}.toString.apply(selector) == "[object Array]") {
[].push.apply(this, selector);
return this;
}
// 3.2 伪数组
else {
// 将自定义的伪数组转换成真数组
var arr = [].slice.call(selector);
[].push.apply(this, arr);
return this;
}
}以上代码可优化为如下:
新增静态方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16kjQuery.isObject = function (selector) {
return typeof selector === "object";
};
kjQuery.isWindow = function (selector) {
return selector === window;
};
kjQuery.isArray = function (selector) {
if (
kjQuery.isObject(selector) &&
!kjQuery.isWindow(selector) &&
"length" in selector
) {
return true;
}
return false;
};1
2
3
4
5else if (kjQuery.isArray(selector)) {
var arr = [].slice.call(selector);
[].push.apply(this, arr);
return this;
}其他类型(对象、DOM元素、基本数据类型等)
1
2
3
4
5else {
this[0] = selector;
this.length = 1;
return this;
}
真伪数组转换
真数组转换为伪数组
1
2var obj = {},arr=[];
[].push.apply(obj,arr);伪数组转换为真数组
1
2var obj = {};
var arr = [].slice.call(obj)
入口函数-extend方法
通过extend方法来为对象或类添加方法。
调用
extend
方法,传入一个对象。1
2
3
4
5
6function kjQuery() { }
kjQuery.extend({
isTest: function () {
console.log('test');
}
})在
extend
方法的实现中,遍历传入的对象,并将值添加到类身上作为类的方法。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/* 为类和对象添加方法 */
kjQuery.extend = kjQuery.prototype.extend = function (obj) {
for (var key in obj) {
this[key] = obj[key];
}
};
/*
/* 为类添加方法 */
kjQuery.extend = function (obj) {
// this指向kjQuery
console.log(this);
for (var key in obj) {
// kjQuery[key] = obj[key]
this[key] = obj[key]
}
}
/* 为对象添加方法 */
kjQuery.extend.prototype = function (obj) {
// this指向实例化对象
console.log(this);
for (var key in obj) {
// kjQuery[key] = obj[key]
this[key] = obj[key]
}
}
*/通过类名调用方法
1
kjQuery.isTest()
通过对象调用方法
1
2
3
4
5
6
7var k = new kjQuery();
k.extend({
isDemo: function () {
console.log('demo')
}
})
k.isDemo()
因此可以将之前定义的静态方法改为如下写法:
1 | kjQuery.extend = kjQuery.prototype.extend = function (obj) { |
完整代码
至此入口函数部分编写已经完成,下边是工具库的全部代码。
1 | (function (window, undefiend) { |