JS如何封装一个库1

76 阅读1分钟

封装一个简单的jQuery

jQ已过时,仅用于学习封装技巧

题目: 页面有10个class含有red的div,请给这10个div每个添加一个green类

DOM API写法
const 所有标签 = document.querySelectorAll('red')
for (let i =0; i < 所有标签.length; i++) {
    const 标签 = 所有标签[i]
    标签.classList.add('green')
}

1.png

jQuery 写法
jQuery.('.red').addClass('green')

$('.red').addClass('green')

$ = jQuery  是个函数  别名
$('red') 没有返回元素,而是返回能操作元素的函数  这不是闭包的作用吗?
可以用闭包实现

const api = f()
api.add()
//等价于
f().add()

经典闭包作用的例子

function f() {
    let n = 0
    return function add() {
        n += 1
        console.log(n)
    }
}

const add = f()
add()//1
add()//2

思考1
f函数名只用了一次, 我们删掉f之后进行变形
const add = function () {
    let n = 0
    return function add() {
        n += 1
        console.log(n)
    }
}()//这个函数为立即执行函数

add()//1
add()//2

思考2
如果f函数被调用多次,那么add1和add2相等吗
function f() {
    let n = 0
    return function add() {
        n += 1
        console.log(n)
    }
}

const add1 = f() //#101
const add1 = f() //#202
console.log(add1 === add2)//false 

思考3
如果除了add还需要你提供一个减一操作,怎么写
返回函数对象
function f() {
    let n = 0
    return {
        function add() {
            n += 1
            console.log(n)
        },
        function sub() {
            n -= 1
            console.log(n)
        }
    }    
}

const api = f()
add() //1
sub() //0

//解构赋值
const {add, sub} = f()
add() //1
sub() //0

简易封装

function jQuery(选择器) {
    const 标签伪数组 = document.querySelectorAll(选择器)
    const 标签 = Array.from(标签伪数组)
    const api = {
        addClass(className) {
            标签数组.forEach((标签) => {
                标签.classList.add(className)
            })
        },
        removeClass(className) {
            标签数组.forEach((标签) => {
                标签.classList.remove(className)
            })
        }
    }
    return api
}
const $ = jQuery

思考
目前的代码是否会在多次调用 $ 时造成内存冗余
$('.red').addClass('green')
$('.red').removeClass('red')

会造成冗余,三种方案
1.把api移到外面(不好解决)
2.使用隐藏属性 + 共有属性(原型写法)
3.使用class写法

原型写法
function jQuery(选择器) {
    const 标签伪数组 = document.querySelectorAll(选择器)
    this.标签数组 = Array.from(标签伪数组)
}
jQuery.prototype = {
    constructor: jQuery
    addClass(className) {
        this.标签数组.forEach((标签) => {
            标签.classList.add(className)
        })
    }
}
    
const $ = jQuery
new $('.red').addClass('green')

class写法
class jQuery {
    constructor(选择器) {
        const 所有元素伪数组 = document.querySelectorAll(选择器)
        this.所有元素 = Array.from(所有元素伪数组)
    }
    addClass(className) {
        const {所有元素} = this // 解构赋值
        for (let i = 0; i < 所有元素.length; i++) {
            所有元素[i].classList.add(className)
        }
    }
}
const $ = jQuery
new $('.red').addClass('green')

如何不用new, 又能享受到new 的便利

最终版本

function jQuery(选择器) {
    if (!(this instanceof jQuery)) {
        //如果this不是jQuery创建的
        //说明new没有参与进来
        return new jQuery(选择器)
    }
    //this.标签伪数组 = document.querySelectorAll(选择器), 闭包 => 实现封装
    const 标签伪数组 = document.querySelectorAll(选择器)
    this.标签数组 = Array.from(标签伪数组)
}
jQuery.prototype = {
    constructor: jQuery
    addClass(className) {
        this.标签数组.forEach((标签) => {
            标签.classList.add(className)
        })
    }
}
    
const $ = jQuery
$('.red').addClass('green')