2017-04-17 jQuery 原理

jQuery 的內(nèi)部實(shí)現(xiàn)

  • 課程目標(biāo)

  • 需要掌握
    了解常用方法的使用
    掌握常用方法的底層實(shí)現(xiàn)邏輯
    學(xué)會(huì)根據(jù)一個(gè)目標(biāo)需求,來根據(jù)自己的思想編寫出大致實(shí)現(xiàn)
    了解一些編寫框架的技巧,比如為什么用閉包,為什么傳遞 window 參數(shù)
    了解一些機(jī)制,比如插件機(jī)制,如何擴(kuò)展一些方法或者屬性
    要求掌握面向?qū)ο蟮乃枷肴ソ鉀Q問題,熟練了解原型, this 指針等概念

  • 整體是一個(gè)執(zhí)行的閉包
    這樣寫的主要目的是為了防止內(nèi)部一些功能被污染

  • 傳遞 window 參數(shù)
    是為了方便亞索,以及提高檢索效率

  • 內(nèi)部接收 undefined
    是為了放置,外界修改 undefined 所代表的含義

  • 內(nèi)部的參數(shù),如何讓外界可以訪問
    可以通過給 window 對(duì)象,動(dòng)態(tài)的增加屬性,暴露給外界調(diào)用

(function(){
        //1.定義構(gòu)造函數(shù)
        var szjQuery = function(selector){
                //內(nèi)部 new 了一個(gè)對(duì)象返回給外界,讓外界使用方便,沒必要再創(chuàng)建
                return new szjQuery.prototype.init(selector);
        }
        //2.修改函數(shù)的原型
        //以后,可以在這個(gè)位置擴(kuò)展一些屬性,或者方法
        //注意:這里有個(gè)比較重要的方法 init 方法,基本上所有的創(chuàng)建 jQuery 對(duì)象初始化的邏輯,都是在這個(gè)函數(shù)里面進(jìn)行處理的
        szjQuery.prototype = {
                szjQuery:'1.1.0',
                selector:'',
                length:0,
                constructor:szjQuery,
                init:function(){
                        //this.length = 0;
                        //在這里處理 selector 傳進(jìn)來的參數(shù)
                        //1.特殊字符 ‘’ null undefined NaN 0 false
                        if(!selector){
                                return this;
                        } 
                        //2.判斷是否是字符串
                        if($.isString(selector)){
                                //2.1要不就是代碼片段,去空格處理
                                var result = $.trimString(selector);
                                if($.isHTML(result)){
                                        var tag = document.createElement('div');
                                        tag.innerHTML = selector;
                                        var firstChildren = tag.children;
                                        //call  apply 作用重新控制 this 指向,此處可以使用遍歷然后控制 this[i] = firstChildren[i] 來實(shí)現(xiàn)
                                        [].push.apply(this,firstChildren);
                                        return this;
                                }
                                //2.2要不就是選擇器
                                var tags = document.querySelectorAll(selector);
                                [].push.apply(this,tags);
                                return this;      
                        } 
                        //3.判定是否是偽數(shù)組/真數(shù)組
                        if($.isWindow(selector) && $.isLikeArray(selector)){
                                [].push.apply(this,selector);
                                return this;    
                        } 
                        //4.判斷是否是一個(gè)函數(shù)
                        if($.isFunction(selector)){
                                $.ready(selector);
                        } 
                        //其他
                        //DOM 基本對(duì)象 基本數(shù)據(jù)類型 1234
                        this[0] = selector;
                        this.length = 1;
                        return this;                    
                }        
        };
        //3.創(chuàng)建一個(gè)快速批量創(chuàng)建靜態(tài)方法/實(shí)例方法的方法
        //因?yàn)閮?nèi)部的 this ,是動(dòng)態(tài)指向調(diào)用者的
        //所以,可以借助這個(gè)特性,來動(dòng)態(tài)的給對(duì)象或者方法添加方法
        szjQuery.extend = szjQuery.prototype.extend = function(funDic){
                for(var key in funDic){
                        this[key] = funDic[key];
                }        
        }
        //4.重新修改 init 函數(shù)的原型指向,避免方法調(diào)用不到的問題
        //此處必須了解以下幾點(diǎn)
        //4.1每個(gè)函數(shù)都有對(duì)應(yīng)的原型對(duì)象
        //4.2通過什么構(gòu)造函數(shù),創(chuàng)建出來的實(shí)例對(duì)象,這個(gè)對(duì)象的 __proto__ 就指向該構(gòu)造函數(shù)的原型對(duì)象
        //4.3當(dāng)調(diào)用一個(gè)對(duì)象方法的時(shí)候,是根據(jù) __proto__ 來進(jìn)行查找方法的
        //4.4所以,當(dāng)一個(gè)對(duì)象找不到方法,應(yīng)該注意查看該對(duì)象的構(gòu)造函數(shù)的
        //4.5注意,任何一個(gè)函數(shù)的原型,都可以修改
        szjQuery.prototype.init.prototype = szjQuery.prototype;
        //通過 window 對(duì)象,將內(nèi)部對(duì)象暴露給外界
        //因?yàn)椋旧希械墓δ芏际且栏接?jQuery 對(duì)象存在的
        //所以,可以jQuery 對(duì)象給外界,然后其他的信息,會(huì)被間接的暴露出去
        window.szjQuery = window.$ = szjQuery;
        //6.擴(kuò)展靜態(tài)方法
        //工具類,判斷類的是否是字符串,是否是對(duì)象等等
        szjQuery.extend({
               //是否是字符串方法
               'isString':function(){
                        return typeof str === 'string';
                },
                //可以壓縮字符串的首尾空格
                //此處注意兼容,以及正則表達(dá)式的使用
                'trimString':function(str){
                        if(str.trim){
                                return str.trim();
                        }
                        return str.replace(/^\s+|\s+$/g, '');
                },
                //判斷是否是窗口過對(duì)象
                'isWindow': function(w){
                        return window.window !== w;
                },
                //是否是偽數(shù)組
                'isLikeArray':function(){
                        return (this.isObject(arr) && 'length' in arr && (arr.length - 1) in arr);
                },    
                //是否是對(duì)象
                'isObject':function(obj){
                        return typeof obj === 'object';
                },
                //是否是代碼片段
                //注意空格的容錯(cuò)處理
                'isHTML':function(html){
                        return html.charAt(0) === '<' && html.charAt(html.length - 1) === '>' &&html.length > 3
                },
                //是否是函數(shù)
                'isFunction':function(fun){
                        return typeof fun === 'function';
                },
                //是否是 DOM 對(duì)象
                'isDOM':function(dom){
                        if('nodeType' in dom){
                                return true;
                        }
                        return false;
                },
                //入口函數(shù)的處理
                //需要注意執(zhí)行時(shí)機(jī),以及執(zhí)行次數(shù)
                'ready':function(func){
                        if(!$.isFunction(func)){
                                return;
                        }
                        //1.判斷當(dāng)前文檔是否已經(jīng)加載完畢
                        //直接執(zhí)行
                        //document.readyState 可以兼容當(dāng)前所有主流瀏覽器
                        var state = document.readyState;
                        if(state === 'complete'){
                                func();
                                return;
                        }
                        //2.如果沒有加載完畢
                        //監(jiān)聽 DOM 樹建立完成
                        // IE9+
                        if(document.addEventListener){
                                document.addEventListener('DOMContentloaded',func);
                                return;
                        }
                        // IE8
                        document.attachEvent('onreadystatechange',function(){
                                var state = document.readyState;
                                if(state === 'complete'){
                                        func();
                                }
                        });
                }
        });
//擴(kuò)展實(shí)例方法
//數(shù)組相關(guān)的一些方法
//比如轉(zhuǎn)換稱為數(shù)組,遍歷,切割數(shù)組等等
//此處需要掌握,數(shù)組操作的常用方法的內(nèi)部實(shí)現(xiàn)
szjQuery.prototype.extend({
        //將偽數(shù)組,轉(zhuǎn)換成為真數(shù)組
        'toArray':function(){
                var result = [].slice.call(this);
                return result;
          //
            // begin = begin === undefined ? 0 : begin;
            // end = (end === undefined || end > this.length) ? this.length: end;
            //
            // var tmpArr = [];
            // for (var i = begin; i < end; i++) {
            //     tmpArr.push(this[i]);
            // }
            //
            // return tmpArr;
        },
        //7.2獲取某個(gè)一個(gè) DOM 元素
        //注意正負(fù)索引值的處理
        'get':function(index){
                if(index === undefined){
                        return this.toArray();
                }
                index = index >=0?index:index + this.length;
                return this[index];
            // if (index >= 0) {
            //     return this[index];
            // }
            // // -1 +
            // index = index + this.length;
            // return this[index];
        },
        //7.3獲取指定的 DOM 元素,包裝的 jQuery 對(duì)象
        //可以考慮,借助其他已經(jīng)實(shí)現(xiàn)過的方法,進(jìn)行處理
        //因?yàn)椋瑑?nèi)部的邏輯大致和 get 方法類似
        'eq':function(index){
                if(index === undefined){
                        return $('');
                }
                return $(this.get(index));
        },
        //7.4獲取第一個(gè)的 DOM 元素,包裝的 jQuery 對(duì)象
        //可以考慮,借助其他已經(jīng)實(shí)現(xiàn)國(guó)的方法,進(jìn)行處理
        //因?yàn)椋瑑?nèi)部的邏輯,大致和 get 方法類似
        'first':function(){
                return this.eq(0);
        },
        //7.5 獲取最后一個(gè)的 DOM 元素,包裝 jQuery 對(duì)象
        //可以考慮,借助其他已經(jīng)實(shí)現(xiàn)的方法,進(jìn)行處理
        //因?yàn)椋瑑?nèi)部的邏輯,大致和 get  方法類似
        'last':function(){
                return this.eq(-1);
        },
        //[].splice 獲取的是方法實(shí)現(xiàn)
        //7.6一定要注意,jQuery 對(duì)象里面的,這些方法的實(shí)現(xiàn),其實(shí)和數(shù)組的這些方法實(shí)現(xiàn)一模一樣
        //所以,此處,可以直接把數(shù)組對(duì)應(yīng)方法的實(shí)現(xiàn)放在這里
        //這樣,到時(shí)候,外界調(diào)用的時(shí)候,就會(huì)按照相同的操作數(shù)組的邏輯,來操作 jQuery 對(duì)象
        'splice':[].solice,
        'push':[].push,
        'sort':[].sort,
        'each':function(func){
                $.each(this,func);
        },
        'reverse':function(){
                var result = [];
                for(var i = this.length - 1 ; i >= 0;i--){
                       result.push(this[i]);                     
                }
                return $(result);
        }
});
//8.靜態(tài)方法
//數(shù)組相關(guān)
szjQuery.extend({
        //8.1 遍歷數(shù)組,或者偽數(shù)組,或者對(duì)象
        //內(nèi)部注意不同類型的處理
        //還有就是,此方法,支持對(duì)象方法,和靜態(tài)方法
        //一般碰到這種情況,我們優(yōu)先開發(fā)靜態(tài)方法,然后再在實(shí)例方法中,調(diào)用靜態(tài)方法實(shí)現(xiàn)即可
        'each':function(obj,func){
                if($.isLikeArray(obj)){
                        for(var i = 0;i < obj.length;i++){
                                //func(i,obj[i]);
                                var isContinue = func.call(obj[i],i,obj[i]);
                                //isContinue = isContinue !== undefined ? isContinue : true;
                                if(isContinue === undefined){
                                        isContinue = true;
                                }
                                if(!isContinue){
                                        return;
                                }
                        }
                        return;
                }
                //不是偽數(shù)組
                if($.isObject(obj)){
                        for(var key in obj){
                                //func(key,obj[key]);
                                var isContinue = func.call(obj[key],key,obj[key]);
                                isContinue = isContinue !== undefined ? isContinue:true;
                                if(!isContinue){
                                        return;
                                }
                        }
                }
        },
        //8.2可以將一個(gè)字符串的空格,都給亞索,然后把里面的有效元素,存儲(chǔ)到一個(gè)數(shù)組中
        //可以用來做一些格式化的操作
        'trimSpaceArrayWithStr':function(str){
                var resultArray = [];
                var temp = str.split(' ');
                for(var i = 0 ; i < temp.length;i++){
                        var value = temp[i];
                        if(value.length > 0){
                                resultArray.push(value);
                        }
                }
                return resultArray;
        }
});
//DOM 操作的相關(guān)方法
//注意:如果想要仿照一個(gè)功能
//先對(duì)這個(gè)功能進(jìn)行充分的測(cè)試
//報(bào)錯(cuò)參數(shù) 參數(shù)個(gè)數(shù) 參數(shù)類型 返回值等信息
szjQuery.prototype.extend({
        //9.1 清空 DOM 對(duì)象里面的內(nèi)容
        'empty':function(){
                this.each(function(){
                        //index value  value
                        //this 就是每一個(gè) DOM 對(duì)象
                        this.innerHTML = '';
                });
                return this;
        },
        //9.2 刪除指定的 DOM 對(duì)象
        'remove':function(){
                this.each(function(){
                        //this 是每一個(gè) DOM 對(duì)象
                        this.parentNode.removeChild(this);
                });
                return this;
        },
        //9.3獲取指定元素的 html 內(nèi)容
        'html':function(content){
                if(content === undefined){
                        return this.get(0).innerHTML;
                }
                this.each(function(){
                        this.innerHTML = content;
                });
                return this;
        },
        //9.3 獲取指定元素的 text 內(nèi)容
        'text':function(content){
                if(content === undefined){
                        var resultStr = '';
                        this.each(function(){
                                resultStr += this.innerText;
                        });
                        return resultStr;
                }
                if($.isFuntion(content)){
                        this.each(function(index,value){
                                this.innerText = content(index,this.innerText)
                        });
                        return this;
                }
                this.each(function(){
                        this.innerText = content;
                });
                return this;
        },
        //9.4追加元素到另外一個(gè)元素中
        //下面幾個(gè)方法類似
        //注意,測(cè)試先后順序,分清插入的位置即可
        //注意核心代碼的實(shí)現(xiàn),然后再擴(kuò)展業(yè)務(wù)邏輯
        'appendTo':function(content){
                content = $(content);
                var source = this;
                var target = content;
                target.each(function(t_index,t_value){
                        source.each(function(s_index,s_value){
                                if(t_index == 0){
                                        t_value.appendChild(s_value);
                                }else{
                                        var clone = s_value.cloneNode(true);
                                        t_value.appendChild(clone);
                                }
                        })
                });
                return this;
        },
        ‘prependTo’:function(content){
                content = $(content);
                var source = this;
                var target = content;
                target.each(function(t_index,t_value){
                        source.reverse().each(function(s_index,s_value){
                                  if(t_index == 0){
                                          //t_value.appendChild(s_value);
                                          t_value.inertBefore(s_value,t_value.firstChild);
                                  }else{
                                          var clone = s_value.cloneNode(true);
                                          //t_value.appendChild(clone);
                                          t_value.insetBefore(clone,t_value.firstChild);
                                  }
                          })
                });
                return this;
        },
        'prepend':function(content){
                if($.isObject(content)){
                      var source = content;
                      var target = this;
                      source.prependTo(target);
                      return this;          
                }
                this.each(function(){
                        // this == DOM 對(duì)象
                        this.innerHTML = content + this.innerHTML;
                });
                return this;
        },
        'append':function(content){
                if($.isObject(content)){
                        var source = content;
                        var target = this;
                        source.appendTo(target);
                        return this;
                }
                this.each(function(){
                        //this == DOM 對(duì)象
                        this.innerHTML = this.innerHTML + content;
                });
                return this;
        }
});
//10.DOM 樣式的獲取
//注意瀏覽器兼容
//此處需要掌握:了解樣式的分類,以及基本的獲取方式
szjQuery.extend({
        'getStyle':function(dom){
                if($.isDOM(dom)){
                        var finalStyle = dom.currentStyle ? dom.currentStyle : window.getComputedStyle(dom,null);
                        return finalStyle;
                }
        }
});
/*
*11.DOM 節(jié)點(diǎn)屬性的操作
*包括,操作屬性,操作節(jié)點(diǎn)屬性,修改/獲取 CSS 樣式
*獲取/設(shè)置 value 值
*判斷類,新增、刪除、切換類
*事件添加和移除
*注意:
*此段代碼的開發(fā),思路如下
*1.需要考慮核心的代碼是什么?
*2.大部分都是批量操作,所以,從小工能開始實(shí)現(xiàn),然后慢慢按照需求進(jìn)行擴(kuò)展
*3.千萬(wàn)不要一次性的從最大的功能開始左
*4.不要求實(shí)現(xiàn)的一模一樣,只需要直到一個(gè)大概邏輯,有個(gè)簡(jiǎn)單基本實(shí)現(xiàn)就好
*5.容錯(cuò)性邏輯,最后統(tǒng)一處理,放置我們觀察主要邏輯
*6.關(guān)于一些類名的判斷,全部都是靠字符串之間的關(guān)系進(jìn)行處理
*注意容錯(cuò)
*/
szjQuery.prototype.extend({
        //操作節(jié)點(diǎn)屬性
        'attr':function(){
                if(arguments.length === 1){
                        var value = arguments[0];
                        //1.如果是字符串
                        if($.isString(value)){
                                //獲取第一個(gè) DOM 對(duì)象的對(duì)應(yīng)的節(jié)點(diǎn)屬性值
                                return this[0].getAttribute(value);
                        }
                        //2.如果是對(duì)象
                        if($.isObject(value)){
                                //批量的設(shè)置 DOM 對(duì)象里面的節(jié)點(diǎn)屬性值
                                this.each(function(index,dom){
                                        $.each(value,function(key,value){
                                                dom.setAttribute(key,value);
                                        })
                                });
                                return this;     
                        }
                }
                //arguments,this,return
                if(arguments.length === 2 && $.isString(arguments[0])){
                        var key = arguments[0];
                        var value = arguments[1];
                        //遍歷所有的 DOM 對(duì)象
                        this.each(function(index,dom){
                                dom.setAttribute(key,value);
                        });
                        return this;
                }
                throw '請(qǐng)輸入正確的參數(shù)';
        },
        'removeAttr':function(){
                var firstParam = arguments[0];
                if($.isString(firstParam)){
                        this.each(function(index,dom){
                                dom.removeAttribute(firstParam);
                        })
                }
                return this;
        },
        'prop':function(){
                if(arguments.length === 1){
                        var value = arguments[0];
                        //1.如果是字符串
                        if($.isString(value)){
                                //獲取第一個(gè) DOM 對(duì)象的對(duì)應(yīng)的屬性值
                                return this[0][value];
                                //this['name'];
                                //this.name
                        }
                        //2.如果是對(duì)象
                        if($.isObject(value)){
                                //批量的設(shè)置 DOM 對(duì)象里面的屬性值
                                this.each(function(index,dom){
                                        $.each(value,function(key,value){
                                                dom[key] = value;
                                        })
                                })
                                return this;
                        }
                }
                if(arguments.length === 2 && $.isString(arguments[0])){
                        var key = arguments[0];
                        var value = arguments[1];
                        //遍歷所有的 DOM 對(duì)象
                        this.each(function(index,dom){
                                dom[key] = value;
                        });
                        return this;
                }
                throw '請(qǐng)輸入正確的參數(shù)';
        },
        'removeProp':function(){
                var firstParam = arguments[0];
                if($.isString(firstParam)){
                        this.each(function(index,dom){
                                delete dom[firstParam];
                        })
                }
                return this;
        },
        'CSS':function(){
                if(arguments.length === 1){
                        var value = arguments[0];
                        //1.如果是字符串
                        if($.isString(value)){
                                //value == background width
                                //獲取第一個(gè) DOM 對(duì)象里面的樣式屬性
                                var dom = this[0];
                                return $.getStyle(dom)[value];
                        }
                        //2.如果是對(duì)象
                        if($is.Object(value)){
                                //批量的設(shè)置 DOM 對(duì)象里面的批量樣式屬性值
                                this.each(function(){
                                        $.each(value,function(index,dom){
                                                //dom.setAttribute(key,value);
                                                dom.style[key] = value;
                                        })
                                });
                                return this;
                        }
                }
                if(arguments.length === 2 && $.isString(arguments[0])){
                        var key = arguments[0];
                        var value = arguments[1];
                        //遍歷所有的 DOM 對(duì)象
                        this.each(function(index,dom){
                                dom.style[key] = value;
                        });
                        return this;
                }
                throw '請(qǐng)輸入正確的參數(shù)';
        },
        'val':funciton(firstParam){
                if(firstParam === undefined){
                        var dom = this[0];
                        //return dom.getAttribute('value');  這種寫法是錯(cuò)誤的無法實(shí)時(shí)獲取更新了的數(shù)值
                        return dom['value'];
                }else{
                        //批量的給所有的 DOM 對(duì)象,賦值 value
                        this.each(function(index,dom){
                                dom['value'] = firstParam;
                        });
                        return this;
                }
        },
        ‘hasClass’:function(firstParam){
                if(firstParam === undefined){
                        return false;
                }
                // 'box111' 'box1' '  box1'
                // ' box1 ' 
                if($.isString(firstParam)){
                        var trimClassName = ' ' + $.trimString(firstParam) + ' ';
                        var hasClass = false;
                        this.each(function(index,dom){
                                var parentClassName = ' ' + $.trimString(dom.className) + ' ';
                                var searchIndex = parentClassName.indexOf(trimClassName);
                                  if(searchIndex >= 0){
                                            hasClass = true; 
                                            //結(jié)束遍歷
                                            return false'     
                                  }
                        });
                        return hasClass;
                }
        },
        'addClass':function(firstParam){
                if(firstParam === undefined){
                        return this;
                }
                if($.isString(firstParam)){
                        //1.遍歷所有的 dom 對(duì)象
                        this.each(function(index,dom){
                                //先獲取給定的類名數(shù)組
                                var classNameArr = $.trimSpaceArrayWithStr(firstParam);
                                $.each(classNameArr,function(index,className){
                                        if(!$(dom).hasClass(className)){
                                                dom.className = dom.className + ' ' + className;
                                        }
                                });
                                //格式化, dom 的類名
                                var resultClassNames = $.trimSpaceArrayWithStr(dom.className);
                                //按照指定的字符,鏈接所有的元素,-》字符串,就是 join 的作用
                                  dom.className = resultClassNames.join(' ');
                        });
                }
                return this;
        },
        'removeClass':function(firstParam){
                if(firstParam === undefined ){
                        //清空所有 DOM 對(duì)象里面的類名
                        this.each(function(index,dom){
                                dom.className = '';
                        });
                        return this;
                }
                //'box1 box2'
                //'box1'
                //'box1 box2'
                if($.isString(firstParam)){
                        //'box1'
                        //var searchClassName = ' ' + $.trimString(firstParam) + ' ';
                        //'   box1   box2  '
                        //['box1','box2']
                        var searchClassNames = $.trimSpaceArrayWithStr(firstParam);
                        //遍歷所有的 DOM 對(duì)象
                        this.each(function(index,dom){
                                //判斷 DOM 對(duì)象的 className,是否包含指定的類名
                                var parentClassName = ' ' + dom.className + ' ';
                                //針對(duì)于每一個(gè) DOM 對(duì)象
                                //刪除,一個(gè)類名 集合
                                $.each(searchClassNames,function(){
                                          //如果包含,則刪除
                                          if($(dom).hasClass(searchClassName)){
                                                  parentClassName = parentClassName.replace(searchClassName,'
 ');
                                          }
                                  });
                                  //代表該刪除的,已經(jīng)刪除完畢
                                  //dom.className = parentClassName;
                                  //['box1','box2','box3']
                                  //'box1 box2 box3'
                                  var classNameArr = #.trimSpaceArrayWithStr(parentClassName);
                                  dom.className = classNameArr.join(' ');
                        })
                }
                return this;
        },
        'toggleClass':function(firstParam){
                if(firstParam  === undefined){
                        this.each(function(index,dom){
                                if(dom.className.length > 0){
                                        //永遠(yuǎn)有值
                                        dom['orginClassName'] = dom.className;
                                }
                                //格式化 className
                                var resultClassName = dom.className === ' ' ? dom['orginClassName'] : ' ';
                                dom.className = $trimSpaceArrayWithStr(resultClassName).join(' ');        
                        });
                        return this;
                }
                if($.isString(firstParam)){
                        //firstParam == '   box1   box2 '
                        var searchClassNames = $.trimSpaceArrayWithStr(firstParam);
                        //firstParam == 'box1'
                        //讓每一個(gè) DOM 對(duì)象,都切換給定的類名
                        this.each(function(index,dom){
                                  $.each(searchClassName,function(index,searchClassName){
                                        //判斷是否有這個(gè)類名
                                        //有就刪除沒有就添加
                                        if($(dom).hasClass(searchClassName)){
                                                $(dom).removeClass(searchClassName);
                                        }else{
                                                $(dom).addClass(searchClassName);
                                        }
                                })
                        });
                        return this;
                }
        }
});
/*
*12 添加基本的事件監(jiān)聽和移除方法
*注意瀏覽器的兼容
*以及以后開發(fā)工具類的時(shí)候,工具類方法的類型
*是選擇對(duì)象方法,還是使用靜態(tài)方法
*主要區(qū)分依據(jù),就是看下在方法內(nèi)部是否可以借助 this 對(duì)象中的屬性獲取其他信息
*如果不需要,則統(tǒng)一使用靜態(tài)方法進(jìn)行實(shí)現(xiàn)
*/
$.extend({
        'addEvent':function(ele,type,func){
                //0.容錯(cuò)處理
                if(!$.isDOM(ele) || !$.isString(type) || isFunction(func)){
                        return;
                }
                //1.根據(jù)傳遞過來的參數(shù),進(jìn)行事件的監(jiān)聽
                if(ele.addEventListener){
                        ele.addEventListner(type,func)
                }else{
                        ele.attachEvent('on' + type ,func)
                }
                return;
        },
        'removeEvent':function(ele,type,func){
                if(!$.isDOM(ele) || $.isString(type) || $.isFunction(func)){
                        return;
                }
                //1.根據(jù)傳遞過來的參數(shù),進(jìn)行事件的監(jiān)聽
                if(ele.removeEventListener){
                        ele.removeEventListener(type,func);         
                }else{
                        ele.detachEvent('on' + type,func);
                }
                return;
        }
});
/*
*13.批量操作事件綁定,和事件解綁的方法
*此處注意開發(fā)步驟
*先從最基本,最小的功能開始左
*比如可以先看下一個(gè) dom 對(duì)象,添加一個(gè)事件類型,綁定一個(gè)回調(diào)函數(shù)的時(shí)候是如何實(shí)現(xiàn)的
*然后慢慢進(jìn)行擴(kuò)展多個(gè)類型的
*多個(gè)回調(diào)函數(shù)的
*多個(gè) DOM 對(duì)象的
*以此類推
*/
$.prototype.extend({
        //一個(gè)類型,一個(gè)回調(diào)函數(shù),多個(gè)DOM 對(duì)象的情況
        '_on':function(type,func){
                //批量給 DOM 對(duì)象,添加事件
                this.each(function(index,dom){
                        $.addEvent(dom,type,func)
                })
        },
        //多個(gè)類型,一個(gè)回調(diào)函數(shù),多個(gè) DOM 對(duì)象的情況
        '_on2':function(types,func){
                //批量給 DOM 對(duì)象,添加事件
                var typeArray = $.trimSpaceArrayWithStr(types);
                this.each(function(index,type){
                        $.each(typeArray,function(index,type){
                                $.addEvent(dom,type,func)
                        })
                })
        },
        //多個(gè)類型,多個(gè)回調(diào)函數(shù),多個(gè) DOM 對(duì)象的情況
        '_on3':function(firstParam){
                //當(dāng)事件綁定的時(shí)候
                if(arguments.length === 1){
                        if(!$.isObject(firstParam)){
                                return this;
                        }
                        this.each(function(index,dom){
                                $.each(firstParam,function(type,func){
                                        $.addEvent(dom,type,func);
                                })
                        });
                        return this;
                }
                if(arguments.length === 2){
                        //'click dbclick mouseover',func
                        //批量給 DOM 對(duì)象,添加事件
                        var typeArray = $.trimSpaceArrayWithStr(arguments[0]);
                        var func = arguments[1];
                        this.each(function(index.dom){
                                $.each(typeArray,function(index,type){
                                          $.addEvent(dom,type,func);
                                  })
                        })
                }
        },
        //為了存儲(chǔ),監(jiān)聽的事件信息,方便解綁
        'on':function(firstParam){
                //當(dāng)事件綁定的時(shí)候,把這個(gè) DOM 對(duì)象身上的事件類型,和事件回調(diào)都給記錄下來
                if(arguments.length === 1){
                        if(!$.isObject(firstParam)){
                                return this;
                        }
                        this.each(function(index,dom){
                                //1.給 dom, 新增一個(gè)屬性
                                dom['eventCache'] = dom['eventCache'] || [];
                                $.each(firstParam,function(type,func){
                                          $.addEvent(dom,type,func);
                                          dom['eventCache'][type] = dom['eventCache'][type] || [];
                                          dom['eventCache'][type].push(func);
                                  })
                        });
                        return this;
                }
                if(arguments.length ===2){
                        var typeArray = $.trimSpaceArrayWithStr(argument[0]);
                        var func = arguments[1];
                        this.each(function(index,dom){
                                dom['eventCache'] = dom['eventCache'] || [];
                                $.each(typeArray,function(index,type){
                                        $.addEvent(dom,type,func);
                                        dom['eventCache'][type] = dom['eventCache'][type] || [];
                                        dom['eventCache'][type].push(func);
                                })
                        })
                }
        },
        //解綁多個(gè) DOM 對(duì)象,一個(gè)事件類型的情況
        '_off':function(type){
                //type === 'click'
                this.each(funcntion(index,dom){
                        var funcs = dom['eventCache'][type];
                        $each(funcs,function(index,func){
                                $.removeEvent(dom,type,func);
                        })
                });
                return this;
        },
        //解綁多個(gè) DOM 對(duì)象,多個(gè)事件類型的情況
        '_off2':function(types){
                var typeArr = $.trimSpaceArrayWithStr(types);
                this.each(function(index,dom){
                          $.each(typeArr,function(index,type){
                                  //找到類型對(duì)應(yīng)的回調(diào)函數(shù)組
                                  var funcs = dom['eventCache'][type];
                                  //解綁函數(shù)
                                  $.each(funcs,function(index,func){
                                          $.removeEvent(dom,type,func)
                                  })
                          })
                });
                return this;
        },
/*
*解綁多個(gè)DOM 對(duì)象,一個(gè)事件類型 指定回調(diào)函數(shù)的情況
*注意:吃出的開發(fā),需要結(jié)合 on 方法內(nèi)部,先記錄下,當(dāng)前對(duì)象綁定的事件信息
*/
                'off': function (types, paramFunc) {
            //  type === 'click'
            var typeArr = $.trimSpaceArrayWithStr(types);

            // 遍歷所有的DOM對(duì)象
            this.each(function (index, dom) {
                console.log(dom['eventCache']);
                // 遍歷所有的事件類型
                $.each(typeArr, function (index, type) {
                    // 找到類型對(duì)應(yīng)的回調(diào)函數(shù)數(shù)組
                    var funcs = dom['eventCache'][type];

                    // 解綁函數(shù)
                    // 遍歷所有的函數(shù)
                    $.each(funcs, function (index, func) {

                        // 處理指定函數(shù)這個(gè)參數(shù)沒有傳遞的情況
                        if (paramFunc === undefined) {
                            $.removeEvent(dom, type, func);
                        }
                        // 處理指定函數(shù) 這個(gè)參數(shù)傳遞的情況
                        if (func === paramFunc) {
                            // 解綁
                            $.removeEvent(dom, type, func);
                        }
                    })
                })
            });
            return this;

        }
})
})(window)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,327評(píng)論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,996評(píng)論 3 423
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,316評(píng)論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,406評(píng)論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,128評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,524評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,576評(píng)論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,759評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,310評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,065評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,249評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,821評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,479評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,909評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,140評(píng)論 1 290
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,984評(píng)論 3 395
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,228評(píng)論 2 375

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,690評(píng)論 25 708
  • 在線閱讀 http://interview.poetries.top[http://interview.poetr...
    前端進(jìn)階之旅閱讀 114,536評(píng)論 24 450
  • 中學(xué)時(shí)代,我羨慕道明寺對(duì)杉菜的霸道,羨慕湘琴對(duì)直樹的執(zhí)著,羨慕李逍遙對(duì)趙靈兒的深情。 我經(jīng)常會(huì)望著教室窗外傻傻發(fā)呆...
    雪落硯閱讀 603評(píng)論 0 0
  • 第一次畫蓮花。以前看那些孩子怎么可以把蓮花畫的那么漂亮,為了避免自尊心受到傷害,所以一直不敢嘗試,今天翻來翻去還是...
    碎碎碎發(fā)隨風(fēng)吹閱讀 430評(píng)論 2 2
  • 最近一直在地鐵上看《曾國(guó)藩家書》,這本書第一次看是在大學(xué),在圖書館翻了翻就放下了,當(dāng)時(shí)看書急功近利,只想著看完,而...
    顏先生閱讀 2,091評(píng)論 0 3