面向对象在JQuery中的应用——手写简版JQ

·  阅读 180

核心方法

  1. 获取DOM =>$('button')
  2. 返回指定下标元素 =>eq()
  3. 添加点击事件click =>$().click()
  4. 添加事件 =>$().on()
  5. 返回上一个操作对象 =>end()
  6. 样式设置与获取 =>$().css()
  7. css样式拦截 => $.cssHooks

链式调用原理

原理是在内部每一个方法后面都加了一个return this

在一个对象里面,this指向的是对象本身,当我们调用方法的时候,这些方法都是在对象内部调用的,所以加this才可以访问到这些方法。

如果没有加上return this的话,那么执行完一个函数之后,会默认返回undefinedundefine是JS内部隐式添加的。

DOMContentLoaded和load

两者触发时机不一样,先执行DOMContentLoaded,再执行load,DOM文档加载的步骤为:

  1. 解析HTML结构;
  2. 加载外部脚本和样式表文件;
  3. 解析并执行脚本代码;
  4. DOM树构建完成,执行DOMContentLoaded
  5. 加载图片等外部文件;
  6. 页面加载完毕,执行load

实现代码

function $(...arg) {
  // 调用$方法并返回Jerry实例化
  return new Jerry(...arg)
}

// 类中的this始终指向new 实例化对象
class Jerry {
  constructor(arg, root) {
    // 保存上一次操作对象,用于调用end()返回
    this.prevObject = root || $('document', {})

    if (typeof arg === 'string') {
      /** @字符串 **/
      const eles = document.querySelectorAll(arg)
      this.setElement(eles)
    } else if (typeof arg === 'object') {
      /** @对象 **/
      this.setElement(arg)
    } else if (typeof arg === 'function') {
      /** @函数 **/
      // 监听DOMContentLoaded事件
      window.addEventListener('DOMContentLoaded', arg)
    }
  }

  // 🔥1、将DOM添加到this上
  setElement(eles) {
    // 如果没有length,表示是DOM对象且只有一个元素
    if (eles.length === undefined) {
      this[0] = eles
      this.length = 1
    } else {
      // 多个元素
      for (let i = 0; i < eles.length; i++) {
        this[i] = eles[i]
      }
      this.length = eles.length
    }
    // 返回this,形成链式结构
    return this
  }

  // 🔥2、获取指定元素
  eq(index) {
    return $(this[index], this)
  }

  // 🔥3、给this上所有元素添加点击事件
  click(fn) {
    for (let i = 0; i < this.length; i++) {
      // 注意fn不能写成fn()
      this[i].addEventListener('click', fn)
    }
    // 返回this,形成链式结构
    return this
  }

  // 🔥4、添加事件(可多个事件绑定同一个方法)
  on(eventNames, fn) {
    // mousemove mouseup => ['mousemove', 'mouseup']
    // 去除两边空格
    eventNames = eventNames.trim().split(' ')
    // 去除无用空格
    eventNames = eventNames.filter(item => item)
    for (let i = 0; i < this.length; i++) {
      for (let j = 0; j < eventNames.length; j++) {
        this[i].addEventListener(eventNames[j], fn)
      }
    }
  }

  // 🔥5、返回上一个操作对象
  end() {
    return this.prevObject
  }

  // 🔥6、样式相关
  css(...arg) {
    // 字符串 agr[0] && !arg[1] => 获取样式
    // 字符串 agr[0] && arg[1] => 设置单个样式
    // 对象 agr[0] => 批量设置样式

    if (typeof arg[0] === 'string') {
      const [ attr, value ] = arg // 注意arg是个数组
      // 设置单个样式
      if (value) {
        for (let i = 0; i < this.length; i++) {
          Jerry.setStyle(this[i], attr, value)
        }
      } else {
        // 获取样式,获取第一个元素的样式
        return Jerry.getStyle(this[0], attr) // 注意要return
      }
    } else if (typeof arg[0] === 'object') {
      // 批量设置样式
      for (const attr in arg[0]) {
        for (let i = 0; i < this.length; i++) {
          Jerry.setStyle(this[i], attr, arg[0][attr])
        }
      }
    }

    // 返回this,形成链式结构
    return this
  }

  // 🔥静态方法setStyle,方便class内部、外部直接使用
  static setStyle(el, attr, value) {
    // 🔥7、css Hooks拦截(设置单个样式时)
    if (attr in $.cssHooks) {
      // 拦截了要设置的样式
      $.cssHooks[attr].set(el, attr, value)
    } else {
      el.style[attr] = value
    }
  }
  // 🔥静态方法setStyle,方便class内部、外部直接使用
  static getStyle(el, attr) {
    // 🔥7、css Hooks拦截(获取单个样式时)
    if (attr in $.cssHooks) {
      // 拦截了要设置的样式
      $.cssHooks[attr].get(el, attr)
    } else {
      return getComputedStyle(el)[attr]
    }
  }
}
复制代码

使用

<div flex>
  <button class="btn1">按钮1</button>
  <button class="btn2">按钮2</button>
  <button class="btn3">按钮3</button>
</div>

// Dom内容加载完毕执行
$(function () {
  console.log('页面加载')
})

// 7、css Hooks拦截(获取/设置单个样式时)
$.cssHooks = {
  backgroundColor: {
    set () {
      console.log('你设置了背景')
    },
    get () {
      console.log('你获取了背景')
    }
  }
}

// 1、获取元素
console.log($('button'), '左获取标签为button的所有元素')
console.log($(document.querySelector('button')), '⬅️获取button的第一个元素')
console.log($(document.querySelectorAll('button')), '⬅️获取button的所有元素')

// 2、获取指定元素
console.log($('button').eq(1), '⬅️获取下表为1的元素')

// 3、添加点击事件
$('button').click(function(){
  console.log(this)
})

// 4、添加事件(可多个事件绑定同一个方法)
$('button').eq(0).on('mouseenter mouseleave', function () {
  console.log('第一个按钮鼠标移入和移出')
})

// 5、返回上一个操作对象
console.log($('button').eq(0).end(), '上一个操作对象')

// 6、样式相关
// 获取样式
console.log($('button').css('backgroundColor'), '获取了样式,无法再链式调用')
// 设置单个样式
$('button').eq(0).css('backgroundColor', '#F56C6C')
// 批量设置样式
$('button').eq(0).css({
  opacity: 0.5,
  borderRadius: '100px'
})
复制代码
分类:
前端
分类:
前端