30·灵魂前端工程师养成-jQuery中的设计模式

2022-09-26 17:02:07 浏览数 (1)

  • 用jQuery风格重新封装
  • 实现find函数
  • 实现end函数
  • 什么?你嫌jQuery太长?

-曾老湿, 江湖人称曾老大。


-多年互联网运维工作经验,曾负责过大规模集群架构自动化运维管理工作。 -擅长Web集群架构与自动化运维,曾负责国内某大型金融公司运维工作。 -devops项目经理兼DBA。 -开发过一套自动化运维平台(功能如下): 1)整合了各个公有云API,自主创建云主机。 2)ELK自动化收集日志功能。 3)Saltstack自动化运维统一配置管理工具。 4)Git、Jenkins自动化代码上线及自动化测试平台。 5)堡垒机,连接Linux、Windows平台及日志审计。 6)SQL执行及审批流程。 7)慢查询日志分析web界面。


用jQuery风格重新封装


创建项目

创建一个dom-2项目

使用 VScode 打开

index.html

代码语言:javascript复制
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>曾老湿 手写 jQuery</title>
</head>
<body>
    你好
    <script src="jquery.js"></script>
    <script src="main.js"></script>
</body>
</html>

jquery.js

代码语言:javascript复制
window.jQuery = function(){
    console.log('我是jQuery')
}

main.js

代码语言:javascript复制
jQuery()

使用parcel运行以上代码,测试

代码语言:javascript复制
MacBook-pro:dom-2 driverzeng$ parcel src/index.html 


链式风格

也叫jQuery风格

window.jQuery()使我们提供的全局函数

特殊函数jQuery jQuery(选择器)用于获取对应的元素 但是它却不返回这些元素 相反,它返回一个对象,称为jQuery构造出来的对象 这个对象可以操作对应的元素

index.html

代码语言:javascript复制
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>曾老湿 手写 jQuery</title>
</head>
<body>
    <div class="test">
        你好1
    </div>
    <div class="test">
        你好2
    </div>
    <div class="test">
        你好3
    </div>
    <script src="jquery.js"></script>
    <script src="main.js"></script>
</body>
</html>

jquery.js

代码语言:javascript复制
window.jQuery = function(selector){
    const elements = document.querySelectorAll(selector)
    // api 可以操作elements
    const api = {
        // 闭包:函数访问外部变量
        addClass(className){
            for(let i=0;i<elements.length;i  ){
                elements[i].classList.add(className)
            }
            return this
        }
    }
    return api
}

main.js

代码语言:javascript复制
const api = jQuery('.test') // 不返回元素们,返回api对象
api.addClass('red') // this 就是 api
   .addClass('green') // this 就是 api
   .addClass('blue') // 链式操作

代码优化:那我们是不是可以把const api这个变量去掉?直接return下面的函数,代码如下:

jquery.js

代码语言:javascript复制
window.jQuery = function(selector){
    const elements = document.querySelectorAll(selector)
    // api 可以操作elements
    return {
        // 闭包:函数访问外部变量
        addClass(className){
            for(let i=0;i<elements.length;i  ){
                elements[i].classList.add(className)
            }
            return this
        }
    }
}

当我们调用的时候 ,是不是也可以不加api?代码如下:

main.js

代码语言:javascript复制
jQuery('.test') // 不返回元素们,返回api对象
   .addClass('red') // this 就是 api
   .addClass('green') // this 就是 api
   .addClass('blue') // 链式操作

实现find函数

jQuery是构造函数么?

是,因为jQuery函数确实构造了一个对象

不是,因为不需要写new jQuery()就能构造一个对象

结论: 1.jQuery是一个不需要加new的构造函数 2.jQuery不是常规意义上的构造函数 3.这是因为jQuery用了一些技巧

那么我们可以使用链式风格来实现增删改查


jQuery('#xxx') 返回值并不是元素,而是一个api对象: 该功能已实现

jQuery('#xxx').find('.red') 查找#xxx里的.red元素

index.html

代码语言:javascript复制
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>曾老湿 手写 jQuery</title>
</head>
<body>
    <div class="test1">
        你好1
        <div class="child">child1</div>
        <div class="child">child2</div>
        <div class="child">child3</div>
    </div>
    <div class="test2">
        你好2
    </div>
    <div class="test3">
        你好3
    </div>
    <script src="jquery.js"></script>
    <script src="main.js"></script>
</body>
</html>

main.js

代码语言:javascript复制
const x1 = jQuery('.test1').find('.child')
console.log(x1)

jquery.js

代码语言:javascript复制
window.jQuery = function(selector){
    const elements = document.querySelectorAll(selector)
    // api 可以操作elements
    return {
        // 闭包:函数访问外部变量
        addClass(className){
            for(let i=0;i<elements.length;i  ){
                elements[i].classList.add(className)
            }
            return this
        },
        find(selector){
            let array = []
            for(let i=0;i<elements.length;i  ){
                const elements2 = Array.from(elements[i].querySelectorAll(selector))
                array = array.concat(elements2)
            }
            return array
        }
    }
}

