前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >30·灵魂前端工程师养成-jQuery中的设计模式

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

作者头像
DriverZeng
发布2022-09-26 17:02:07
1.5K0
发布2022-09-26 17:02:07
举报
文章被收录于专栏:Linux云计算及前后端开发

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


-多年互联网运维工作经验,曾负责过大规模集群架构自动化运维管理工作。 -擅长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
        }
    }
}

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-05-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 用jQuery风格重新封装
  • 实现find函数
  • 实现end函数
  • 什么?你嫌jQuery太长?
相关产品与服务
运维安全中心(堡垒机)
腾讯云运维安全中心(堡垒机)(Operation and Maintenance Security Center (Bastion Host))可为您的 IT 资产提供代理访问以及智能操作审计服务,为客户构建一套完善的事前预防、事中监控、事后审计安全管理体系,助力企业顺利通过等保测评。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档