没想到啊

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  6 随笔 :: 379 文章 :: 97 评论 :: 24万 阅读
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

文章比较浅显,高手勿入。

常常写小页面自己玩的同学有没有发现,偶尔需要的一些功能,如果引用JQ,PROTOTYPE,MOOTOOLS这些著名JS库的话,代价略大,感觉略重了点。
如果你也纠结这些,想让自己的小项目略完美一些,那么为什么不写一个自己的小脚本库呢。

可能有的同学写过很多了。像是下面的这种:

复制代码
    function toolDoSomeThingA(){
        //code...
    }
    function toolDoSomeThingB(){
        //code...
    }
    function toolDoSomeThingC(){
        //code...
    }
    function toolDoSomeThingD(){
        //code...
    }
复制代码

 

这样是可以称作脚本库,比较简单直接,但是通常情况这不一定是一个最佳实践。
如果考虑到扩充不发生冲突的话,你的函数应该有一个命名空间,或者挂载在一个非WINDOW的对象之下。(特殊需求特殊考虑)

那么如果完成一个类似JQ,自己完全可以掌握,可以控制的脚本库呢?如果你感兴趣,那么就继续看下去吧:D

首先我们希望创建一个共有的元素来包装或者说来代理所有的操作,可以简化我们的重复操作,以及进行一些必要的变量保存。

然后我们希望这个元素的使用方法和JQEURY一样,可以JQUERY(ANY)来选择操作某个元素;

那我们不妨这样做,具体看注释:

复制代码
// 防止重复定义,函数重载
var soulteary = soulteary ||
function(e) {
    // 例子就限定带ID的元素,比较简单
    // 你可以根据你的项目对浏览器的支持
    // 扩展浏览器支持和方法,比如:document.querySelector
    // 或者自己写兼容IE的XPATH等...
    var target = document.getElementById(e) || target;
    return target;
}
复制代码

接着, 我们开始扩充这个对象,把我们的工具函数都挂载上去.这里我们使用稳妥构造创建的方法,好处详见设计模式.
接下来我们要搞一个比较简单的,仅支持webkit的mini库,支持jquery的bind,unbind,创建callback,创建ajax.
至于其他浏览器,感兴趣的童鞋可以看完本文后扩充。

我们先来说说jquery的bind和unbind,

jquery代理了我们的绑定和解除绑定, 尤其是解除绑定,我们知道 removeEventListener这个函数需要传入相同的函数, 言简意赅的说就是, 你的函数的handle要一样。

文章开始说的那种定义式的函数,很容易解决,全局的名字就是他的句柄,但是闭包内的函数呢,函数内的函数呢,callback回来的函数呢。
这个时候,我们使用对象内部空间就有了用武之地。

我们可以先设计一个数据仓库,来存放我们在这个文档中的元素和元素下绑定的事件,以及事件下的函数们。
转换成伪代码就是这样。

var funcList = {}; // 想想之中,这货该是这样的... { HTML_ELEMENT_ID: { EVENT: [ FUNCTION, ...] }, ... }, ... }

    funcList = {
        'a#click-me':{
            'click':[function A(){},function A(){}],
            'dbclick':[function A(){},function A(){}]
        }
    }

然后因为我们的句柄和原始函数都存在了这个对象中,解除绑定的时候,我们就也可以指定某个元素的某类事件,或者指定某元素,或者什么都不传入,卸载所有的事件了。

bind和unbind简单实现如下:

复制代码
var soulteary = soulteary ||
function(e) {

    var target = document.getElementById(e) || target;
    // 内部对象
    var me = new Object();
    var funcList = {}; // 想想之中,这货该是这样的... { HTML_ELEMENT_ID: { EVENT: [ FUNCTION, ...] }, ... }, ... }
    me.bind = function(type, func) {
        if(!target) {
            return this;
        }
        if(typeof func !== 'function') {
            return this;
        }

        // 例子就先管潮流浏览器 和非冒泡情况
        target.addEventListener(type, func, false);
        // 例子就先管单一事件 不进行不同类型的事件叠加管理
        funcList = funcList || {};
        funcList[e] = funcList[e] || [];
        funcList[e][type] = funcList[e][type] || [];
        if(!(func in funcList)) {
            funcList[e][type].push(func);
        };

        // V8太快了, 连续绑定, 基本感觉和写一起一样,
        // 莫非是V8的语句优化, 除非timeout设置特别大的时间.
        // 区别的话,有测试的同学知道 :D
        console.log(this, '@:', funcList)

        return this;

    }
    me.unbind = function(any) {
        var mode = arguments.length;
        switch(mode) {

        case 1:
            // 偷懒就先支持某类型吧
            for(var func in funcList) {
                for(var obj in funcList[func]) {
                    if(obj == any) {
                        for(var e in funcList[func][obj]) {
                            if(typeof funcList[func][obj][e] == 'function') {
                                target.removeEventListener(obj, funcList[func][obj][e], false);
                            }
                        }
                    }
                }
            }
            break;
        case 0:
        default:
            for(var func in funcList) {
                for(var obj in funcList[func]) {
                    for(var e in funcList[func][obj]) {
                        if(typeof funcList[func][obj][e] == 'function') {
                            target.removeEventListener(obj, funcList[func][obj][e], false);
                        }
                    }
                }
            }
        }
        return this;
    }




    return target?target:me;
}
复制代码

看到上面的处理,有的同学会提问,什么叫做不处理事件叠加。
是这样的,之前元素可能绑定了事件,我们需要枚举和保存下来,等处理完我们的事件后,再把事件叠加上去。
这里有一个额外的处理,就是针对在元素内写死onclick之类的事件情况,我们要保存属性事件到自己的函数变量中,
然后obj.removeAttribute(‘onclick’)来完全解除绑定…

