如何用不到200行代码写一款属于自己的js类库

徐小夕
• 阅读 1411

前言

JavaScript 的核心是支持面向对象的,同时它也提供了强大灵活的 OOP 语言能力。本文将使用面向对象的方式,来教大家用原生js写出一个类似jQuery这样的类库。我们将会学到如下知识点:

  • 闭包:减少变量污染,缩短变量查找范围
  • 自执行函数在对象中的运用
  • extend的实现原理
  • 如何实现跨浏览器的事件监听
  • 原型链与继承

接下来我会对类库的核心api进行讲解和展示,文章最后后附带类库的完整源码,在我之前的文章《3分钟教你用原生js实现具有进度监听的文件上传预览组件》中也使用了类似的方式,感兴趣的可以一起学习,交流。

更加完整的类库地址,请移步github《Xuery——仿jquery API风格的轻量级可扩展的原生js框架

类库设计思路

如何用不到200行代码写一款属于自己的js类库

API介绍和效果展示

  1. 事件绑定 Xuery.on(eventName, fn) 案例如下:

    Xuery('#demo').on('click', function(e){
     alert('hello world!')
    })
  2. 访问和设置css Xuery.css(string|object, ?[string]) 案例如下:

    // 访问css
    Xuery('#demo').css('width')
    // 设置css
    Xuery('#demo').css('width', '1024px')
    // 设置css
    Xuery('#demo').css({
     width: '1024px',
     height: '1024px'
    })
    1. 访问和设置属性 Xuery.attr(string|object, ?[string]) 案例如下:
      // 访问attr
      Xuery('#demo').attr('title')
      // 设置attr
      Xuery('#demo').attr('title', '1024px')
      // 设置attrs
      Xuery('#demo').attr({
      title: '1024px',
      name: '1024px'
      })
  3. 访问和设置html 案例如下:

    // 访问
    Xuery('#demo').html()
    // 设置
    Xuery('#demo').html('前端学习原生框架')

还有其他几个常用的API在这里就不介绍了,大家可以在我的github上查看,或者基于这套基础框架,去扩展属于自己的js框架。

核心源码

以下源码相关功能我做了注释,建议大家认真阅读,涉及到原型链和构造函数的指向的问题,是实现上述调用方式的核心,又不懂可以在评论区交流沟通。

/**
 * 链模式实现自己的js类库
 */
(function(win, doc){
    var Xuery = function(selector, context) {
        return new Xuery.fn.init(selector, context)
    };

    Xuery.fn = Xuery.prototype = {
    constructor: Xuery,
    init: function(selector, context) {
        // 设置元素长度
        this.length = 0;
        // 默认获取元素的上下文document
        context = context || document;
        // id选择符,则按位非将-1转化为0
        if(~selector.indexOf('#')) {
        this[0] = document.getElementById(selector.slice(1));
        this.length = 1;
        }else{
        // 在上下文中选择元素
        var doms = context.getElementsByTagName(selector),
        i = 0,
        len = doms.length;
        for(; i<len; i++){
            this[i] = doms[i];
        }
        }
        this.context = context;
        this.selector = selector;
        return this
    },
    // 增强数组
    push: [].push,
    sort: [].sort,
    splice: [].splice
    };

    // 方法扩展
    Xuery.extend = Xuery.fn.extend = function(){
    // 扩展对象从第二个参数算起
    var i = 1,
    len = arguments.length,
    target = arguments[0],
    j;
    if(i === len){
        target = this;
        i--;
    }
    // 将参数对象合并到target
    for(; i<len; i++){
        for(j in arguments[i]){
        target[j] = arguments[i][j];
        }
    }
    return target
    }

    // 扩展事件方法
    Xuery.fn.extend({
    on: (function(){
        if(document.addEventListener){
        return function(type, fn){
            var i = this.length -1;
            for(; i>=0;i--){
            this[i].addEventListener(type, fn, false)
            }
            return this
        }
        // ie浏览器dom2级事件
        }else if(document.attachEvent){
        return function(type, fn){
            var i = this.length -1;
            for(; i>=0;i--){
            this[i].addEvent('on'+type, fn)
            }
            return this
        }
        // 不支持dom2的浏览器
        }else{
        return function(type, fn){
            var i = this.length -1;
            for(; i>=0;i--){
            this[i]['on'+type] = fn;
            }
            return this
        }
        }
    })()
    })

    // 将‘-’分割线转换为驼峰式
    Xuery.extend({
    camelCase: function(str){
        return str.replace(/\-(\w)/g, function(all, letter){
        return letter.toUpperCase();
        })
    }
    })

    // 设置css
    Xuery.fn.extend({
    css: function(){
        var arg = arguments,
        len = arg.length;
        if(this.length < 1){
        return this
        }
        if(len === 1) {
        if(typeof arg[0] === 'string') {
            if(this[0].currentStyle){
            return this[0].currentStyle[arg[0]];
            }else{
            return getComputedStyle(this[0], false)[arg[0]]
            }
        }else if(typeof arg[0] === 'object'){
            for(var i in arg[0]){
            for(var j=this.length -1; j>=0; j--){
                this[j].style[Xuery.camelCase(i)] = arg[0][i];
            }
            }
        }
        }else if(len === 2){
        for(var j=this.length -1; j>=0; j--){
            this[j].style[Xuery.camelCase(arg[0])] = arg[1];
        }
        }
        return this
    }
    })

    // 设置属性
    Xuery.fn.extend({
    attr: function(){
        var arg = arguments,
        len = arg.length;
        if(len <1){
        return this
        }
        if(len === 1){
        if(typeof arg[0] === 'string'){
            return this[0].getAttribute(arg[0])
        }else if(typeof arg[0] === 'object'){
            for(var i in arg[0]){
            for(var j=this.length -1; j>= 0; j--){
                this[j].setAttribute(i, arg[0][i])
            }
            }
        }
        }
        else if(len === 2){
        for(var j=this.length -1; j>=0; j--){
            this[j].setAttribute(arg[0], arg[1]);
        }
        }
        return this
    }
    })

    // 获取或者设置元素内容
    Xuery.fn.extend({
    html: function(){
        var arg = arguments,
        len = arg.length;
        if(len === 0){
        return this[0] && this[0].innerHTML
        }else{
        for(var i=this.length -1; i>=0; i--){
            this[i].innerHTML = arg[0];
        }
        }
        return this
    }
    })

    Xuery.fn.init.prototype = Xuery.fn;
    window.Xuery = Xuery;
})(window, document);

最后

欢迎关注《趣谈前端》,获取更多前端知识精粹学习社群.

更多推荐

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Karen110 Karen110
3年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Stella981 Stella981
3年前
JS 对象数组Array 根据对象object key的值排序sort,很风骚哦
有个js对象数组varary\{id:1,name:"b"},{id:2,name:"b"}\需求是根据name或者id的值来排序,这里有个风骚的函数函数定义:function keysrt(key,desc) {  return function(a,b){    return desc ? ~~(ak
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这