class
;DOM
的 API
实现比较繁琐,因此本身封装 API
;html
中有一个 ul
标签,在 ul
中有 5
个 li
。<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>
复制代码
id
为 item3
的兄弟元素。 首先定义一个 allChildren
变量来存储 item3
的父节点全部的子元素;var allChildren = item3.parentNode.children;
复制代码
而后定义一个空数组来存兄弟元素;html
var array = {length:0};
复制代码
item3
,那么就存到 array
伪数组中。for(let i = 0;i < allChildren.length;i++){
if(allChildren[i] !== item3){
array[array.length] = allChildren[i]; //数组下标一一对应存储 item 元素
array.length+=1;
}
}
复制代码
这个 array
是一个伪数组,它的原型链直接指向了 Object.protottype
并无指向 Array.prototype (只有原型链中指向 Array.prototype
的数组才是真正的数组。)node
return
这个数组,给咱们的函数加一个参数,而后调用这个函数同时传参item3
;function getSiblings(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+=1;
}
}
return array;
}
getSiblings(item3);
复制代码
class
的函数function addClass = function(node, classes){
classes.forEach( (value) => node.classList.add(value) );
};
addClass(item3, ['a','b','c']);
复制代码
调用这个函数同时传参,这样咱们就能够给 item3
添加3个 class
,分别为 a
, b
, c
设计模式
如今咱们有两个 API
了,可是它们看起来很分散,咱们有什么办法能让这两个 API
有关联呢? 咱们能够声明一个变量 window.reChenDom = {}
;数组
window.reChenDom = {};
reChenDom.getSiblings = 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+=1;
}
}
return array;
};
reChenDom.addClass = function(node, classes){
classes.forEach( (value) => node.classList.add(value) );
};
reChenDom.getSiblings(item3);
reChenDom.addClass(item3, ['a','b','c']);
复制代码
这就叫作命名空间,也是一种设计模式。命名空间是很是有必要的,若是没有命名空间,有两个缺点:第一是别人不知道你的库叫什么,另外一个是会不知不觉把全局对象给覆盖了。闭包
node
放在前面接下来第三个特色,咱们要用的时候特别麻烦,老是要:函数
reChenDom.getSiblings(item3);
reChenDom.addClass(item3, ['a','b','c']);
复制代码
能不能像下面这样每次使用都方便点呢ui
item3.getSiblings();
item3.addClass( ['a','b','c'] );
复制代码
有两种办法:this
Node
的原型(扩展 Node
接口直接在 Node.prototype
上加函数):Node.prototype.getSiblings = 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+=1;
}
}
return array;
};
Node.prototype.addClass = function( classes){
classes.forEach( (value) => this.classList.add(value) );
};
item3.getSiblings();
item3.addClass( ['a','b','c'] );
复制代码
this
就是 getSiblings()
和 item3.addClass( ['a','b','c'] )
被调用时前面的对象。 其实这个函数写的很差,为何呢?这样写是在改 Node
的属性,若是有其余人也这样写就会被覆盖。spa
Node2
改个名字吧jQuery
本身构造的一个函数,调用 Node
版本:window.jQuery = function(node){
return {
getSiblings : 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+=1;
}
}
return array;
},
addClass : function( classes){
classes.forEach( (value) => node.classList.add(value) );
}
}
}
var node2 = jQuery(item3);
node2.getSiblings();
node2.addClass( ['a','b','c'] );
复制代码
这样就不单单能够传 Node
,也能够传其它的,好比选择器:‘#item3’
,因此这个名字就不叫 node
了:prototype
window.jQuery = function(nodeOrSelector){
let node;
if( typeof nodeOrSelector === 'string'){
node = document.querySelector(nodeOrSelector);
}else{
node = nodeOrSelector;
};
return {
getSiblings : 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+=1;
}
}
return array;
},
addClass : function( classes){
classes.forEach( (value) => node.classList.add(value) );
}
}
}
var node2 = jQuery('#item3');
node2.getSiblings();
node2.addClass( ['a','b','c'] );
复制代码
在这里咱们用到了闭包,addClass
这个函数用到了 node
,这个 node
不是函数的内部声明,那这个node
是哪声明的呢?是在外面,若是一个函数用到了它外面的变量,那么 node
和这个匿名函数统称为闭包。
咱们的 jQuery
能不能再厉害一点呢?若是我想同时操做多个 li
,给这些 li
添加 class
,怎么办呢,这个时候就不能叫 node
了,要叫 nodes
,以前的结构已经不适用了。新结构:
window.jQuery = function (nodeOrSelector){
let nodes = {};
if( typeof nodeOrSelector === 'string'){
var 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( (value) => {
for(let i=0; i<nodes.length; i++){
nodes[i].classList.add(value);
};
});
};
return nodes;
};
var nodes = jQuery('ul>li')
nodes.addClass( ['red'] );
复制代码
alias
吧window.$ = window.jQuery
window.$ = function (nodeOrSelector){...}
var $nodes = $('ul>li') //在变量前加上一个 $, 防止变量弄混
复制代码
那如今还能够添加几个有用的 jQuery
的 API
,好比说获取/设置元素的文本:
window.$ = function (nodeOrSelector){
let $nodes = {};
if( typeof nodeOrSelector === 'string'){
var 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( (value) => {
for(let i=0; i<$nodes.length; i++){
$nodes[i].classList.add(value);
};
});
};
$nodes.text = function(text){
if( text === undefined ){
var texts = [];
for( let i=0; i<$nodes.length; i++){
texts.push( $nodes[i].textContent )
}
return texts;
}else{
for( let i=0; i<$nodes.length; i++){
$nodes[i].textContent = text;
}
}
}
return $nodes;
};
var $nodes = $('ul>li')
$nodes.addClass( ['red'] );
$nodes.text('hi'); //若是不给参数说明是获取text,若是给了一个参数说明是设置text
复制代码
这样咱们就从封装两个函数到实现了简化版的 jQuery
,添加 class
和获取/设置文本的 API
。