尝试写一个极简的jQuery ,有1-2个功能,拢共分几步.
1步. 封装一个函数
此处选取实现2个功能:node.getSiblings()---获取指定节点的所有兄弟元素;node.addClass(classes)----给指定节点添加多个 className;
原始代码如下:
HTML:
<body>
<ul>
<li id="item1">选项1</li>
<li id="item2">选项2</li>
<li id="item3">选项3</li>
<li id="item4">选项4</li>
<li id="item5">选项5</li>
</ul>
</body>
JS:
//获取兄弟元素
var allChildren = item3.parentNode.children;
var array = {
length: 0,
};
for(let i = 0; i < allChildren.length; i++){
if(allChildren[i] !== item3){
array[array.length] = allChildren[i];
array.length = array.length + 1;
}
};
console.dir(array);
//添加 classNames
var classes = ['red','blue','yellow','green','orange'];
classes.forEach(function(value,key){
array[0].classList.add(value);
console.dir(array[0]);
});
将上面两个功能封装后如下:
function getSiblins(node){
var allChildren = node.parentNode.children;
var array = {
length: 0,
};
for(let i = 0; i < allChildren.length; i++){
if(allChildren[i] !== node){
array[array.length] = allChildren[i];
array.length = array.length + 1;
}
};
return array;
}
var nodeList = getSiblins(item2);
console.dir(nodeList);
var classes = ['red','blue','yellow','green','orange'];
function addClass(node,calsses){
classes.forEach(function(value,key){
node.classList.add(value);
});
};
addClass(item4,classes);
两步. 命名空间
在上面的步骤中,已经完成了函数的封装,已经可以通过调用封装好的函数来完成相应的功能了;接下来有个问题,如果有1024个功能,那就得封装1024个函数,声明1024个函数名,并且调用这些函数的代码内也不能出现这1024个变量名,否则会被覆盖掉........................而且这1024个函数名还没有共同特征....;
为了解决以上有关1024的问题,需要声明一个独特的,有分别性的空间;
比如创建一个 hash ,将所有的函数作为 键值对 存放到里头;
var ffdom = {};
ffdom.getSiblins = function(node){
var allChildren = node.parentNode.children;
var array = {
length: 0,
};
for(let i = 0; i < allChildren.length; i++){
if(allChildren[i] !== node){
array[array.length] = allChildren[i];
array.length = array.length + 1;
}
};
return array;
}
var nodeList = ffdom.getSiblins(item2);
console.dir(nodeList);
var classes = ['red','blue','yellow','green','orange'];
ffdom.addClass = function(node,calsses){
classes.forEach(function(value,key){
node.classList.add(value);
});
};
ffdom.addClass(item3,classes);
3步. 能不能把 node 放在前面
上面调用时格式为:ffdom.getSiblins(item2);
,ffdom.addClass(item3,classes);
,显然这样不符合我们习惯用法,
接下来要能这样使用:node.getSiblins();
,item3.addClass(classes);
最直接的思路就是直接将以上函数添加在 node 的原型链方法中;。
Node.prototype.getSiblins = function(){
var allChildren = this.parentNode.children;
var array = {
length: 0,
};
for(let i = 0; i < allChildren.length; i++){
if(allChildren[i] !== this){
array[array.length] = allChildren[i];
array.length = array.length + 1;
}
};
return array;
};
console.log(item1.getSiblins.call(item1));
var classes = ['red','blue','yellow','green','orange'];
Node.prototype.addClass = function(classes){
var node = this
classes.forEach(function(value,key){
console.log(this); // 此处this !=== this
node.classList.add(value);
});
};
item2.addClass.call(item2,classes);
----------------------
或者:
Node.prototype.addClass = function (classes) {
classes.forEach( (value) => this.classList.add(value) )
}
需要注意的点:
4步. 把 Node2 改个名字吧
步骤3存在一个问题:直接在Node.prototype里添加方法的话会比较乱,比如有1024个人在Node.prototype里添加方法,谁也不能保证不会重复、覆盖。
因此,再换一种方式;
window.Node2 = function(node){
return {
getSiblins: function(){
var allChildren = node.parentNode.children;
var array = {
length: 0,
};
for(let i = 0; i < allChildren.length; i++){
if(allChildren[i] !== node){
array[array.length] = allChildren[i];
array.length = array.length + 1;
}
};
return array;
},
addClass: function(classes){
classes.forEach(function(value,key){
node.classList.add(value);
});
}
}
};
var classes = ['red','blue','yellow','green','orange'];
node2 = Node2(item4);
node2.addClass(classes);
console.dir(node2.getSiblins());
console.dir(item4);
--------------------------------------------
如果:
window.jQuery = window.Node2;
则:
node2 =jQuery(item4);
node2.addClass(classes);
到这一步,在形式上已经比较像 jQuery 了。
不过 jQuery 的参数不仅可以传节点,还可以传选择器(即字符串);
实现如下:
window.jQuery = function(nodeOrSelector){
let node;
if(typeof nodeOrSelector === 'string'){
node = document.querySelector(nodeOrSelector);
}else{
node = nodeOrSelector;
}
return {
getSiblins: function(){
var allChildren = node.parentNode.children;
var array = {
length: 0,
};
for(let i = 0; i < allChildren.length; i++){
if(allChildren[i] !== node){
array[array.length] = allChildren[i];
array.length = array.length + 1;
}
};
return array;
},
addClass: function(classes){
classes.forEach(function(value,key){
node.classList.add(value);
});
}
}
};
var classes = ['red','blue','yellow','green','orange'];
node2 = jQuery('ul > li:nth-child(5)');
node2.addClass(classes);
console.dir(node2.getSiblins());
console.dir(item4)
在上面的代码中使用到了闭包,如下:
let node;
+
function(value,key){
node.classList.add(value);
}
//上面就是闭包
上面方法是对单个节点的操作,接下来要求对多个获取的节点操作,如下:
window.jQuery = function(nodeOrSelector){
let nodes = {};
if(typeof nodeOrSelector === 'string'){
let temp = document.querySelectorAll(nodeOrSelector);
for(let i = 0; i < temp.length; i++){//获取一个没有多余原型链的纯洁伪数组
nodes[i] = temp[i];
nodes.length = temp.length;
}
}else if(nodeOrSelector instanceof Node){
//获取一个伪数组,与上面保持同一体型
nodes = {
0: nodeOrSelector,
length: 1
}
}
nodes.addClass = function(classes){
classes.forEach(function(value,key){
for(let i = 0; i < nodes.length; i++){
nodes[i].classList.add(value);
}
});
},
nodes.text = function(text){
let contents = [];
for(let i = 0; i < nodes.length; i++){
if(text === undefined){
contents.push(nodes[i].textContent)
}else{
nodes[i].textContent = 'hi'
}
}
}
return nodes
};
var classes = ['red','blue','yellow','green','orange'];
var $nodes2 = jQuery('ul > li:nth-child(5)');
$nodes2.addClass(classes);
$node2.text('hi');
重点技巧---
如过用 jQuery 构造一个对象,那么这个对象的变量名的第一个标识符建议用 $。