jQuery 中的设计模式

108 阅读1分钟

链式风格

也叫 jQuery 风格

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

命名风格

下面的代码令人误解

  • const div = $('div#test')
  • 我们会误以为 div 是一个 DOM
  • 实际上 div 是 jQuery 构造的 api 对象

可以改成这样

  • const div=div = ('div#test')
  • $div.appendChild 不存在,因为它不是 jQuery 对象
  • $div.find 存在, 因为它是 jQuery 对象

特殊函数 jQuery

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

jQuery对象 代指 jQuery函数构造出来的对象

  • $('<div><span>1</span></div>') 创建 div
  • .appendTo(document.body) 插入到 body 中

  • $div.remove()
  • $div.empty()

  • $div.text(?) 读写文本内容
  • $div.html(?) 读写HTML内容
  • $div.attr('titile', ?) 读写属性
  • $div.css({color: 'red'}) 读写 style
  • $div.addClass('blue')
  • $div.on('click', fn)
  • $div.off('click', fn)
  • $div 大部分时候对应了多个 div 元素

  • jQuery('#xxx') 返回值并不是元素,而是一个 api 对象
  • jQuery('#xxx').find('.red') 查找 #xxx 里的 .red 元素
  • jQuery('#xxx').parent() 获取爸爸
  • jQuery('#xxx').children() 获取儿子
  • jQuery('#xxx').siblings() 获取兄弟
  • jQuery('#xxx').index() 获取排行老几(从0开始)
  • jQuery('#xxx').next() 获取弟弟
  • jQuery('#xxx').prev() 获取哥哥
  • jQuery('.red').each(fn) 遍历并对每个元素执行 fn

jQuery 的设计模式

jQuery 用到了那些设计模式

  • 不用 new 的构造函数, 这个模式没有专门的名字
  • $(支持多种参数), 这个模式叫做重载
  • 用闭包隐藏细节, 这个模式没有专门的名字
  • $div.text() 即可读也可以写, getter / setter
  • .fn.fn 是 .prototype 的别名
  • jQuery 针对不同浏览器使用不同代码, 这叫适配器
window.$ = window.jQuery = function (selectorOrArray) {
    let elements
    if (typeof selectorOrArray === 'string') {
        elements = document.querySelectorAll(selectorOrArray)
    } else if (selectorOrArray instanceof Array) {
        elements = selectorOrArray
    }
    // api 可以操作elements
    const api = Object.create(jQuery.prototype) // 创建一个对象,这个对象的 __proto__ 为括号里面的东西
    // const api = {__proto__: jQuery.prototype}
    // api.elements = elements
    // api.oldApi = selectorOrArray.oldApi
    Object.assign(api, { // 浅复制进 api
        elements: elements,
        oldApi: selectorOrArray.oldApi
    })
    return api
};

jQuery.fn = jQuery.prototype = {
    constructor: jQuery,
    jQuery: true,
    find(selector) {
        let array = []
        for (let i = 0; i < this.elements.length; i++) {
            const elements2 = Array.from(this.elements[i].querySelectorAll(selector))
            array = array.concat(elements2)
        }
        array.oldApi = this // this 就是省掉的 api
        // 这个函数调用的this是上一个的,所以得构造一个新的jQuery函数
        return jQuery(array)
    },
    each(fn) {
        for (let i = 0; i < this.elements.length; i++) {
            fn.call(null, this.elements[i], i)
        }
        return this
    },
    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.children) === -1) {
                array.push(...node.children) // 相当于拆分
            }
        })
        return jQuery(array)
    },
    // 闭包:函数访问外部的变量
    addClass(className) {
        for (let i = 0; i < this.elements.length; i++) {
            this.elements[i].classList.add(className)
        }
        return this
    },
    print() {
        console.log(this.elements)
    },
    end() {
        return this.oldApi // this 是当前的 api
    },
}