最近学习到了jQuery的内容,从DOM到jQuery之间,到底发生了什么变化,怎么从DOM转变到jQuery,他们之间存在什么联系和区别,这篇博文梳理一下初入门学习jQuery的一些思路和分析过程。
假设现在我们有这样的一个HTML结构:
<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>
.red {
color: red
}
.fontSize {
font-size: 20px;
}
1.用DOM获取一个元素节点并设置样式
item3.classList.add('red')
item3.classList.add('fontSize')
2.进行函数封装
给代码进行封装,提高代码复用率和健壮性,同时加入判断:如果传入的参数是一个字符串,那就为这个元素添加样式;如果传入的参数是一个数组,那么就为这个元素增加这个数组中的样式。
function addClass(node,classes){
if(typeof classes === 'string'){
node.classList.add(classes)
}else if(Array.isArray(classes)){
for(let i=0;i<classes.length;i++){
node.classList.add(classes[i])
}
}else{
alert('请输入一个字符串或者数组来表示你所要设置的样式名称')
}
}
addClass(item3,['red','fontSize'])
addClass(item3,'red')
命名空间
关于命名空间更详细的文章。
由于JavaScript没有文件作用域,不同的函数分散在不同的文件中,甚至由不同的人编写,重名的概率大大增加。是不是足够小心就可以了呢?也不尽然,还有些意外情况,比如经常会用到继承,于是写了一个没出现过的函数名extend,不料在EcmaScript5中加入了extend函数,命名空间的必要性就体现出来了。
JavaScript有函数的作用域,可以利用这点把自定义的函数写到一个函数体内,这样函数内的变量、对象、函数就像在一个命名空间内一样和外部隔离。
我们给刚刚创建的函数一个命名空间,以避免和其他的函数起冲突:
window.dom = {}
dom.addClass = addClass
dom.addClass(item3,['red','fontSize'])
更直观的使用方式
但是,为了更加直观和符合语法认知,如果我们要实现类似于这种方法:
item3.addClass(['red','fontSize'])
该怎么办呢?
两种方法:
- 将这个方法添加进公有属性(prototype)中
Node.prototype = function addClass(node,classes){
if(typeof classes === 'string'){
node.classList.add(classes)
}else if(Array.isArray(classes)){
for(let i=0;i<classes.length;i++){
node.classList.add(classes[i])
}
}else{
alert('请输入一个字符串或者数组来表示你所要设置的样式名称')
}
}
item3.addClass(['red','fontSize'])
// 等价于 item3.addClass.call(item3,['red','fontSize'])
缺点:容易覆盖原型中的方法,造成混乱。
- 改造成构造函数,返回一个拥有自定义方法的对象
window.Node2 = function(node){
return {
addClass: function(node,classes){
if(typeof classes === 'string'){
node.classList.add(classes)
}else if(Array.isArray(classes)){
for(let i=0;i<classes.length;i++){
node.classList.add(classes[i])
}
}else{
alert('请输入一个字符串或者数组来表示你所要设置的样式名称')
}
}
}
}
var node2 = Node2(item3)
node2.addClass(['red','fontSize'])
node2通过传入一个参数给Node2的函数,返回了一个新的对象,这个对象里面有我们自己定义的方法,然后我们就可以使用这些方法和属性操作这个节点。
我们再把命名改一下,发现他越来越接近jQuery了:
window.jQuery = function(node){
return {
addClass: function(node,classes){
if(typeof classes === 'string'){
node.classList.add(classes)
}else{
classes.forEach((value) => {
node.classList.add(value)
})
}
}
}
}
}
var node2 = jQuery(item3)
node2.addClass(['red','fontSize'])
这就是怎么从DOM改造成jQuery的思路,jQuery操作比DOM更方便,效率更高。
优化代码
接下来优化一下代码:如果我们接收的参数不是一个id选择器,而是一个字符串呢?(如我们不只要给item3添加样式,而是要给这个ul里全部的li添加样式,该怎么做呢?)
window.jQuery = function(nodesOrSelector){
let nodes = {}
//判断传入参数是否为字符串
if (typeof nodesOrSelector === 'string'){
let temp = document.querySelectiorAll(nodesOrSelector)
for (let i = 0;i<temp.length;i++){
nodes[i] = temp[i]
}
//如果传入的参数是一个节点
} else if(nodeOrSelector instanceof Node){
nodes = {0:nodeOrSelector,length:1}
}
nodes.addClass = function(classes){
if(typeof classes === 'string'){
for(let i=0;i<nodes.length;i++){
nodes[i].classList.add(classes)
}
}else if(Array.isArray(classes)){
classes.forEach((value) => {
for(let i = 0;i<nodes.length;i++){
nodes[i].classList.add(value)
}
})
}else{
alert('请输入一个字符串或者数组来表示你所要设置的样式名称')
}
}
return nodes
}
var node2 = jQuery('ul > li')
node2.addClass(['red','fontSize'])
小结
这就是jQuery的模式:jQuery实际上是一个构造函数,接受一个参数,这个参数有可能是节点,然后返回一个方法对象去操作节点。(接受一个元素,生成一个新的对象,这个新的对象有新的API)