zoukankan      html  css  js  c++  java
  • 浅谈jQuery构造函数

    $()函数到底做的什么

      jQuery在前端领域路人皆知,对于一向喜欢玩js的博主来说,虽然能力有限,但是还是很喜欢研究他的做为。那么一个简单的美元符号$与一对常见的()括号,jQuery底层到底做了哪些工作,如果你是前端新人,并喜欢刨根问底,你可以看一下下面的介绍。如果你是有经验的牛人,你可以指出错误,毕竟博主还是个半瓶子醋,没法完全理解。

    一、函数调用

    $(selector, context) : $()这一个简单的代码,其实调用了jQuery的构造函数,其中的selector和context是传递给jQuery构造函数的参数表达式,前者代表着选择器字符串,后者是上下文,jQuery会根据这两个参数,在页面中找到相应的dom对象,并包装成jQuery对象返回给我们。

    var jQuery = function(selector, context) {
        return new jQuery.fn.init(selector, context, rootjQuery);
    }
    
    
    jQuery.fn = jQuery.prototype = {
       init: function (selector, context, rootjQuery) {
          // ...
       }
    }
    
    jQuery.fn.init.prototype = jQuery.fn;

    调用jQuery()后,返回了一个new jQuery.fn.init(),其实就是jQuery实例,因为jQuery.fn.init 的原型指向了jQuery.fn,也就是jQuery的原型,这部分不需要解释了,是原型链的问题了。

    二、步入函数

    就这样jQuery进入到了init函数中,接下来看下面代码

    // 如果没有参数,返回当前jQuery对像
    if (!selector) {
      return this;
    }
    //如果selector有nodeType属性,则是dom对象,返回包装后的jQuery对象
    if (selector.nodeType) {
      this.context = this[0] = selector;
      this.length = 1;
      return this;
    }
    //如果selector是body,由于body只有一个,并且我们可以明确的指出,所以直接把body元素包装后返回jQuery对象
    if (selector === "body" && !context && document.body) {
      this.context = document;
      this[0] = document.body;
      this.selector = selector;
      this.length = 1;
      return this;
    }

    由此我们可以知道 $()反回了个空的jQuery对象, $(document.body) 走了第二个if返回了body对象的jQuery对象, $('body')走了第三个if也返回了body对象的jQuery对象。

    接下来,jQuery构造函数进入的最难的部分,请看着源码来读下面的介绍。

    一、如果selector是字符串类型,有两种情况:

    1. 如果是dom元素字符串,如$('<div>')  或者 $('<div>', {title: 'good'})等情况,走一种处理方法

    2. 如果是单独的div选择器,如$('#box') ,走一种处理方法

    3.1. 如果不是上面两种情况,如果context参数存在并且是jQuery对象,则用这个jQuery对象查找selector,如 $('span', $('body')) 相当于 $('body').find('span')

    3.2. 如果不是1、2两种情况,并没有context参数,就用根jQuery对象(document的jQuery对象)查找selector,如: $('span') 相当于 $(document).find('span')

    4. 其他情况,就是context是dom对象时,用context包装成jQuery对象查找selector, 如$('span', document.body) => $(document.body).find('span')

    二、如果selector是函数,那么进入ready方法,等待dom加载完毕执行函数,也就是$(function () {...})

    三、如果以上都不是,并且selector有selector和context属性,这代表什么呢?当然是传进来的jQuery对象了,如果是这样,那jQuery返回一个类似的新对象。。。

    三、难区详解(selector是字符串类型的1、2两种情况)

      由容易到难,现说是id选择器的情况,再详细说是dom元素字符串的情况。。。

    1. selector是个id选择器

    elem = document.getElementById(match[2]);

    //
    黑莓手机兼容解决 if (elem && elem.parentNode) { // ie 欧朋 选择name解决 if (elem.id !== match[2]) { return rootjQuery.find(selector); } this.length = 1; this[0] = elem; } this.context = document; this.selector = selector; return this;

    如果是id选择器,就直接选出元素包装成jQuery对象返回。

    2. selector是dom元素字符串


    这里又分为3种情况:

    1. 如果selector是一个简单的dom字符串,如: $('<div>'), $('<div></div>')

      1.1 如果第二个参数context是个纯js对象,也就是键值对,保存着属性和属性值,如$('<div>', {title: 'good'}) ,那么创建dom对象并赋值这些属性

      1.2 如果第二个参数存在并且是dom对象,那么就用这个dom对象创建selector这个dom, 如果context不是dom对象,就用document创建selector这个dom

    2. 如果selector并不是一个简单的字符串,其中有属性赋值在里面,如 $('<div id="box">'),进入 jQuery.buildFragment 方法构造dom对象结构

    四、dom对象的构造中转站

    这里不贴代码了,同学们可以看着源码走下去。

    jQuery.buildFragment中,jquery先取到要转化成dom的字符串,和要插入到真是dom的dom对象,也就是context参数或者是document对象。

    然后根据各种条件判断,这个dom对象是否是可缓存的,如果是可缓存,查找缓存区是否已经有了,如有有了,直接返回,如果没有,那么进入jQuery.clean方法创建并缓存。

    五、dom对象的构造工坊

    jQuery.clean 函数中, jQuery还是先确定下要创建的dom字符串数组,和在哪个dom下创建。

    进入创建循环,看到这样一句话

    if (!rhtml.test(elem)) {
        elem = context.createTextNode(elem);
    }

    博主看rhtml正则,觉得这个是判断是不是实体字符和html标签,如果不是实体字符或html标签,创建文本节点。

    往下看,如果不是实体字符,jQuery把这个字符串转化为开闭式的合格dom字符串并且取到了dom的名称

    elem = elem.replace(rxhtmlTag, "<$1></$2>");
    
    var tag = (rtagName.exec(elem) || ["", ""])[1].toLowerCase()

    由于tr td option等标签,是依赖于上级的标签,所以jQuery在这里需要考虑如果要创建他们,需要先创建他们上级,所以下面代码有个

    wrap = wrapMap[tag] || wrapMap._default

    那我们去查看wrapMap这个对象是什么

    wrapMap = {
      option: [1, "<select multiple='multiple'>", "</select>"],
      legend: [1, "<fieldset>", "</fieldset>"],
      thead: [1, "<table>", "</table>"],
      tr: [2, "<table><tbody>", "</tbody></table>"],
      td: [3, "<table><tbody><tr>", "</tr></tbody></table>"],
      col: [2, "<table><tbody></tbody><colgroup>", "</colgroup></table>"],
      area: [1, "<map>", "</map>"],
      _default: [0, "", ""]
    }

    我们来到了这里,看到了这个对象。我们可以看到,这个对象包含了那些需要上级dom才能存活的dom列表,他们的值是一个数组,数组第一个表示的层级关系,比如option是select下的第一级, tr是table下的第二级,数组第二个和第三个元素很明显分别表示了开口标签闭口标签,那么有什么用呢?继续往下看

    div.innerHTML = wrap[1] + elem + wrap[2];

    看到下面几行,终于茅塞顿开,原来是这样创建的依赖上级元素的 dom元素,前后上级标签中间夹着需要创建dom的字符串,把整个字符串赋值给一个div,构成了完整的dom。知道了后,往回走。

    safeChildNodes = safeFragment.childNodes

    上面还有一行这个是什么意思呢?我们去看看safeFragment把

    //    h5标签
    nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
        "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
    
    safeFragment = createSafeFragment(document);
    
    function createSafeFragment(document) {
      var list = nodeNames.split("|"),
      safeFrag = document.createDocumentFragment();
    
      if (safeFrag.createElement) {
        while (list.length) {
          safeFrag.createElement(
            list.pop()
          );
        }
      }
      return safeFrag;
    }

    我们找到了这三个重要的表达式,哦,原来这个safeFragment保存着所有h5的标签

    if (context === document) {
      safeFragment.appendChild(div);
    } else {
      createSafeFragment(context).appendChild(div);
    }

    接下来jQuery保存了之前我们创建的div,还不知道要干什么,继续往下看吧。

    while (depth--) {
        div = div.lastChild;
    }

    接下来,jQuery根据层级关系,一层一层的把外部容器去掉,只留下最后一层包裹着我们想要的那个dom。

    下面,就是对ie的两个兼容,ie下自动生成tobody,jQuery做了兼容,ie下破坏首行缩进,jQuery做了兼容。

    elem = div.childNodes;
    if
    (div) { div.parentNode.removeChild(div); if (safeChildNodes.length > 0) { remove = safeChildNodes[safeChildNodes.length - 1]; if (remove && remove.parentNode) { remove.parentNode.removeChild(remove); } } } }

    保存我们需要的dom节点后,把此次循环的div删掉。把插入到safeFragment的div也删掉,清空干净,马上要进入下一循环

    var len;
    if (!jQuery.support.appendChecked) {
      if (elem[0] && typeof(len = elem.length) === "number") {
        for (j = 0; j < len; j++) {
          findInputs(elem[j]);
        }
      } else {
        findInputs(elem);
      }
    }

    上面这一个if针对的 checkbox和radio元素在ie下checked失效的bug

    if (elem.nodeType) {
      ret.push(elem);
    } else {
      ret = jQuery.merge(ret, elem);
    }

    终于,保存了此次dom元素到ret数组中。然后下面的一个if是对script标签的插入处理,博主没有去看,因为不常用啊。。

    终点

      终于,jQuery方法走完了,走的好慢好艰辛,走过了这次行程,下一次是不是就很好走了O(∩_∩)O~。

  • 相关阅读:
    django表单字段
    python3之Django表单(一)
    python3之Django模型(一)
    python3迭代器和生成器
    python3数字、日期和时间
    python3字符串与文本处理
    python3数据结构与算法
    git仓库使用
    django邮件
    python3光学字符识别模块tesserocr与pytesseract
  • 原文地址:https://www.cnblogs.com/xujiazheng/p/6505246.html
Copyright © 2011-2022 走看看