但是问题出来了,如果说,我找到child,让后想往child后面加.red,该如何添加?

main.js

代码语言:javascript复制
const x1 = jQuery('.test1').find('.child')
console.log(x1)
x1.addClass('.red')

然后会发现,array是一个数组啊,数组是不能添加的,浏览器会报错

那么我们可不可以,再返回一个this?

jquery.js

代码语言:javascript复制
window.jQuery = function(selector){
    const elements = document.querySelectorAll(selector)
    // api 可以操作elements
    return {
        // 闭包:函数访问外部变量
        addClass(className){
            for(let i=0;i<elements.length;i  ){
                elements[i].classList.add(className)
            }
            return this
        },
        find(selector){
            let array = []
            for(let i=0;i<elements.length;i  ){
                const elements2 = Array.from(elements[i].querySelectorAll(selector))
                array = array.concat(elements2)
            }
            return this
        }
    }
}

 它并没有把.red 加到child上,我们把main.js的代码简化一下

main.js

代码语言:javascript复制
jQuery('.test1').find('.child').addClass('.red')

实际上它加到了find前面的api于是乎,就加到了test1上了,那么我们只能重新封装,得到新的api

jquery.js

代码语言:javascript复制
window.jQuery = function(selectorOrArray){
    let elements
    if(typeof selectorOrArray === 'string'){
        elements = document.querySelectorAll(selectorOrArray)
    }else if(selectorOrArray instanceof Array){
        elements = selectorOrArray
    }
    // api 可以操作elements
    return {
        // 闭包:函数访问外部变量
        addClass(className){
            for(let i=0;i<elements.length;i  ){
                elements[i].classList.add(className)
            }
            return this
        },
        find(selector){
            let array = []
            for(let i=0;i<elements.length;i  ){
                const elements2 = Array.from(elements[i].querySelectorAll(selector))
                array = array.concat(elements2)
            }
            const newApi = jQuery(array)
            return newApi
        }
    }
}

来点别的需求

先给test1定义为api1 然后加上blue,然后给api2赋值是找到test1的child给他red,再杀一个回马枪,给api1添加green

main.js

代码语言:javascript复制
const api1 = jQuery('.test1')
api1.addClass('blue')

const api2 = api1.find('.child').addClass('red')

api1.addClass('green')

jQuery('#xxx').parent() 获取爸爸

jquery.js

代码语言:javascript复制
window.jQuery = function(selectorOrArray){
    let elements
    if(typeof selectorOrArray === 'string'){
        elements = document.querySelectorAll(selectorOrArray)
    }else if(selectorOrArray instanceof Array){
        elements = selectorOrArray
    }
    // api 可以操作elements
    return {
        oldApi: selectorOrArray.oldApi,
        // 闭包:函数访问外部变量
        addClass(className){
            for(let i=0;i<elements.length;i  ){
                elements[i].classList.add(className)
            }
            return this
        },
        find(selector){
            let array = []
            for(let i=0;i<elements.length;i  ){
                const elements2 = Array.from(elements[i].querySelectorAll(selector))
                array = array.concat(elements2)
            }
            array.oldApi = this // this就是 旧api
            const newApi = jQuery(array)
            return newApi
        },
        each(fn){
            for(let i=0;i<elements.length;i  ){
                fn.call(null,elements[i],i)
            }
            return this
        },
        print(){
            console.log(elements)
        },
        parent(){
            const array = []
            this.each((node)=>{
                if(array.indexOf(node.parentNode) === -1){
                    array.push(node.parentNode)
                }
            })
            return jQuery(array)
        },
        end(){
            return this.oldApi
        }
    }
}

main.js

代码语言:javascript复制
const x = jQuery('.test1')

x.parent().print()

jQuery('#xxx').children() 获取儿子

jquery.js

代码语言:javascript复制
window.jQuery = function(selectorOrArray){
    let elements
    if(typeof selectorOrArray === 'string'){
        elements = document.querySelectorAll(selectorOrArray)
    }else if(selectorOrArray instanceof Array){
        elements = selectorOrArray
    }
    // api 可以操作elements
    return {
        oldApi: selectorOrArray.oldApi,
        // 闭包:函数访问外部变量
        addClass(className){
            for(let i=0;i<elements.length;i  ){
                elements[i].classList.add(className)
            }
            return this
        },
        find(selector){
            let array = []
            for(let i=0;i<elements.length;i  ){
                const elements2 = Array.from(elements[i].querySelectorAll(selector))
                array = array.concat(elements2)
            }
            array.oldApi = this // this就是 旧api
            const newApi = jQuery(array)
            return newApi
        },
        each(fn){
            for(let i=0;i<elements.length;i  ){
                fn.call(null,elements[i],i)
            }
            return this
        },
        print(){
            console.log(elements)
        },
        parent(){
            const array = []
            this.each((node)=>{
                if(array.indexOf(node.parentNode) === -1){
                    array.push(node.parentNode)
                }
            })
            return jQuery(array)
        },
        children(){
            const array = []
            this.each((node)=>{
                if(array.indexOf(node.parentNode) === -1){
                    array.push(...node.children)
                    // ... 代表 array.push(node.children[0],node.children[1],node.children[2]...)因为不知道有几个元素 所以用...把数组拆开
                }
            })
            return jQuery(array)
        },
        end(){
            return this.oldApi
        }
    }
}

