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

向善的燈發表於2018-07-15
版權宣告:本文為博主原創文章,未經博主允許不得轉載。更多學習資料請訪問我愛科技論壇:www.52tech.tech https://blog.csdn.net/m0_37981569/article/details/81056006
/*
* @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,等價於[

	f]
            S: 匹配非空格字元,等價於[^ 

	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);


相關文章