导读:
- 本篇文章用于处在jQuery进阶阶段的小伙伴,通过阅读官方原版jQuery库部分重要方法,通过自己动手封装加深对库底层源码的理解同时掌握基本库的封装思路(立即执行的闭包),这对以后模块化封装和链式编程都有较大的提升。
源码实现:
(function(window, undefined) {
var wjQuery = function(selector) {
return new wjQuery.prototype.init(selector);
}
wjQuery.prototype = {
constructor: wjQuery,
init: function(selector) {
/*
1.传入 '' null undefined NaN 0 false 返回空jQuery对象
2.字符串:
代码片段:会将创建好的DOM元素存储到jQuery对象中返回
选择器:会将找到的所有元素存储到jQuery对象中返回
3.数组:
会将数组中存储的元素依次存储到jQuery对象中返回
4.除上述类型以外:
会将传入的数据存储到jQuery对象中返回
*/
// 0. 去除字符串两端的空格
selector = wjQuery.trim(selector);
if(!selector) {
return this;
}
// 函数
else if(wjQuery.isFunction(selector)) {
// this[0] = selector;
wjQuery.ready(selector);
}
// 字符串
else if(wjQuery.isString(selector)) {
//判断是不是代码片段
if(wjQuery.isHTML(selector)) {
// 1.根据代码片段创建元素
var tmp = document.createElement('div');
tmp.innerHTML = selector;
// 2.将创建好的一级元素返回到jQuery对象中,并添加length属性
[].push.apply(this, tmp.children);
// 3.返回加工好的this(jQuery), 调用init()方法本身就会返回一个值, 所以return this可写可不写这里语义化写上
// return this;
} else {
// 根据传入的选择器找到所有元素
var res = document.querySelectorAll(selector);
[].push.apply(this, res);
// return this;
}
}
// 数组
else if(wjQuery.isArray(selector)) {
// 真数组
/*if(({}).toString.apply(selector) === "[object Array]") {
[].push.apply(this, selector);
return this;
} else {
// 伪数组转真数组
var arr = [].slice.call(selector);
// console.log(arr);
[].push.apply(this, arr);
return this;
} */
// 优化后:
var arr = [].slice.call(selector);
[].push.apply(this, arr);
// return this;
}
// 除上述以外
else {
this[0] = selector;
this.length = 1;
}
return this;
},
jQuery: "1.0.0",
selector: "",
length: 0,
// 由wjQuery对象调用故省略this指向
push: [].push,
sort: [].sort,
splice: [].splice,
toArray: function() {
return [].slice.call(this);
},
get: function(num) {
if(arguments.length == 0) {
return this.toArray();
} else if(num >= 0) {
return this[num];
} else {
return this[this.length + num];
}
},
eq: function(num) {
var obj = {
prevObject: this.toArray(),
};
if(arguments.length != 0) {
return wjQuery(this.get(num));
}
return obj;
},
first: function() {
return this.eq(0);
},
last: function() {
return this.eq(-1);
},
each: function(fn) {
return wjQuery.each(this, fn);
}
}
wjQuery.extend = wjQuery.prototype.extend = function(obj) {
// 通过静态方法调用和实例方法调用统一
for(var key in obj) {
this[key] = obj[key];
}
};
// 工具方法
wjQuery.extend({
isString : function(str) {
return typeof str === 'string';
},
isHTML : function(str) {
return (str.charAt(0) == '<' && str.charAt(str.length - 1) == '>' && str.length >= 3);
},
trim : function(str) {
if(!wjQuery.isString(str)) return str;
if(str.trim) return str.trim();
else return str.replace(/^\s+|\s+$/g, "");
},
isArray : function(ele) {
if(wjQuery.isObject(ele) && !wjQuery.isWindow(ele) && 'length' in ele) return true;
},
isObject : function(ele) {
return typeof ele === 'object';
},
isWindow : function(ele) {
return ele === window;
},
isFunction : function(ele) {
return typeof ele === 'function';
},
ready: function(fn) {
// 判断DOM是否加载完成
if(document.readyState == 'complete') {
fn();
} else if(document.addEventListener) {
document.addEventListener('DOMContentLoaded', function() {
fn();
});
} else {
document.attachEvent('onreadystatechange', function() {
if(document.readyState == 'complete') {
fn();
}
});
}
},
each: function(obj, fn) {
// 判断是不是数组
if(wjQuery.isArray(obj)) {
for(var i = 0; i < obj.length; i++) {
var res = fn.call(obj[i], i, obj[i]);
if(res === true) continue;
else if(res === false) break;
}
} else if(wjQuery.isObject(obj)){
for(var key in obj) {
var res = fn.call(obj[key], key, obj[key]);
if(res === true) continue;
else if(res === false) break;
}
}
return obj;
},
map: function(obj, fn) {
var res = [];
if(wjQuery.isArray(obj)) {
for(var i = 0; i < obj.length; i++) {
var tmp = fn(obj[i], i);
if(tmp) res.push(tmp);
}
} else if(wjQuery.isObject(obj)) {
for(var key in obj) {
var tmp = fn(obj[key], key);
if(tmp) res.push(tmp);
}
}
return res;
},
get_nextsibling: function(ele) {
var n = ele.nextSibling;
if(n != null && n.nodeType != 1) {
n = n.nextSibling;
}
return n;
},
get_previoussibling: function(ele) {
var n = ele.previousSibling;
if(n != null && n.nodeType != 1) {
n = n.previousSibling;
}
return n;
},
getStyle: function(dom, styleName) {
if(window.getComputedStyle) {
return window.getComputedStyle(dom)[styleName];
} else {
return dom.currentStyle[styleName];
}
}
});
// DOM操作相关方法
wjQuery.prototype.extend({
empty: function() {
// 遍历所有找到的元素
this.each(function(key,value) {
value.innerHTML = "";
});
// 方便链式编程
return this;
},
remove: function(selector) {
if(arguments.length == 0) {
// 遍历指定的元素
this.each(function(key, value) {
// 根据遍历到的元素找到指定的父元素
var parent = value.parentNode;
// 通过父元素删除指定元素
parent.removeChild(value);
});
} else {
var $this = this;
// 根据传入的选择器找到对应的元素
$(selector).each(function(key, value) {
// 遍历找到的元素,获取对应的类型
var type = value.tagName;
$this.each(function(k, v) {
var t = v.tagName;
if(t == type) {
var parent = value.parentNode;
parent.removeChild(value);
}
});
});
}
return this;
},
html: function(content) {
if(arguments.length == 0) {
return this[0].innerHTML;
} else {
this.each(function(key, value) {
value.innerHTML = content;
});
}
},
text: function(content) {
if(arguments.length == 0) {
var res = "";
this.each(function(key, value) {
});
return res;
} else {
this.each(function(key, value) {
value.innerText = content;
})
}
},
appendTo: function(selector) {
// 遍历取出所有指定添加到的元素
var target = $(selector);
var res = [];
$.each(target, function(key, value) {
// 遍历取出素有被添加的元素
this.each(function(k, v) {
// 判断是否是第0指定的个元素
if(key == 0) {
value.appendChild(v);
res.push(v);
} else {
// 先拷贝在添加
var temp = v.cloneNode(true);
value.appendChild(temp);
res.push(temp);
}
})
});
return $(res);
},
prependTo: function(selector) {
// 遍历取出所有指定添加到的元素
var target = $(selector);
var source = this;
var res = [];
$.each(target, function(key, value) {
// 遍历取出素有被添加的元素
source.each(function(k, v) {
// 判断是否是第0指定的个元素
if(key == 0) {
value.insertBefore(v, value.firstChild);
res.push(v);
} else {
// 先拷贝在添加
var temp = v.cloneNode(true);
value.insertBefore(temp, value.firstChild);
res.push(temp);
}
})
});
return $(res);
},
append: function(selector) {
// 判断传入的参数是否是字符串
if(wjQuery.isString(selector)) {
this[0].innerHTML += selector;
} else {
$(selector).appendTo(this);
}
return this;
},
prepend: function(selector) {
// 判断传入的参数是否是字符串
if(wjQuery.isString(selector)) {
this[0].innerHTML = selector + this[0].innerHTML;
} else {
$(selector).prependTo(this);
}
return this;
},
insertBefore: function(selector) {
// 遍历取出所有指定添加到的元素
var target = $(selector);
var source = this;
var res = [];
wjQuery.each(target, function(key, value) {
// 遍历取出素有被添加的元素
var parent = value.parentNode;
source.each(function(k, v) {
// 判断是否是第0指定的个元素
if(key == 0) {
parent.insertBefore(v, value);
res.push(v);
} else {
// 先拷贝在添加
var temp = v.cloneNode(true);
parent.insertBefore(temp, value);
res.push(temp);
}
})
});
return $(res);
},
insertAfter: function(selector) {
// 遍历取出所有指定添加到的元素
var target = $(selector);
var source = this;
var res = [];
$.each(target, function(key, value) {
// 遍历取出素有被添加的元素
var parent = value.parentNode;
var sibling = value.nextSibling;
source.each(function(k, v) {
if(sibling == null) {
// 判断是否是第0指定的个元素
if(key == 0) {
parent.appendChild(v);
res.push(v);
} else {
// 先拷贝在添加
var temp = v.cloneNode(true);
parent.appendChild(temp);
res.push(temp);
}
} else {
// 判断是否是第0指定的个元素
if(key == 0) {
parent.insertBefore(v, sibling);
res.push(v);
} else {
// 先拷贝在添加
var temp = v.cloneNode(true);
parent.insertBefore(temp, sibling);
res.push(temp);
}
}
})
});
return $(res);
},
replaceAll: function(selector) {
// 遍历取出所有指定添加到的元素
var target = $(selector);
var res = [];
$.each(target, function(key, value) {
// 遍历取出素有被添加的元素
var parent = value.parentNode;
this.each(function(k, v) {
// 判断是否是第0指定的个元素
if(key == 0) {
$(v).insertBefore(value);
$(value).remove();
res.push(v);
} else {
// 先拷贝在添加
var temp = v.cloneNode(true);
$(temp).insertBefore(value);
//删除指定元素
$(value).remove();
res.push(temp);
}
})
});
return $(res);
},
clone: function(boolean) {
var res = [];
if(boolean) {
// 深复制
this.each(function(key, value) {
var temp = value.cloneNode(true);
// 遍历元素中的eventsCache对象
$.each(value.eventsCache, function(name, array) {
// 遍历每个事件对应的数组
$.each(array, function(index, method) {
// 给复制的元素添加事件
temp.on(name, method);
});
});
res.push(temp);
});
} else {
// 浅复制
this.each(function(key, value) {
var temp = value.cloneNode(true);
res.push(temp);
});
}
return res;
}
});
// 筛选相关方法
wjQuery.prototype.extend({
next: function(selector) {
var res = [];
// 返回所有找到的
if(arguments.length == 0) {
this.each(function(key, value) {
var temp = wjQuery.get_nextsibling(value);
if(temp != null) res.push(temp);
});
} else {
// 返回指定找到的
this.each(function(key, value) {
var temp = wjQuery.get_nextsibling(value);
$(selector).each(function(k, v) {
if(v !== temp || v == null) return true;
res.push[v];
});
});
}
return $(res);
},
pre: function(selector) {
}
});
// 属性操作相关方法
wjQuery.prototype.extend({
attr: function(attr, value) {
// 先判断传入的是对象还是字符串
if($.isString(attr)){
if(arguments.length == 1) {
return this[0].getAttribute(attr);
} else {
this.each(function(key, ele){
ele.setAttribute(attr, value);
});
}
} else if($.isObject(attr)) {
// 遍历取出所有属性节点名称及对应值
for(var key in attr) {
// 遍历取出所有标签
this.each(function(k, v) {
v.setAttribute(key, attr[key]);
});
}
}
return this;
},
prop: function(attr, value) {
// 先判断传入的是对象还是字符串
if($.isString(attr)){
if(arguments.length == 1) {
return this[0][attr];
} else {
this.each(function(key, ele){
ele[attr] = value;
});
}
} else if($.isObject(attr)) {
// 遍历取出所有属性节点名称及对应值
for(var key in attr) {
// 遍历取出所有标签
this.each(function(k, v) {
v[key] = attr[key];
});
}
}
return this;
},
css: function(attr, value) {
// 先判断传入的是对象还是字符串
if($.isString(attr)){
if(arguments.length == 1) {
return wjQuery.getStyle(this[0], attr);
} else {
this.each(function(key, ele){
ele.style[attr] = value;
});
}
} else if($.isObject(attr)) {
// 遍历取出所有属性节点名称及对应值
for(var key in attr) {
// 遍历取出所有标签
this.each(function(k, v) {
v.style[key] = attr[key];
});
}
}
return this;
},
val: function(content) {
if(arguments.length == 0) {
return this[0].value;
} else {
this.each(function(key, ele) {
ele.value = content;
});
return this;
}
},
hasClass: function(name) {
var flag = false;
if(arguments.length == 0) return flag;
else {
this.each(function(key, value) {
// 取出每个元素的class属性值并在两端加上空格便于判断
var className = " " + value.className + " ";
name = " " + name + " ";
if(className.indexOf(name) != -1) {
flag = true;
return false;
}
})
}
return flag;
},
addClass: function(name) {
var names = name.split(" ");
if(arguments.length == 0) return this;
else {
// 遍历取出每一个元素
this.each(function(key, value) {
// 遍历取出每一个类名
$.each(names, function(k, v) {
if(!$(value).hasClass(v)) value.className += " " + v;
});
});
}
return this;
},
removeClass: function(name) {
if(arguments.length == 0) {
this.each(function(key, value) {
value.className = "";
});
} else {
var names = name.split(" ");
// 遍历取出每一个元素
this.each(function(key, value) {
// 遍历取出每一个类名
$.each(names, function(k, v) {
if($(value).hasClass(v)){
value.className = (" "+value.className+" ").replace(" "+v+" ", "");
}
});
});
}
return this;
},
toggleClass: function(name) {
// 如果不传参则删除所有
if(arguments.length == 0) this.removeClass();
else {
var names = name.split(" ");
// 遍历取出每一个元素
this.each(function(key, value) {
// 遍历取出每一个类名
$.each(names, function(k, v) {
if($(value).hasClass(v)){
// 如果有就删除
$(value).removeClass(v);
} else {
$(value).addClass(v);
}
});
});
}
return this;
}
})
// 事件操作相关方法
wjQuery.prototype.extend({
on: function(name, callBack) {
this.each(function(key, ele) {
// 判断有没有保存所有事件类型的对象
if(!ele.eventsCache) {
ele.eventsCache = {};
}
// 判断有没有保存每个对应事件类型的事件的数组
if(!ele.eventsCache[name]) {
ele.eventsCache[name] = [];
ele.eventsCache[name].push(callBack);
if(ele.addEventListener) {
ele.addEventListener(name, function(){
for(var i = 0; i < ele.eventsCache[name].length; i++) {
ele.eventsCache[name][i]();
}
});
} else {
ele.attachEvent("on"+name, function(){
for(var i = 0; i < ele.eventsCache[name].length; i++) {
ele.eventsCache[name][i]();
}
});
}
} else {
ele.eventsCache[name].push(callBack);
}
})
},
off: function(name, callBack) {
if(arguments.length == 0) {
this.each(function(key, ele) {
ele.eventsCache = {};
});
} else if(arguments.length == 1) {
this.each(function(key, ele) {
ele.eventsCache[name] = [];
});
}else if(arguments.length == 2) {
this.each(function(key, ele) {
$.each(ele.eventsCache[name], function(index, method) {
// 判断传入的参数和数组中存的参数是否相同
if(method == callBack) ele.eventsCache[name].splice(index, 1);
});
});
}
}
})
wjQuery.prototype.init.prototype = wjQuery.prototype;
window.wjQuery = window.$ = wjQuery;
})(window)
总结:
- 以上源码需对JavaScript一些方法有较深的理解,否则在阅读时可能会晕头转向的,最后希望以上源码能对正在学习jQuery源码并尝试自己动手封装的小伙伴提供一个参考思路。
网友评论