我们接着来说下callback,我写的很简单,你可以扩充一下。

复制代码
me.callback = function(params){
    if (!params.url || !params.id) {return this;}
    params.id+='__cb';
    var dom = document.getElementById(params.id);
    if (dom) {dom.parentNode.removeChild(dom);}
    var dom = document.createElement('script');
        dom.type = 'text/javascript';
        dom.src = params.url;
        dom.id = params.id;
    var body = document.getElementsByTagName('body')[0];
        body.appendChild(dom);
    return this;
}
复制代码

大概思路就是创建元素,把你的url请求的元素添加文档,之前的体验都是加在head中,但是实际上不算是太友好,IE head元素有个小坑,感兴趣的童鞋可以搜索一下,题外话了,就不说了,body尾部算是一个不错的选择.

接着我们来看看AJAX,同样很简单,随便写的.

复制代码
me.ajax = function(params) {
    if(!params.url) {return this;}

    // 这里做个最简单的实现
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {
        if(xhr.readyState == 4 && xhr.status == 200) {
            params.callback = params.callback ||
            function(data) {
                eval(data);
                return true;
            }

            params.callback(xhr.responseText);
        }
        return false;
    }
    var r = new Date();
        r = r.getMilliseconds();
        r*=r;
    params.url += '?c=' + r;
    params.mode = params.mode || 'GET';
    xhr.open(params.mode, params.url, true);
    xhr.setRequestHeader('Content-Type', 'text/plain;charset=UTF-8');
    xhr.send();

    return this;
}
复制代码

ajax回调的话,自定义比较多,这个参考jq或者mootools都可以…

然后是常见的属性赋值,这里少写一个获取,大家懂的。
如果你只是使用一般的标签和textarea的话…
至于多什么,自己判断type和浏览器,分别获取一下就好了,推荐做成list。

复制代码
me.text = function(str){
    // 这个可以自己补全
    if ('textarea' == target.type) {
        target.value = str;
    }else{
        target.textContent = str;
    }
    return this;
}
复制代码

然后这个对象或许名字长了点,和楼主的英文名都撞一起了。
那么我们给他个alias

var $ = soulteary;
$('test-me').bind('click', function(){}).unbind('click').text('内容');
$('test-me').unbind();
$.ajax({url:'/?a=1'});
$.callback({url:'/?a=2'});

这个脚本的扩展还有很多,这里没有把全局的工具方法用prototype超类重写挂载到原型上,你可以在对象外部再来一层,把对象挂载上去,看起来会更加的美观.

感谢你耐心看完这篇浅显的渣文,欢迎吐槽,欢迎留言讨论。

最后,把上面的不完整版本的完整版本(绕嘛)贴一下。

复制代码
var soulteary = soulteary || function(e) {
    var target = document.getElementById(e) || target;
    var me = new Object();
    var funcList = {};
    me.bind = function(type, func) {
        if(!target) {
            return this;
        }
        if(typeof func !== 'function') {
            return this;
        }
        target.addEventListener(type, func, false);
        funcList = funcList || {};
        funcList[e] = funcList[e] || [];
        funcList[e][type] = funcList[e][type] || [];
        if(!(func in funcList)) {
            funcList[e][type].push(func);
        };
        return this;

    }
    me.unbind = function(any) {
        var mode = arguments.length;
        switch(mode) {

        case 1:
            for(var func in funcList) {
                for(var obj in funcList[func]) {
                    if(obj == any) {
                        for(var e in funcList[func][obj]) {
                            if(typeof funcList[func][obj][e] == 'function') {
                                target.removeEventListener(obj, funcList[func][obj][e], false);
                            }
                        }
                    }
                }
            }
            break;
        case 0:
        default:
            for(var func in funcList) {
                for(var obj in funcList[func]) {
                    for(var e in funcList[func][obj]) {
                        if(typeof funcList[func][obj][e] == 'function') {
                            target.removeEventListener(obj, funcList[func][obj][e], false);
                        }
                    }
                }
            }
        }
        return this;
    }

    me.callback = function(params) {
        if(!params.url || !params.id) {
            return this;
        }
        params.id += '__cb';
        var dom = document.getElementById(params.id);
        if(dom) {
            dom.parentNode.removeChild(dom);
        }
        var dom = document.createElement('script');
        dom.type = 'text/javascript';
        dom.src = params.url;
        dom.id = params.id;
        var body = document.getElementsByTagName('body')[0];
        body.appendChild(dom);
        return this;
    }
    me.ajax = function(params) {
        if(!params.url) {
            return this;
        }

        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function() {
            if(xhr.readyState == 4 && xhr.status == 200) {
                params.callback = params.callback ||
                function(data) {
                    eval(data);
                    return true;
                }

                params.callback(xhr.responseText);
            }
            return false;
        }
        var r = new Date();
        r = r.getMilliseconds();
        r *= r;
        params.url += '?c=' + r;
        params.mode = params.mode || 'GET';
        xhr.open(params.mode, params.url, true);
        xhr.setRequestHeader('Content-Type', 'text/plain;charset=UTF-8');
        xhr.send();

        return this;
    }
    me.text = function(str) {
        console.log(target);
        if('textarea' == target.type) {
            target.value = str;
        } else {
            target.textContent = str;
        }
        return this;
    }
    return me;
}
var $ = soulteary;
复制代码
作者: 苏洋

当黑夜张开翅膀,月光静悄悄在流淌。风带不走的忧伤,孤单的梦想在远航。关注互联网,热爱计算机,我是苏洋。

VIA: http://soulteary.com/2013/01/25/write-js-lib.html

 

posted on   没想到啊  阅读(192)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· [AI/GPT/综述] AI Agent的设计模式综述
点击右上角即可分享
微信分享提示