jQuery的原型上的核心方法和属性,DOM操作相关方法的个人理解

//jQuery本质是用一个闭包
//jQuery用闭包的原因是因为在用多个框架式 可能会有函数 方法名字一样的情况 从而产生冲突
//为了不冲突 把所有的变量 关键字 方法等都声明为局部变量 但是让局部变量在全局访问 就可以用闭包(闭包 把整个代码运行环境都传给外边)
//比如把jQuery设置为 window.jQuery 设置为全局变量 用window.xxx = xxx
//jQuery给自己传入一个window的实参是为了可以让内部的形参可以简化 比如 形参为w  实参window 这样就可以减少文件大小 方便压缩代码
//自身直接传入window 可以提高代码效率 如果不穿 在代码需要用的时候 代码需要自己去一层一层的出去找 这样效率低

//接收undefined 是为了不被外面的可能被篡改过得undefined影响 在一些低版本的浏览器中undefined的值是可以被篡改的
//在这直接接收会直接得到undefined 因为在下面的传入中只传入了一个实参 第二个固定式undefined  
(function(window,undefind){
    
    
    var njQuery = function(selector){
    
    
        return new njQuery.prototype.init(selector);
    }
    njQuery.prototype = {
    
    
        constructor:njQuery,
        init:function(selector){
    
    
            /*jQuery的入口函数传入参数的几条规律
                什么都不传 和传入‘’ null undefined NaN 0 false 
                    会返回一个空的jQuery对象
                传入的是字符串
                    如果为代码片段会将所有找到的元素存储到jQuery对象中返回
                    如果为先创建对用代码片段中的元素  在把元素依次存储到jQuery对象中返回
                传入的是数组或伪数组
                    都会将数组的每个元素依次存储到jQuery对象当中返回
                传入的是上述意外的类型
                    如果为对象或dom元素会将传入的对象或dom元素存储到Query对象中返回
                    如果为基本数据类型会将传入的基本数据类型存储到Query对象中返回
            */

            //进行判断前先进 去除开头 或者结尾的空格
            selector = njQuery.trim(selector);// njQuery.trim在下面定义的

            // 什么都不传 和传入‘’ null undefined NaN 0 false 
            if(!selector){
    
    // !意为“非”  !'' !null !undefined !NaN !0 !false  的结果都为true
                return this;
            }
            //方法处理
            else if(njQuery.isFunction(selector)){
    
    
                njQuery.ready(selector);
            }
            //字符串
            else if(njQuery.isString(selector)){
    
    
                //判断是代码片段还是选择器  
                if(njQuery.isHTML(selector)){
    
    
                    //根据代码片段创建元素 
                    var temp = document.createElement('div');
                    temp.innerHTML = selector;//innerHTML就有创建代码片段中的元素   所有利用原生创建一个元素 在用该元素的innerHTML创建代码片段的元素
                    
                    //吧代码片段传入jQuery对象 并且 对于有多层嵌套的代码片段 只传入第一级的
                    //原生的children属性 会获得该元素的一级的子元素 这就直接满足了要求
                    // for(var i = 0; i < temp.children.length; i++){
    
    
                    //     this[i] = temp.children[i];
                    // }
                    // //最后给jQuery添加length属性
                    // this.length = temp.children.length;//直接赋值 没有length属性 会自动创建

                    [].push.apply(this,temp.children);//数组转换为伪数组
                    //通过[]数组 找到数组中的push方法 
                    //apply会强行改变 this的指向   apply的第二个参数是个数组 将该数组的元素依次取出作为调用apply的参数 
                    //children属性会返回一级子级  
                    //所以 temp.children返回的是子级数组 因为apply吧这个数组作为push的参数依次传入 并把this指向切换 原本指向 [] 现在指向njQuery.prototype  因为在HTML中是用$()来调用的 $就是njQuery

                    //返回这个jQuery对象
                    // return this;
                }
                else{
    
    
                    //字符串为选择器
                    //根据传入的选择器找到对应的元素
                    var res = document.querySelectorAll(selector);
                    //将找到的元素添加到njQuery上
                    // for(var i = 0; i < res.length; i++){
    
    
                    //     this[i] = res[i];
                    // }
                    // //最后给jQuery添加length属性
                    // this.length = res.length;//直接赋值 没有length属性 会自动创建
                    [].push.apply(this,res);
                    //返回加工好的this
                    // return this;
                }
            }
            //数组或伪数组
            //根据selectors的类型是不是object和是否含有length判断是不是数组或伪数组  但是window对象比较特殊 他不是伪数组但也满足条件 所以要排除一下
            else if(njQuery.isArray(selector)){
    
    
                //判断是数组 还是 伪数组  将他们两个toString 数组会将所以元素拼接成一个字符串 伪数组不同
                //({}).toString.apply(arr) 通过向apply的括号传入数组 最后打印出来的结果是[object Array]     伪数组.toString 的结果是[object Object]
                // if(({}).toString.apply(selector) === '[object Array]'){//数组
                //     [].push.apply(this,selector);//数组转换为jQuery对象   就是  数组转换为伪数组
                //     return this;
                // }
                // else{//将伪数组转回为jQuery对象
                //     var arr = [].slice.call(selector);//伪数组转换为数组   这样搞是为了兼容捞比IE
                //     [].push.apply(this,arr);//将伪数组转回为jQuery对象  
                //     return this;
                // }

                //不关他是数组还是伪数组 我都先转为数组
                var arr = [].slice.call(selector);
                [].push.apply(this,arr);
                // return this;
            }
            //上述意外的类型
            else{
    
    //就直接将传入的参数存到jQuery对象中  并且设置length属性 
                this[0] = selector;
                this.length = 1;
                // return this;
            }
            return this;
        },
        njQuery:'1.1.0',//版本号
        selector:'',//默认值
        length:0,//默认值
        //[].push就是找到push方法  
        //puhs:[].push就是相当于改this 等价于 [].push.apply(this);
        puhs:[].push,
        sort:[].sort,
        splice:[].splice,
        toArray:function(){
    
    //把实例转换为数组返回
            return [].slice.call(this);//伪数组转数组
        },
        get:function(num){
    
    //返回的是原生DOM元素
            // 为整数时 表示索引    为负数时 就是总长度加上这个参数   不传参数相当于调用toArray()
            //不传参时
            if(arguments.length === 0){
    
    
                return this.toArray();
            }
            //参数为正数
            else if(num >= 0){
    
    
                return this[num];
            }
            //参数为负数
            else{
    
    
                return this[this.length + num];
            }
        },
        eq:function(num){
    
    //逻辑跟get类似  但eq返回的是njQuery对象 不是原生的
            if(arguments.length === 0){
    
    
                return new njQuery;//没有就是返回空的njQuery对象
            }
            else{
    
    //不需要判断 直接交给get判断
                return njQuery(this.get(num));//返回njQuery包装过的
            }
        },
        first:function(){
    
    //返回jQuery对象 保存第一个元素
            return this.eq(0);
        },
        last:function(){
    
    
            return this.eq(-1);//返回jQuery对象 保存最后一个元素
        },
        each:function(fn){
    
    
            return njQuery.each(this,fn);
        },
        map:function(){
    
    

        }
    }

    //njquery.extend的内容直接赋给njQuery 
    njQuery.extend = njQuery.prototype.extend = function(obj){
    
    
        for (var key in obj) {
    
    //通过遍历传入的参数obj 
            this[key] = obj[key];
            //njquery[key] = obj[key];  key就是obj中的各个属性和方法
            //njquery["isString"] = obj["isString"];  例如
            //njquery["isString"] = function(str){eturn typeof str === 'string';;}; 例如
        }
    }//通过上述的挨个遍历赋值把njquery.extend的内容直接赋给njQuery 
    
    //工具方法
    njQuery.extend({
    
    
        //判断是不是字符串
        isString : function(str){
    
    
            return typeof str === 'string';
        },
        //判断字符串是不是代码片段
        isHTML : function(str){
    
    
            //代码片段的开头一定会< 结尾一定是 >  而且代码片段最小最短是 3
            return str.charAt(0)==='<' && str.charAt(str.length-1)==='>' && str.length >= 3;
        },
        //去除字符串开头跟结尾的无用空格
        trim : function(str){
    
    //trim是用于去除字符串开头和结尾的空格用的  但是垃圾IE 低版本不支持
            if(!njQuery.isString(str))return str;//对于不是字符串的直接返回
            else if(str.trim){
    
    
                return str.trim();
            }
            else{
    
    
                //replace属性用于替换字符 第一个参数为被替换的字符 第一个参数可以是正则表达式
                return str.replace(/^\s+|\s+$/g,'');//正则表达式 中 \s 为匹配空格  ^为匹配范围为字符串开头   $为匹配范围为字符串结尾      g为全局匹配 多次匹配 知道不匹配为止     |为或
            }
        },
        //判断传入的是不是对象
        isObject : function(sele){
    
    
            return typeof sele === 'object';
        },
        //判断传入的是不是window
        isWidow : function(sele){
    
    
            return sele === window;
        },
        //判断传入的是不是数组
        isArray : function(sele){
    
    
            if(njQuery.isObject(sele) && !njQuery.isWidow(sele) && 'length' in sele){
    
    
                return true;
            }
            else{
    
    
                return false;
            }

        },
        //判断传入的是不是函数
        isFunction : function(sele){
    
     
            return typeof sele === 'function';//如果是函数他的typeof结果就是 'function'
        },
        //判断DOM是否加载完毕
        ready:function(fn){
    
    
            
            if(document.readyState === 'complete'){
    
    
                fn();
            }
            else if(document.addEventListener){
    
    
                document.addEventListener('DOMContentLoaded',function(){
    
    
                    fn();
                });
            }
            else{
    
    
                document.attachEvent('onreadystatechange',function(){
    
    
                    if(document.readyState == 'complete'){
    
    //只有在 complete - 载入完成打印
                        fn();
                    }
                })
            }
        },
        //遍历方法
        each:function(obj,fn){
    
     //默认返回传入对象
            //判断是不是数组
            if(njQuery.isArray(obj)){
    
    
                for(var i = 0; i < obj.length; i++) {
    
    
                    // var res = fn(i,obj[i])//遍历给外部的函数返回 索引和值
                    //遍历给外部的函数返回 索引和值
                    //jQuery中each中的this会被改向 value 就是每次遍历的值
                    var res = fn.call(obj[i],i,obj[i])
                     // 让return false相当于break   让return true 相当于continue
                    if(res == true){
    
    
                        continue
                    }
                    else if(res == false){
    
    
                        break;
                    }
                }
            }
            //判断是不是对象
            else if(njQuery.isObject(obj)){
    
    
                for(var key in obj){
    
    
                    // var res = fn(key,obj[key]);
                    var res = fn.call(obj[key],key,obj[key])
                    if(res == true){
    
    
                        continue
                    }
                    else if(res == false){
    
    
                        break;
                    }
                }
            }
            return obj;
        },
        map:function(obj,fn){
    
     //默认返回空数组
            var res = [];
            //判断是不是数组
            if(njQuery.isArray(obj)){
    
    
                for(var i = 0; i < obj.length; i++){
    
    
                    var temp = fn(obj[i],i);
                    if(temp){
    
    //只要有在接收到外部函数return的返回值时 添加
                        res.push(temp);
                    }
                }
            }
            //判断是不是对象
            else if(njQuery.isObject(obj)){
    
    
                for(var key in obj){
    
    
                    var temp = fn(obj[key],key);
                    if(temp){
    
    
                        res.push(temp);
                    }
                }
            }
            return res;
        },
        getStyle:function(dom,styleName){
    
    
            //判断是否支持getComputedStyle
            if(window.getComputedStyle){
    
    
                return window.getComputedStyle(dom)[styleName];
            }
            else{
    
    
                return dom.currentStyle[styleName];
            }
        }
    });
    //DOM操作相关方法
    njQuery.prototype.extend({
    
    
        empty:function(){
    
    //清空元素内容
            //1.遍历所有找到的元素
            this.each(function(key,value){
    
    
                value.innerHTML = '';
            });
            return this;//为了方便链式编程
        },
        remove:function(sele){
    
    //删除所有的元素或指定元素
            //判断是否有参数传入
            if(arguments.length === 0){
    
    //没有就删除所有的
                //遍历元素
                this.each(function(key,value){
    
    
                    //js中不能元素自己删除自己 只能通过父元素删除
                    var parent = value.parentNode;//parentNode获取指定元素的父元素
                    parent.removeChild(value);
                });
                return this;
            }
            else{
    
    //否则删除指定的
                var $this = this;
                //根据选择器找到指定的元素
                njQuery(sele).each(function(key,value){
    
    //找到需要删除的元素
                    var type = value.tagName;
                    $this.each(function(k,v){
    
    //跟原本的所有的元素对比 
                        var t = v.tagName;
                        if(t === type){
    
    //对上的删除
                            var parent = value.parentNode;//parentNode获取指定元素的父元素
                            parent.removeChild(value);
                        }
                    })
                });
            }
        },
        html:function(content){
    
    //设置修改元素内容 会根据字符串和代码片段改变
            if(arguments.length === 0){
    
    //没有实参
                return this[0].innerHTML;//直接返回指定元素的第一个内容
            }
            else{
    
    //否者将指定的所有元素的内容改为传入的
                //需要遍历
                this.each(function(key,value){
    
    
                    value.innerHTML = content;  //而且 innerHTMl自己就会区分是代码片段 还是普通的字符串
                })
            }
        },
        text:function(content){
    
    //设置修改元素内容 不会根据字符串和代码片段改变
            if(arguments.length === 0){
    
    //没有实参
                var res = '';
                this.each(function(key,value){
    
    
                    res += value.innerText;//将所有元素的内容依次拼接 最后形成字符串
                })
                return res;//返回这个字符串
            }
            else{
    
    //否者将指定的所有元素的内容改为传入的
                //需要遍历
                this.each(function(key,value){
    
    
                    value.innerText = content;  //将元素的内容设置为传入的字符串 只是字符串
                })
            }
        },
        appendTo:function(sele){
    
    //作用:将一组或一个(为一个整体)需要转移的元素 转移到到一个或多个元素的内部    并且移动的位置是最后面   并且会把字符串当做选择器的处理   返回值是所有操作的元素
            var $target = $(sele);//将传入的数据 在经过一下jQuery核心函数 的处理 这样就不管是什么类型参数 结果就都是jQuery对象了
            var $this = this;
            var res = []
            //外城循环 控制的是转移的位置     内城循环 控制的是转移的元素
            $.each($target,function(key,value){
    
    //传入的是通过jQuery核心函数处理的 肯定是jQuery对象
                //经过遍历挨个取出里面的元素
                $this.each(function(k,v){
    
    //遍历需要转移的元素 因为需要朱阿姨的可能不止一个 
                    //获得每一个需要转移的元素
                    if(key === 0){
    
    //只有第一组需要转移的元素 是直接改   判断第一个转移到的位置  所有是用key判断   
                        value.appendChild(v);
                        res.push(v);
                    }
                    else{
    
    //其他的都为克隆第一个转移的元素
                        var temp = v.cloneNode(true);
                        value.appendChild(temp);
                        res.push(temp);
                    }
                })
            })
            //返回值  需要返回所有添加移动的元素 并且是以一个jQuery对象的数组
            return $(res);
        },
        prependTo:function(sele){
    
    //与appendTo类似 只是他是移动到元素的内部最前面
            var $target = $(sele);//将传入的数据 在经过一下jQuery核心函数 的处理 这样就不管是什么类型参数 结果就都是jQuery对象了
            var $this = this;
            var res = []
            //外城循环 控制的是转移的位置     内城循环 控制的是转移的元素
            $.each($target,function(key,value){
    
    //传入的是通过jQuery核心函数处理的 肯定是jQuery对象
                //经过遍历挨个取出里面的元素
                $this.each(function(k,v){
    
    //遍历需要转移的元素 因为需要朱阿姨的可能不止一个 
                    //获得每一个需要转移的元素
                    if(key === 0){
    
    //只有第一组需要转移的元素 是直接改   判断第一个转移到的位置  所有是用key判断   
                        value.insertBefore(v,value.firstChild);
                        res.push(v);
                    }
                    else{
    
    //其他的都为克隆第一个转移的元素
                        var temp = v.cloneNode(true);
                        value.insertBefore(temp,value.firstChild);
                        res.push(temp);
                    }
                })
            })
            //返回值  需要返回操作的元素
            return $(res);
        },
        //append和appendTo区别是 
        //他们的参数和调用者的顺序不一样 从字面上就看得出了一个是to一个不是  
        //他们两个对于参数为字符串类型的处理不同 appendTo会当做选择器处理 append不会
        //append的返回值是直接返回调用者  appendTo的返回值返回所有添加移动的元素 并且是以一个jQuery对象的数组
        append:function(sele){
    
    //跟appendTo类似 但是还是有区别的,append不会把参数当做选择器处理  append的返回值是直接返回调用者  
            //先判断是不是字符串
            if(njQuery.isString(sele)){
    
    //字符串会区分代码片段跟普通字符串
                //用+=就可以直接将要添加的内容直接添加到当前调用者的内部的最后
                this[0].innerHTML += sele;//加[0]是因为 this指向的是jQuery对象 但是innerHTML是原生元素的属性 所有要获得以下原生的 jQuery对象里面的就是原生的
            }
            else{
    
    //其他的就跟appendTo一样了 只需要调整下顺
                //append理解为 向xxx添加yyy      
                //appendTo理解为  吧yyy添加到xxx中
                $(sele).appendTo(this);
            }
            return this;
        },
        prepend:function(sele){
    
    //跟prependTo类似 prepend与prependTo的不同跟append与appendTo的不同类似 同理
            if(njQuery.isString(sele)){
    
    //字符串会区分代码片段跟普通字符串
                // this[0].innerHTML代表调用者原来的内容  把 新的内容加调用者原来的内容 再付给他 就相当于添加在最前面了
                this[0].innerHTML = sele + this[0].innerHTML;//加[0]是因为 this指向的是jQuery对象 但是innerHTML是原生元素的属性 所有要获得以下原生的 jQuery对象里面的就是原生的
            }
            else{
    
    
                $(sele).prependTo(this);
            }
            return this;
        },
        insertBefore:function(sele){
    
    //prependTo类似  区别是他是插入在指定元素的外边的前面  
            //调用者.insertBefore(插入的元素,参考位置的元素);  (原生的)
            //insertBefore方法 是调用者是谁就会将元素添加到那个元素里面  (原生的)
            //想要实现jQuery的insertBefore就是添加在指定元素外面的前面 
            //可以利用父元素 获得父元素 让父元素使用 原生的 insertBefore就实现了
            var $target = $(sele);
            var $this = this;
            var res = []
            //外城循环 控制的是转移的位置     内城循环 控制的是转移的元素  
            $.each($target,function(key,value){
    
    
                var parent = value.parentNode;//获得父元素
                $this.each(function(k,v){
    
    
                    if(key === 0){
    
       
                        parent.insertBefore(v,value);//这里的坐标元素 就直接用指定元素 这样就实现了 只是在指定元素前面 而不会因为兄弟元素出错
                        res.push(v);
                    }
                    else{
    
    
                        var temp = v.cloneNode(true);
                        parent.insertBefore(temp,value);
                        res.push(temp);
                    }
                })
            })
            return $(res);
        },
        replaceAll:function(sele){
    
    
            var $target = $(sele);
            var $this = this;
            var res = []
            //外城循环 控制的是转移的位置     内城循环 控制的是转移的元素  
            $.each($target,function(key,value){
    
    
                var parent = value.parentNode;//获得父元素
                $this.each(function(k,v){
    
    
                    if(key === 0){
    
       
                        $(v).insertBefore(value);//通过jQuery的insertBefore实现插入到指定元素的外部的前面
                        $(value).remove();//删除指定元素
                        res.push(v);
                    }
                    else{
    
    
                        var temp = v.cloneNode(true);
                        $(temp).insertBefore(value);//通过jQuery的insertBefore实现插入到指定元素的外部的前面
                        $(value).remove();//删除指定元素
                        res.push(temp);
                    }
                })
            })
            return $(res);
        }
    })
    //属性操作相关方法
    njQuery.prototype.extend({
    
    
        //attr()
        //传入一个参数 获得第一个该元素指定的属性节点的值
        //传入两个参数 设置该元素所有匹配的指定的属性节点的值  第一个参数属性节点名 第二个值
        //还可以传入一个对象 这个对象就是 他里面的每个属性名代表代表属性节点名   属性值就是属性节点值
        //返回值是方法的调用者
        attr:function(attr,value){
    
    
            //先判断是字符串还是对象
            if(njQuery.isString(attr)){
    
    
                //判断几个参数
                if(arguments.length === 1){
    
    
                    return this[0].getAttribute(attr);//getAttribute通过名称获取属性的值
                }
                else{
    
    //两个参数 循环更改所有匹配的指定的属性节点的值
                    this.each(function(key,ele){
    
    
                        ele.setAttribute(attr,value);//setAttribute通过名称更改所有匹配的指定的属性节点的值
                    })
                }
            }
            else if(njQuery.isObject(attr)){
    
    
                var $this = this;
                //遍历取出对象中的属性和他的值
                $.each(attr,function(key,value){
    
    //这个循环是循环传入的对象的 但是传入的对象可能并不是jQuery对象 可能没有each方法  所有用的$
                    $this.each(function(k,ele){
    
    
                        ele.setAttribute(key,value);//传入的参数都得用 传入对象中的内容
                    })
                })
                
            }
            return this;//返回值是方法的调用者
        },
        //prop()跟attr类似 但是他是操作属性的 其他一样
        prop:function(attr,value){
    
    
            //先判断是字符串还是对象
            if(njQuery.isString(attr)){
    
    
                //判断几个参数
                if(arguments.length === 1){
    
    
                    return this[0][attr];//用中括号就可以直接获得属性
                }
                else{
    
    //两个参数 循环更改所有匹配的指定的属性节点的值
                    this.each(function(key,ele){
    
    
                        ele[attr] = value;//设置值就跟普通的数组,伪数组赋值差不多
                    })
                }
            }
            else if(njQuery.isObject(attr)){
    
    
                var $this = this;
                //遍历取出对象中的属性和他的值
                $.each(attr,function(key,value){
    
    //这个循环是循环传入的对象的 但是传入的对象可能并不是jQuery对象 可能没有each方法  所有用的$
                    $this.each(function(k,ele){
    
    
                        ele[key] = value;//传入的参数都得用 传入对象中的内容
                    })
                })
                
            }
            return this;//返回值是方法的调用者
        },
        //css跟attr类似 但是他是css样式的 其他一样
        //原生js 通过window.getComputedStyle(通过dom获得元素)['需要获取的css样式名']; 后面的中括号也可以用.
        //但是IE8以下不支持getComputedStyle 需要用  通过dom获得元素.currentStyle['需要获取的css样式名']
        //原生js 通过 通过dom获得元素.style['需要设置的css样式名' = 样式值;
        css:function(attr,value){
    
    
             //先判断是字符串还是对象
             if(njQuery.isString(attr)){
    
    
                //判断几个参数
                if(arguments.length === 1){
    
    
                    return $.getStyle(this[0],attr);
                }
                else{
    
    //两个参数 循环更改所有匹配的指定的属性节点的值
                    this.each(function(key,ele){
    
    
                        ele.style[attr] = value;
                    })
                }
            }
            else if(njQuery.isObject(attr)){
    
    
                var $this = this;
                //遍历取出对象中的属性和他的值
                $.each(attr,function(key,value){
    
    //这个循环是循环传入的对象的 但是传入的对象可能并不是jQuery对象 可能没有each方法  所有用的$
                    $this.each(function(k,ele){
    
    
                        ele.style[key] = value;//传入的参数都得用 传入对象中的内容
                    })
                })
                
            }
            return this;//返回值是方法的调用者
        }
    })
    njQuery.prototype.init.prototype = njQuery.prototype;
    window.njQuery = window.$ = njQuery;
})(window);

猜你喜欢

转载自blog.csdn.net/weixin_44492275/article/details/107149884