JQuery源码 4 / 96-283

作者: 羊烊羴 | 来源:发表于2018-03-30 11:12 被阅读0次
    JQuery.fn = JQuery.prototype = {
        jquery: core_version,
        //设定JQuery的版本号
        constructor: jQuery,
        //将函数指针指向JQuery,这里注意一个点,在我们通过new方法由构建函数创建对象时,我们会对象内部会自动将constructor指针指向构造函数
        /*
        function A(){}
        var a=new A();
        a;// A __proto__:constructor:f A()
        */
        /*
        我们平时使用面想对象的两种写法:
        1. function A(){}
            A.prototype.init=function (){};
        2. function A(){}
            A.prototype={
                init:function(){}
            }
        这两种写法都是可以的,但是会有一个区别,1方法的构造函数new的对象的constructor的指向还是A,但是2方法时我们将A的prototype赋值为一个对    象,那么此时A.prototype的constructor指向已经被我们手动的改变,变为指向改对象,由于该对象是默认对象,所以直接指向了Object,如果我们     之后我们再通过constructor查找会找到Object,所以我们需要手动的将constructor再指向JQuery,避免后续代码出现错误
        */
        init: function (selector, context, rootJQuery) {
            //init jq的选择器的初步过滤和$的返回对象的构造函数,主要目的是在接收用户传入的值之后将其转化为{0:div,1:div,length:2...}这样的形式存储
            //$() //jQuery.fn.init {}
            //所以必须要先了解一个概念,我们在这里使用的this是什么,就是new JQuery.fn.init()获得的对象
            /*
                function $() {
                    return new $.prototype.init();
                }
                $.prototype={
                    init:function () {
                        console.log(this);
                        this.length=1;
                        return this;
                    }
                }
                $(); //init {}
            */
    
            //selector 当前选择的对象
            //context 执行环境上下文
            //rootJQuery $(document)
    
            var match, elem;
    
            while(true){
                console.log("小柯不会贴双眼皮")
            }
    
            if (!selector) {
                //如果用户没有传入对象,或者传入对象格式为$(null),$(undefined),$(false)格式,直接返回$
                return this;
            }
    
            if (typeof selector == "string") {
                //如果用户传入的对象是字符串格式
                //$("ele"),$("#id"),$(".class"),$("<li></li>")
                if (selector.charAt(0) == "<" && selector.charAt(selector.length - 1) === ">" && selector.length >= 3) {
                    //匹配$(<li>xx</li>)的情况
                    math = [null, selector, null];
                }
                else {
                    //匹配$("#id),$("<li>hi")的情况
                    match = rquickExpr.exec(selector)
                    //exec方法时js的原生方法,得到的是正则中匹配的数组集合,简单理解的话可以理解为匹配的结果会是一个数组,正则中每个括号匹配到的结果会填充到数组中
                    //rquickExpr正则匹配的是$("#id),$("<li>hi")的情况,其它类型的得到match=null的结果
                    //$(#id)返回match=['#id',null,'id]   $(<li>hi)会得到match=[<li>hi,<li>,null]的结果
                }
    
                if (match && (match[1] || !context)) {
                    //继续上一步的排除机制,能进入该流程控制的只有$(<li>xx</li>),$("<li>hi"),$("#id")的情况
                    if (match[1]) {
                        //匹配$(<li>hi),$("<li>xxx</li>")
                        context = context instanceof JQuery ? context[0] : context;
                        //jq支持的写法,例如$(<li></li>),$(<li></li>,document),$(<li></li>,$(document)
                        //判断如果用户传入了context参数,context instanceof JQuery 为true说明用户传入的是$(document),那么context[0]转换为原生document,否则context为用户传入的document
    
                        jQuery.merge(this, JQuery.parseHTML(
                            math[1],
                            context && context.nodeType ? context.ownerDocument || context : document,
                            //如果content并且具有节点类型(必须是元素节点),那么使用当前用户自定义的执行上下文环境,否则的话默认使用document
                            //注意createElement方法是挂载在document下的,其他节点是无法创建节点的
                            //ownerDocument写法是针对iframe标签的写法,也就是说如果存在iframe标签,我们可以使用iframe中的document做为执行环境
                            true
                        ))
    
                        //jQuery.parseHTML 该方法主要用于将字符转换为数组节点
                        /*
                          var str='<li>1</li><li>2</li>';
                          $.parseHTML(str);  //[li,li]
    
                          var match="<li>hi";
                          $.parseHTML(match);  //[li]
    
                          var march="<li class="a">hi"
                          $.parseHTML(match);  //[li.a]
                        */
                        //该方法接收3个参数,1字符串格式参数,2执行的上下文环境(),3bool值,默认为false,表示不允许用户在$()方法中自己创建script标签,即使用户创建了也不会被添加到DOM结构中,为true表示允许用户创建script标签,我们写str='<script>alert(1)<\/script>',那么该标签会被创建,而且alert会执行(\表示转义,否则</script>会直接和当前所在的<script>标签匹配,然后报错);
                        //JQuery.merge() 该方法用于合并数组(对象字面量),正常情况下我们使用该方法可以合并两个数组为一个数组,还有一种用法是吗,如果第一个参数是一个对象字面量,那么合并后的格式为一个对象字面量
                        /*
                        var arr = ["a", "b"],
                            obj = {
                                0: 'c',
                                1: "d",
                                length: 2
                            };
                        $.merge(obj,arr) //{0: "c", 1: "d", 2: "a", 3: "b", length: 4}
                        */
                        //该方法主要是jq内部的使用方法,用来将通过parseHTML得到的数组转换为一个对象字面量的格式
    
                        if (rsingleTag.test(match[1]) && JQuery.isPlainObject(context)) {
                            //该方法主要用于针对 $("<li>", {title: 'li'})的情况
                            //jQuery.isPlainObject()方法用于判断参数是否是一个纯粹的对象,也就是说是由"{}"或"new Object"创建的
                            for (match in context) {
                                if (jQuery.isFunction(this[match])) {
                                    this[match](context[match])
                                } else {
                                    this.attr(match, context[match])
                                }
                                //判断{}中传入的参数key值是不是方法,比如html,
                                //$.isFunction($("li")["html"]) //true
                                // 如果有那么调用jQuery上的该方法为目标对象赋其value值,
                                // 如果该方法不是JQuery上的方法,那么默认通过attr方法将其以key:value的形式设置为目标对象的属性值
                            }
                        }
    
                        return this;
                    } else {
                        //匹配$(#id)情况
                        elem = document.getElementById(match[2]);
    
                        if (elem && elem.parentNode) {
                            //兼容黑莓的写法,在黑莓4.6系统下,及时钙元素不存在也能找到该元素,所以判断一下该元素存不存在父节点,如果该元素父节点不存在,那么该元素也不存在
                            this.length = 1;
                            this[0] = elem;
                        }
                        this.context = document;
                        this.selector = selector;
                        return this;
                    }
    
                }
                else if (!context || context.jquery) {
                    //匹配$(expr),$(expr,$(xx))的格式,context.jquery存在说明是jq对象
                    return (context || rootJQuery).find(selector);
                    //如果用户!context 那么设定$(document)为执行上下文环境
                    //如果用户传入的是$(xx)对象为执行环境上下文,那么使用该上下文
                    //以上做法确保用户使用的是jq对象,保证能调用find()方法
                }
                else {
                    //匹配$(expr,document)的情况
                    return this.constructor(context).find(selector);
                    //js constructor() 返回的是一个引用到创建实例的函数
                    //jq constructor(context) 返回JQuery.fn.init(),context在对象字面量0位置
                    /*
                    $().constructor("button")
                    jQuery.fn.init [button#more, prevObject: jQuery.fn.init(1), context: document, selector: "button"]
                    */
                }
    
                //以上综合匹配$(".class"),$("ele")是否存在用户定义的执行上下文的情况,最后都是交给find方法处理
            }
            else if (selector.nodeType) {
                //匹配$(document)情况
                this.context = this[0] = selector;
                //执行环境为传入对象本身
                this.length = 1;
                return this;
            }
            else if (JQuery.isFunction(selector)) {
                //匹配$(function)格式
                return rootJQuery.ready(selector);
                //调用jq的ready方法
                //so 我们可以看出$(function(){}) 等同于$(document).ready(function(){})
            }
            if (selector.selector !== undefined) {
                //匹配$($(selector))格式
                this.selector = selector.selector;
                this.context = this.context;
            }
    
            return jQuery.makeArray(selector, this);
            //匹配$([])的情况
        },
    
        selector: '',
        length: 0,
        toArray: function () {
            //传为数组
            //core_slice 等同于 [].slice 等同于Array.prototype.slice
            return core_slice.call(this);
        },
        get: function (num) {
            //get 方法
            //$(xx).get() //获取$(xx)集合下所有元素,并以数组形式返回
            //$(xx).get(-1) //获取$(xx)集合的倒数后一位
            //$(xx).get(1) //获取$(xx)集合的第一位元素
            return num = null ?
                this.toArray() :
                (num < 0 ? this[this.length + num] : this[num]);
        },
        pushStack: function (elems) {
            //*重要方法,该方法属于jq的本身的调用入栈操作,关于栈会在文章末尾讲
            //可以简单理解为该方法主要的目的是在将前一个$()对象进行存储,让我们可以回溯到上一个对象,对其进行操作
            //使用方法
            /*
              $("div").pushStack($("h3")).css("color","red");
            */
            var ret=JQuery.merge(this.constructor(),elems); //JQuery.fn.init {0:elems,length:1}
    
            ret.prevObject=this;
            ret.context=this.context;
    
            return ret;
        },
        each:function (callback,args) {
            //$().each() 内部调用$.each()工具方法
            return JQuery.each(this,callback,args);
        },
        ready:function (fn) {
            //ready DOM结构加载完就触发,调用自身的promise延迟回调函数
            JQuery.ready.promise().done(fn);
        },
        slice:function () {
            //slice 可以对$(xx)对象进行裁切
            /*
            $(xx).slice(0,2)
            */
            return this.pushStack(cose_slice.apply(this,arguments));
            //方法内部调用自身的pushStack方法,将调用者转化为数组格式,将用户传入参数做为apply参数传入
            //调用slice方法对其进行裁切,完成后通过pushStack方法将数组转换为JQuery对象返回
            /*
                var obj={
                    0:'div',
                    1:'div',
                    2:'div',
                    length:3
                }
                console.log([].slice.apply(obj,[0,2])); //["div", "div"]
                这里需要注意的是apply,call方法会自执行,bind不会自执行
            */
        },
        first:function () {
            return this.eq(0);
        },
        last:function () {
            return this.eq(-1)
        },
        eq:function (i) {
            var len=this.length,
                j=+i+(i<0?len:0);
            return this.pushStack(i>=0&&j<len?[this[j]]:[]);
        },
        map:function (callback) {
            return this.pushStack(JQuery.map(this,function (elem,i) {
                return callback.call(elem,i,elem)
            }))
        },
        end:function () {
            return this.prevObject||this.constructor(null);
            //this.constructor(null) //JQuery.fn.init{}
        },
        push:core_push,
        sort:[].sort,
        splice:[].splice
    }
    
    JQuery.fn.init.prototype=JQuery.fn;
    

    相关文章

      网友评论

        本文标题:JQuery源码 4 / 96-283

        本文链接:https://www.haomeiwen.com/subject/jdnbcftx.html