【JavaScript框架封装】公共框架的封装

/*
* @Author: 我爱科技论坛
* @Time: 20180706
* @Desc: 实现一个类似于JQuery功能的框架

// 公共框架
// 种子模块:命名空间、对象扩展、数组化、类型的判定、domReady机制,无冲突处理
(function (xframe) {
    // 需要参与链式访问的(必须使用prototype的方式来给对象扩充方法)
    xframe.extend({
        each: function (fn) {
            var i = 0,
                len = this.length;
            for (; i < len; i++) {
                // call第一个参数传递的实际上就是this的执行,后面的参数就是目标函数fn需要传递的参数(可省略)
                // this[i] 里面的取值方式类似于json取值,每一个参数存储了选择器获取的所有的nodeList元素集合中的一个元素
                fn.call(this[i]);
            }
            return this;
        }
    });

    // 不需要参与链式访问的
    /*公共部分*/
    xframe.extend(xframe, {});

    /*字符串处理模块*/
    xframe.extend(xframe, {
        /*
        * 下面的这几个都会用到正则表达式,会在后面补充
        * camelCase函数的功能就是将形如background-color转化为驼峰表示法:backgroundColor
        * */
        camelCase: function (str) {
            //  all: -c, letter: c
            return str.replace(/\-(\w)/g, function (all, letter) {
                // 把所有的字母都转换为大写的状态
                return letter.toUpperCase();
            });
        },
        /**
         * 去掉左边的空格 str = ' ()'
         * @param str
         * @returns {*}
         */
        ltrim: function (str) {
            /*
            ^ :表示以XX开头
            \s: 表示空格
            *:  表示匹配零个或者多个
            g: 表示匹配全部,如果没有的话默认只会匹配一个
            (^\s*): 表示以空格开头的一个或者多个字符
            str.replace(, ''): 替换……


            ----------------------------------------------------[其他用法归纳]-------------------------------------
            ^, $: 匹配字符串开始,结束的位置      eg:
            g, i:匹配所有,不区分大小写的字符串; eg: /a/g, /a/i
            *, +, ?: 匹配任意次数, 匹配前面的字符一次或者多次, 0次或者1次

            [] : 匹配一个字符集合; eg: [a-z]所有小写字母的集合, [0-9]所有数字的集合
                                  eg: [a-zA-Z]所有大小写字母的集合
            脱字符^: 匹配任何不在该集合中的字符,与上面的用法正好相反
            {}: 指定重复前面的一个字符多少遍  eg:{N} 重复n遍
                                            eg:{n, m}重复n-m遍
                                            eg: {n, }至少重复n遍
                                            eg:{,m}至多重复m遍



            // 【熟记:同类记忆法】
            \s: 表示空格:包括空格、换行、回车、tab,等价于[\n\r\t\f]
            \S: 匹配非空格字符,等价于[^ \n\r\t\f]
            \d: 表示十进制数字,等价于[0-9]
            \D: 匹配一个非数字字符, 等价于[^0-9]
            \w(小写): 表示字母或者数字,等价于[a-zA-Z0-9]
            \W: 非字母且非数字,与\w相反,等价于:[^a-zA-Z0-9]*

            * */
            return str.replace(/(^\s*)/g, '');
        },
        /* 去掉右边的空格, str = '() '
        * @param str
        */
        rtrim: function (str) {
            return str.replace(/(\s*$)/g, '');
        },
        /**
         * 用于去掉两边的空格(去掉所有的空格) str  =' () '
         * @param str
         * @returns {*}
         */
        trimOld: function (str) {
            return str.replace(/(\s*$)/g, '');
        },
        /**
         * 【使用模板来实现一个简单的数据绑定】
         * 实现简单的数据绑定: @(name), @(sex)
         * data: var user = {name : 'xiugang', role, '钻石会员'}
         * str: = '欢迎@(name), 等级:@(role)光临本站!';
         * @param str   原始的数据格式
         * @param data  需要绑定的数据对象,是一个json格式的数据, json = {name : 'xiuxiu', age : 18}
         * @returns {*}
         */
        formateString: function (str, data) {
            // 使用后面的值去替换掉前面的值
            // 细节分析:((\w+))使用括号匹配的值在JavaScript中实际上就是一个$1, 把这个参数传给match
            // (\w+) 第二个括号实际上匹配到的就是一个$2, 把这个参数传给key
            // match: @(name), @(age), @(sex)
            // key: name, age, sex
            return str.replace(/@\((\w+)\)/g, function (match, key) {
                // 先判断有没有匹配到相应的字符串
                // 找到@()开始的字符串, 使用数据域中的数据去替换
                // 如果json数据data里面么有找到相应的data[key]数据,返回的实际上就是一个空的字符串
                return typeof  data[key] === 'undefined' ? '' : data[key];
            });

        },
        /**
         * @param str
         * @returns {*}
         */
        trimLeft: function (str) {
            return str.replace(/^\s*/g, '');
        },
        /**
         * @param str
         * @returns {*}
         */
        trimRight: function (str) {
            return str.replace(/\s*$/g, '');
        },
        /**
         * 去掉所有的空格(两边的空格), 可以针对任意格式的字符串
         * 先去掉左边的空格,然后去掉右边的空格
         * @param str
         * @returns {*}
         */
        trim: function (str) {
            // var regx = '/^\s*\s*$/g';
            // return str.replace(regx, '');
            // | 表示或的意思, 也就是满足| 左边的也成立, 满足 | 右面的也成立
            // (^\s*) 表示的就是以0个空格或者多个空格开头
            // (\s*$) 的意思就是, 以0个空格或者多个空格结尾
            // /…/g 是正则表达式的属性, 表示全文匹配, 而不是找到一个就停止
            return str.replace(/(^\s*)|(\s*$)/g, "");
            //return this.trimRight(this.trimLeft(str));
        },
        /**
         * 发送一个ajax请求
         * @param url  请求的URL地址信息
         * @param fn, 请求成功的回调函数
         */
        ajax: function (url, fn) {
            // 创建一个XMLHTTPRequest对象
            var xhr = createXHR();
            // 每当 readyState 改变时,就会触发 onreadystatechange 事件。
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4) {
                    // 接受到响应之后,第一步检查status属性,为200则表明成功,此时responseText已经准备就绪;
                    // 为304表明请求资源未被修改,可以直接使用浏览器中的缓存版本。
                    if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
                        fn(xhr.responseText);
                    } else {
                        alert('错误的文件!');
                    }
                }
            };


            // 定义请求参数, 对于指定的url发送一个get请求
            xhr.open('get', url, true);
            // 发送请求
            // 第三个参数:指示请求使用应该异步地执行。
            // 如果这个参数是 false,请求是同步的,后续对 send() 的调用将阻塞,直到响应完全接收。
            // 如果这个参数是 true 或省略,请求是异步的,且通常需要一个 onreadystatechange 事件句柄。
            xhr.send();


            /**
             *   创建一个XHR
             */
            function createXHR() {
                //本函数来自于《JavaScript高级程序设计 第3版》第21章
                if (typeof XMLHttpRequest != "undefined") {
                    return new XMLHttpRequest();
                } else if (typeof ActiveXObject != "undefined") {
                    // arguments.callee用于指向他的回调函数
                    if (typeof arguments.callee.activeXString != "string") {
                        var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0",
                                "MSXML2.XMLHttp"
                            ],
                            i, len;

                        for (i = 0, len = versions.length; i < len; i++) {
                            try {
                                new ActiveXObject(versions[i]);
                                arguments.callee.activeXString = versions[i];
                                break;
                            } catch (ex) {
                                //skip
                            }
                        }
                    }

                    return new ActiveXObject(arguments.callee.activeXString);
                } else {
                    throw new Error("No XHR object available.");
                }
            }


        },
        /**
         * json转换为字符串
         * @param json
         * @returns {string}
         */
        json2String: function (json) {
            return JSON.stringify(json);
        },
        /**
         * 字符串转换为json
         * @param str
         * @returns {any}
         */
        string2Json: function (str) {
            return eval(str);
        }
    });


    /*数组相关*/
    xframe.extend(xframe, {
        /**
         * 将一个数组清空,并返回数组的引用
         * 只需要把数组的元素置空为0即可
         * @return {xframe}
         */
        clear: function () {
            this.length = 0;
            return this;

        },
        /**
         * 返回数组的第0个元素
         * @return {*}
         */
        first: function () {
            return this[0];

        },
        /**
         * 返回数组的最后一个元素
         * @return {*}
         */
        last: function () {
            return this[this.length - 1];
        },
        /**
         * 计算一个数组的大小尺寸
         * @return {number|*}
         */
        size: function () {
            return this.length;
        },
        cacl: function (arr, callback) {
            var ret;
            for (var i = 0; i < arr.length; i++) {
                // 专门用于处理每一项的计算机过程
                ret = callback(arr[i], ret);
            }
            return ret;
        },
        /**
         * 对数组里面的所有元素求和
         * @return {*}
         */
        sum: function () {
            // 1. 正常写法
            var ret;
            for (var i = 0; i < this.length; i++) {
                ret = ret + this[i];
            }
            return ret;
        },
        max: function () {

        },
        min: function () {

        },
        avg: function () {

        },
        intersect: function () {

        },
        union: function () {

        },
        diff: function () {

        },
        unique: function () {

        },
        forEach: function () {

        },
        map: function () {

        },
        filter: function () {

        },
        every: function () {

        },
        some: function () {

        },
        reduce: function () {

        },
        reduceRight: function () {

        },
        indexOf: function () {

        },
        lastIndexOf: function () {

        },
        enhanceUnique: function () {

        },
        without: function () {

        },
        flatten: function () {

        },
        random: function () {

        },
        removeAt: function () {

        },
        contains: function () {

        }
    });


    /*Math*/
    xframe.extend(xframe, {
        random: function () {

        }

    });


    /*数据类型检验*/
    xframe.extend(xframe, {
        // 鸭子类型(duck typing)如果它走起路来像鸭子,叫起来也是鸭子,那么它就是鸭子。
        // 只关注对象的行为,不关注对象本身面向接口编型 ,而不是面向实现编程,是设计模式中最重要的思想。
        // 【理解】:一个对象有效的语义,不是由集成自特定的类或实现特定的接口, 而是由当前方法和属性的集合决定的!!!
        isNumber: function (val) {
            // 如果这个数字是有限的话, 而且是数字类型
            return (typeof val === 'number' && isFinite(val)) && (Object.prototype.toString.call(val) === '[object Number]');
        },
        /***
         * 判断一个变量是不是Boolean类型
         * @param val
         * @returns {boolean}
         */
        isBoolean: function (val) {
            return (typeof val === 'boolean') && (Object.prototype.toString.call(val) === '[object Boolean]');
        },
        /**
         * 判断一个变量是不是字符串类型
         * @param val
         * @returns {boolean}
         */
        isString: function (val) {
            return (typeof val === 'string') && (Object.prototype.toString.call(val) === '[object String]');
        },
        /**
         * 判断一个变量是不是undefined
         * @param val
         * @returns {boolean}
         */
        isUndefined: function (val) {
            // oid 0 is a correct and standard way to produce undefined.
            return (val === void 0) || (typeof val === 'undefined') && (Object.prototype.toString.call(val) === '[object Undefined]');
        },
        /**
         * 判断一个变量是不是为空
         * @param val
         * @returns {boolean}
         */
        isNull: function (val) {
            return (val === null) && (Object.prototype.toString.call(val) === '[object Null]');
        },
        /**
         * 检测
         * @param obj
         * @returns {*}
         */
        isNaN: function (val) {
            // 只要这个数字通过判断是不是和他自身相同或者使用typef的方式去检测
            return val !== val;
        },
        /**
         * 判断一个变量是不是一个对象类型
         * @param val
         * @returns {boolean}
         */
        isObject: function (val) {
            if (val !== null && val !== undefined) {
                if ((typeof val === 'object') && (Object.prototype.toString.call(val))) {
                    return true;
                }
            }
            return false;
        },
        /**
         * 判断一个对象是不是数组对象
         * @param val
         * @returns {boolean|void|string}
         */
        isArray: function (val) {
            // 判断上不是一个数组的先判断这个数组对象是不是为空, 因为如果val为空的话,就是val.constructor这个属性实际上是没有的,error
            if (val !== null || typeof val !== "undefined") {
                // 注意在使用constructor判断数据类型的时候比较的实际上是他的原型对象的constructor属性, 这个属性指向的实际上是这个变量的原型对象
                return (val.constructor === Array) && (Object.prototype.toString.call(val));
            }
            return false;
        }

    });


    /*数组化:arguments, document.forms, document.getElementsByName, document.getElementsByTagName()*/
    xframe.extend(xframe, {
        /**
         * 把一个伪数组转换为一个新的数组
         * 实现思路: 取出伪数组中的每一个元素, 然后把取出来的这些元素重新放入到一个新的数组里面去!!!
         * @param start
         * @param end
         * @returns {Array}
         */
        toArray: function (start, end) {
            var result = [];
            var start = start || 0,
                // 这里的this指向调用的对象,使用了call之后, 改变了this的指向, 指向传进来的对象(外边必须要修改this的指向)
                // 如果外边不修改this的指向,这里的this默认指向的是xframe这个框架对象
                end = end || this.length;
            for (var i = start; i < end; i++) {
                result.push(this[i]);
            }
            return result;
        },

        /**
         * 方法二: 直接把一个伪数组转换为JavaScript中的一个数组对象
         * @param obj
         * @returns {T[]}
         */
        slice: function (obj) {
            return Array.prototype.slice.apply(obj);
        }
    });

    /*domReady的实现*/
    xframe.extend(xframe, {
        //arguments 的主要用途是保存函数参数, 但这个对象还有一个名叫 callee 的属性,该属性是一个指针,指向拥有这个 arguments 对象的函数
        /**
         * 实现一个domReady方法:所有元素都加载完毕之后一个回调函数
         * @param domElement
         * @param fn
         */
        onDOMReady: function (fn) {
            if (document.addEventListener) {
                // W3C组织: 如果传过来的是一个DOM元素的话,就直接对这个DOM元素添加监听, 否则,就对整个document添加事件监听
                document.addEventListener('DOMContentLoaded', fn, false);
            } else {
                // IE浏览器
                IEContentLoaded(fn);
            }


            /**
             * 微软的IE浏览器的处理方法
             * @param fn
             * @constructor
             */
            function IEContentLoaded(fn) {
                // 定义需要的全局变量
                var done = false, document = window.document;


                // 这个函数只会在所有的DOM节点树创建完毕的时候才会继续向下执行
                var init = (function () {
                    if (!done) {
                        console.log('done……');
                        // 如果DOM树创建完毕的话
                        done = true;
                        fn();
                    }
                })();


                /*
                使用这个立即函数来调用IE浏览器的内置函数实现domReady的功能
                 */
                (function () {
                    try {
                        // DOM树在未创建完毕之后调用 doScroll的话,会抛出错误
                        document.documentElement.doScroll('left');

                    } catch (err) {
                        // 延迟1秒之后再次执行这个函数, 形成一个函数递归调用的功能【回调函数】
                        // clllee是一个函数指针,指向的是拥有这个arguments对象的函数, 从而实现再次调用这个函数
                        setTimeout(arguments.callee, 1);
                        return;
                    }

                    // 如果没有错误的话,表示DOM树已经完全创建完毕, 此时开始执行用户的回调函数
                    init();
                })();

                // 监听document的加载状态(DOM加载的过程中会不断回调这个函数)
                document.onreadystatechange = function () {
                    console.log('onreadystatechange……');
                    if (document.readyState === 'complete') {
                        console.log('complete……');
                        // 如果加载完成的话
                        document.onreadystatechange = null;
                        init();
                    }
                }
            }
        }
    });
})(xframe);

猜你喜欢

转载自blog.csdn.net/m0_37981569/article/details/81056006