jQuery的原理理解及仿写
先写段代码
var dom = {}
dom.getSiblings(node)
dom.addClass(node, {a: true, b: false})
dom就是命名空间,一个对象,对后面的封装和理解有帮助
<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>
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]; //不是数组没有push方法
array.length += 1
}
}
console.log(array)
上面的代码实现的效果是获得节点的兄弟姐妹
console.log
输出的是除了item3以外的其他兄弟节点,即item1, #item2, item4, #item5
接下来把代码进行封装
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
}
console.log(getSiblings(item3))
接下来增加实现删除添加class类功能的代码
var classes = {'a':true,'b':false,'c':true}
for( let key in classes ){
var value = classes[key]
if(value){
item3.classList.add(key)
}else{
item3.classList.remove(key)
}
}
我们通过审查元素item3就可以看到item3上多了a
和b
的class
类,
封装删除添加class类的代码
function addClass(node,classes) {
for (let key in classes) {
var value = classes[key]
if (value) {
node.classList.add(key)
} else {
node.classList.remove(key)
}
}
}
addClass(item3,{'a':true,'b':false,'c':true})
将上面两个方法代码合并整合,来实现同样的效果
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]; //不是数组没有push
array.length += 1
}
}
return array
}
function addClass(node,classes) {
for (let key in classes) {
var value = classes[key]
var methodName = value ? 'add': 'remove'
node.classList[methodName](key)
}
}
window.xzdom = {}
xzdom.getSiblings = getSiblings
xzdom.addClass = addClass
xzdom.getSiblings(item3)
xzdom.addClass(item3,{'a':true,'b':false,'c':true})
上面代码的最后五行就命名空间, 是一种设计模式
命名空间非常有必要,如果没有命名空间有两个缺点1.别人不知道叫什么2.会不知不觉覆盖全局变量。
而jQuery就是用了命名空间的模式来实现的。
下面正式引出了jQuery的设计原理
首先我们在原型上加,Node.prototype原型上加
<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>
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) )
} //移除class的功能取消了,保留了addclass功能
console.log( item3.getSiblings() )
// 等价于 console.log(item3.getSiblings.call(item3)) //this作为第一个参数
item3.addClass( ['a','b','c'] )
// 等价于 item3.addClass.call(item3, ['a','b','c'] ) //['a','b','c']作为第二个参数
这里实现了在node
原型上的添加class的功能,但是有个问题,它会覆盖全局变量的方法,比如原型上有getSiblings
这个方法或者属性,那么这里就会覆盖掉。
其次通过修改名称来保留原来的方法或属性---新建一个Node2(或者叫jQuery)
window.jQuery = function(node) { //window.Node2 = 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]; //不是数组没有push
array.length += 1
}
}
return array
},
addClass: function(classes) {
classes.forEach((value) => node.classList.add(value))
}
}
}
var node2 = jQuery(item3) //var node2 = Node2(item3)
console.log(node2.getSiblings())
node2.addClass(['a', 'b', 'c'])
代码就实现了在item3
上添加了a
,b
,c
三个class
类
Node2(jQuery)
接受一个旧的节点,然后返回一个新的对象,这个对象就是Node2(jQuery)
对象
下面加上选择器
.blue{color:blue}
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]; //不是数组没有push
array.length += 1
}
}
return array
},
addClass: function(classes) {
classes.forEach((value) => node.classList.add(value))
}
}
}
var node2 = jQuery('ul>li:nth-child(3)') // var node2 = jQuery('#item3')
console.log(node2.getSiblings())
node2.addClass(['blue', 'b', 'c'])
代码实现了在item3上添加blue
,b
,c
三个class
类,并且blue类有样式,并且item3
的li
变蓝
同时操作6个li
.blue{color:blue;}
<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>
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((value) => {
for (let i = 0; i < nodes.length; i++) {
nodes[i].classList.add(value)
}
})
}
nodes.getText = function() {
var texts = []
for (let i = 0; i < nodes.length; i++) {
texts.push(nodes[i].textContent)
}
return texts
}
nodes.setText = function(text) {
for (let i = 0; i < nodes.length; i++) {
nodes[i].textContent = text
}
}
return nodes
}
var node2 = jQuery('ul > li')
node2.addClass(['blue'])
console.log(node2.getText())
node2.setText('hi')
通过全局变量的jQuery
的setText
方法就把6个li
变蓝了
把getText
和setText
方法放在同一个函数封装
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
}
}
}
这里的设置文本内容和jQuery
很像,传一个参数,如果是undefined
,就获取元素本是的文本内容;如果有内容就替换文本内容,用用传进来的值覆盖。
我们可以看出 jQuery是函数(有括号,6种数据类型不符合,只能是对象里的函数这个类型符合)。并且是链式操作,因为上面的代码在node
的原型链上线指向了构造出来的jQuery
的原型,然后再指向Object.prototype
习俗:如果这个对象是由jQuery
构造出来的或者是$构造出来的,就在对象前面加$,表示它是jQuery
的对象