main.js

代码语言:javascript复制
const x = jQuery('.test1')
x.children().print()

jQuery('#xxx').siblings() 获取兄弟 jQuery('#xxx').index() 获取排行老几(从0开始) jQuery('#xxx').next() 获取弟弟 jQuery('#xxx').prev 获取哥哥 jQuery('#xxx').each(fn) 遍历并对每个元素执行fn

jquery.js

代码语言:javascript复制
window.jQuery = function(selectorOrArray){
    let elements
    if(typeof selectorOrArray === 'string'){
        elements = document.querySelectorAll(selectorOrArray)
    }else if(selectorOrArray instanceof Array){
        elements = selectorOrArray
    }
    // api 可以操作elements
    return {
        oldApi: selectorOrArray.oldApi,
        // 闭包:函数访问外部变量
        addClass(className){
            for(let i=0;i<elements.length;i  ){
                elements[i].classList.add(className)
            }
            return this
        },
        find(selector){
            let array = []
            for(let i=0;i<elements.length;i  ){
                const elements2 = Array.from(elements[i].querySelectorAll(selector))
                array = array.concat(elements2)
            }
            array.oldApi = this // this就是 旧api
            const newApi = jQuery(array)
            return newApi
        },
        each(fn){
            for(let i=0;i<elements.length;i  ){
                fn.call(null,elements[i],i)
            }
            return this
        },
        end(){
            return this.oldApi
        }
    }
}

实现end函数


用end表示上一个api结束

jquery.js

代码语言:javascript复制
window.jQuery = function(selectorOrArray){
    let elements
    if(typeof selectorOrArray === 'string'){
        elements = document.querySelectorAll(selectorOrArray)
    }else if(selectorOrArray instanceof Array){
        elements = selectorOrArray
    }
    // api 可以操作elements
    return {
        oldApi: selectorOrArray.oldApi,
        // 闭包:函数访问外部变量
        addClass(className){
            for(let i=0;i<elements.length;i  ){
                elements[i].classList.add(className)
            }
            return this
        },
        find(selector){
            let array = []
            for(let i=0;i<elements.length;i  ){
                const elements2 = Array.from(elements[i].querySelectorAll(selector))
                array = array.concat(elements2)
            }
            array.oldApi = this // this就是 旧api
            const newApi = jQuery(array)
            return newApi
        },
        end(){
            return this.oldApi
        }
    }
}

main.js

代码语言:javascript复制
jQuery('.test1')
    .addClass('blue')
    .find('.child')
    .addClass('red')
    .addClass('green')
    .end()
    .addClass('yellow')

什么?你嫌jQuery太长?

那么我们来解决这么一个小问题,有的时候我们写jQuery,一会小写一会大写,敲起来很费劲,那么我们在调用的时候,可以把它简化,只需要在代码中加入一句话。

代码语言:javascript复制
window.$ = window.jQuery

那么后台调用如下:

代码语言:javascript复制
// 之前调用方式
jQuery('.test1')
    .children().print()

// 简化调用方式
$('.test1')
    .children().print()

还可以继续简化,我们把代码折叠后,会发现,window.jQuery = fn,window.$ = window.jQuery

所以,我们是不是可以用小学学习的数学知识等量代换

window.$ = window.jQuery = fn

最终代码如下:

代码语言:javascript复制
window.$ = window.jQuery = function(selectorOrArray){
    let elements
    if(typeof selectorOrArray === 'string'){
        elements = document.querySelectorAll(selectorOrArray)
    }else if(selectorOrArray instanceof Array){
        elements = selectorOrArray
    }
    // api 可以操作elements
    return {
        oldApi: selectorOrArray.oldApi,
        // 闭包:函数访问外部变量
        addClass(className){
            for(let i=0;i<elements.length;i  ){
                elements[i].classList.add(className)
            }
            return this
        },
        find(selector){
            let array = []
            for(let i=0;i<elements.length;i  ){
                const elements2 = Array.from(elements[i].querySelectorAll(selector))
                array = array.concat(elements2)
            }
            array.oldApi = this // this就是 旧api
            const newApi = jQuery(array)
            return newApi
        },
        each(fn){
            for(let i=0;i<elements.length;i  ){
                fn.call(null,elements[i],i)
            }
            return this
        },
        print(){
            console.log(elements)
        },
        parent(){
            const array = []
            this.each((node)=>{
                if(array.indexOf(node.parentNode) === -1){
                    array.push(node.parentNode)
                }
            })
            return jQuery(array)
        },
        children(){
            const array = []
            this.each((node)=>{
                if(array.indexOf(node.parentNode) === -1){
                    array.push(...node.children)
                    // ... 代表 array.push(node.children[0],node.children[1],node.children[2]...)因为不知道有几个元素 所以用...把数组拆开
                }
            })
            return jQuery(array)
        },
        end(){
            return this.oldApi
        }
    }
}

0 人点赞