一.3-jQuery.prototype对象中的属性与方法(上)

2021-03-05 09:29

阅读:387

标签:context   xpl   简单   索引   body   是什么   长度   eth   core   

源码96-280行

 

前言

上一篇说到了jQuery的extend方法,它是用来给jQuery函数扩展方法或者给jQuery的原型上加方法。

其实上,jQuery的原型上还是有一些属性和方法的。其中就有我们熟悉的init方法。

当我们调用 $("li") 时,new的便是jQuery.prototype.init构造函数

 

源码

首先源码比较长,我先写一个简版的

jQuery.fn = jQuery.prototype = {
    jquery: core_version, // 49行  core_version = "2.0.3"
    constructor: jQuery,
    init: function(){},
    selector: "",
    length: 0,
    toArray: function(){},
    get: function(){},
    pushStack: function(){},
    each: function(){},
    ready: function(){},
    slice: function(){},
    first: function(){},
    last: function(){},
    eq: function(){},
    map: function(){},
    end: function(){},
    push: core_push,   
    // 其实这个方法就是Array.prototype.push。这样可以方便压缩代码。见53行及47行
    sort: [].sort,
    splice: [].splice
}

可以看到还是有很多属性和方法的。有些实现比较简单,比如最后三个(push, sort,splice),有些很复杂,如init方法

 

1.jquery: core_version

获取jQuery的版本

console.log($().jquery);  // "2.0.3"

有用处吗?

有,有些库是需要特定版本的jQuery的(有可能版本特别低),他的检测方法基本上就通过这种方法的。嘿嘿,只要你改一下这个,可能这个库就能用了。(我试过)

为啥子不换jQuery呢? 因为麻烦。哈哈(也不麻烦,jQuery有防冲突的机制,可以实现一个页面多个版本的jQuery库)

 

 

2.constructor: jQuery

重写下constructor的指向

 

 

3. init: function(){}

这个方法很重要,这个方法要需要处理的情况比较多。下面举些例子

$(function(){});     // 当dom加载完成后就调用此方法(我们自己写的代码都在这里面)
$("li");                   // 选择所有的li标签
$("
"); // 创建元素 $("li", $(".class1")); // 选择所有的li标签,这个li标签必须在.class1标签下

 

可以看到init方法中有大量的if判断(毕竟要完成大量的判断呢), 我们先整理下吧。写个简版吧。

jQuery源码中也给每一种的情况写了注释,在jQuery源码算是少见的注释了。

情况一
// 容错处理 $(""), $(null), $(undefined), $(false)
if (!selector) {
    return this;
}
情况二
// 字符串的情况: 如选择器$("div"), $("div p") , 创建元素, $("
  • "),$("
    ")
  • if (typeof selector === "string"){ 情况三 // $(this), $(document) }else if(selector.nodeType){ 情况四 // $(function(){}) }else if(jQuery.isFunction(selector){ } if (){}

    情况一:

    这是容错处理,就算用户乱输入,也尽量不报错

     

    情况二: 

    情况二比较复杂,因为我们不但能选择元素,还能创建元素。

    选择器可能是复杂选择器(如"div p"),也可能是简单选择器(如"div").

    创建的元素可能是单个元素,也可能是多个元素。因此采取的策略都不太一样。

    // 111-117
    if
    (selector.charAt(0) === "" && selector.length >= 3){ // 创建元素 情况1
        match = [null, selector, null];
    }else{ // 选择器    情况2
        match = rquickExpr.exec(selector);
    }

    这个if判断还是很长的。首先这个字符串的第一个字符得是 , 其次这个选择器的长度得大于等于3.

    这么一看, 那便是创建 元素的情况。 $("

  • ")和$("
  • ")

     

    有人可能会问了,假如我传入了这种的 $("

  • 123")。jQuery会怎么处理?

    这种情况肯定是走else了,放心,在else中会针对这种情况来进行处理的。

     

     

    else语句中的rquickExpr是啥?

    那是一个正则表达式(75行)

    rquickExpr = /^(?:\s*()[^>]*|#([\w-]*))$/,

    源码也有详细的解释

    // A simple way to check for HTML strings
    // Prioritize #id over  to avoid XSS via location.hash (#9521)
    // Strict HTML recognition (#11290: must start with 

     

    我来稍微说一个这个正则表达式吧。(如果看明白了这个正则表达式,也就知道了match的结果了)

    技术图片技术图片
    rquickExpr = /^(?:\s*()[^>]*|#([\w-]*))$/
    
    ^必须是什么什么开始
    $ 必须以什么什么结束
    
    (?:\s*()[^>]*|#([\w-]*))
    这是为啥子要包裹一下括号,而且还有?:
    这是非捕获型分组,主要是提高性能用的。
    
    \s*()[^>]*|#([\w-]*)
    \s (space)是空格的意思。\r\n\f\v\\x20 等都可以被认为是空格
    
    *代表可以重复0次或多次。*可以联想到天上的星星。有时候没有,而有时候数都数的不过来
    
    注意到中间有一个管道符,这代表或的意思。
    情况一
    ()[^>]*
    
    情况二
    #([\w-]*))
    
    
    情况一
    ()[^>]*
    注意分组内容 
    这个正则很有意思 
    必须以 开头,中间的字符任意(包括空格), 个数大于等于1。
    [^>]* 代表除了^之外,啥字符都能匹配的上,匹配次数是0次或者多次
    
    
    情况二
    #([\w-]*) 
    这是匹配id选择器用的
    第一个字符得是 #
    [\w-]的字符范围比较广,0到9,a到z,A到Z以及字符-。
    View Code

     

    下面是情况1和情况2的总结

    情况1总结

    可能的情况: $("

  • ") 和 $("
  • ")

    match的结果: 

    1. $("
  • ") [null, ‘
  • ‘, null] 2. $("
  • ") [null, ‘
  • ‘, null]
  •  

    情况2总结

    可能的情况: $("#main") $("div p") $("

  • 123")

    match的结果: 

    rquickExpr = /^(?:\s*()[^>]*|#([\w-]*))$/
    
    1. $("#main") 
    ["#main", undefined, "main"]
    
    2. $("div p")  // 对,没错,选择器其实不在这里做处理
    null 
    
    3. $("
  • 123") [
  • 123‘, ‘
  • ‘, null]
  •  

    前面的代码只是做了预处理,分类了字符串的各种情况

    // 120-174

    if
    (match && (match[1] || !context)){
    // 创建标签或者id选择器的情况

    // id选择器的match的第二个元素为undefined,但是id选择器是没有context的。
    // context是啥? $(“li”, $("div")) $的第二个参数便是context,但我们如果使用id选择器的话,是不会传入第二个参数来缩小上下文的。
    // 因此id选择器也算一个

      if (match[1]){
      // 创建标签的情况,见下面
        

      }else{
      // id选择器的情况, 见下面
      
      }

    }
    else if (!context || context.jquery){ }else{ }

     

    创建标签的情况

    1. 124行

    // 124
    context = context instanceof jQuery ? context[0] : context;

    这个context便是我们在使用$ 时传入的第二个参数,用于缩小上下文。

    $("li", $("div")); //此时context便是jQuery的实例,但是这玩意不能直接用的,因此要将jQuery转为dom。
    // (因为jQuery实例是一种伪数组的形式,因此直接context[0])

     

    2. 127行-131行

    jQuery.merge(this, jQuery.parseHTML(
        match[1],
        context && context.nodeType ? context.ownerDocument || context : document,
        true
    ));

    这几行代码看起来比较复杂啊。

    里面涉及了两个jQuery工具方法(通过上一节说的extend进行扩展的)

     

    先说说这两个方法的使用吧

    merge

    var arr1 = [1,2];
    var arr2 = [3,4];
    $.merge(arr1, arr2);
    console.log(arr1);  //  [1,2,3,4]
    console.log(arr2);  //  [3,4]
    
    
    var obj = {
        0: 1,
        1: 2,
        length: 2
    }
    var arr3 = [3,4];
    $.merge(obj, arr3);
    console.log(obj);  // {0:1, 1:2, 2:3,3:4, length: 4}
    console.log(arr3); // [3,4]

    通过上面的代码可以看到merge工具方法接受两个参数(实现中也是两个形参)

    这个两个参数可以是数组,也可以是伪数组(为什子要支持伪数组,因为jQuery实例便是一个伪数组)

    第一个参数会融合第二个参数的元素。因此第一个参数会被修改,第二个参数就不会了。(可参考上面的例子)

     

    parseHTML

    这是一个处理html字符串的,如
  • ,
  • 返回一个数组,数组中是创建好的单个标签。

    $.parseHTML("
  • ") // [li] 注意数组中的元素是node节点!!!! $.parseHTML("
  • ") // [li, li] $.parseHTML("
    ") // [ul]

    $.parseHTML("
  • ") // [li, li]

    $.parseHTML("
  • ", document, true) // [li, li, script]
  • parseHTML的第一个参数便是要解析的html字符串, 第二个参数是context,默认是document(大多情况是document,除非你想在iframe中操作)

    第三个参数是一个布尔值。是否解析script标签,默认是不解析的。(为了安全考虑)

     

    再来看看这几行代码

    jQuery.merge(this, jQuery.parseHTML(
        match[1],
        context && context.nodeType ? context.ownerDocument || context : document,
        true
    ));

     

    context的确定

    context && context.nodeType ? context.ownerDocument || context : document

    提示下: ownerDocument是对于iframe的处理。iframe.ownerDocument 返回这个iframe的document。而document就没有这个属性。

    merge 会将 创建好的元素融合到this对象中, 举例子

    $("
  • ")
  • 这时this对象会长成这个样子

    {
        0: li      
    }

    第三个参数是true,这样script标签就能创建了

     

    3.135-146行

    这一部分是针对这样创建标签的

    $("

    ", {title: 111, html: 123}}).appendTo($(document.body));

    //

    123

     

    if (rsingleTag.test(match[1]) && jQuery.isPlainObject(context)) {
        for (match in context) {
            // Properties of context are called as methods if possible
            if (jQuery.isFunction(this[match])) {
                this[match](context[match]);
    
                // ...and otherwise set as attributes
            } else {
                this.attr(match, context[match]);
            }
        }
    }

    首先得满足这两个条件

    要创建的标签得是单标签, 第二个参数得是对象字面量( {} 这个便是对象字面量)

    说一下这个正则,涉及了反向引用

    rsingleTag = /^(?:|)$/
    
    \1 是反向引用,这里的字符得和第一个分组的结果一样,即这部分 (\w+)
    
    如果是这样的html字符 
  • , 那么就不满足要求
  • 然后for遍历这个对象,将属性和属性值放到这个创建好的this对象上

     

    这里还有一个判断

    if (jQuery.isFunction(this[match]))

    这主要是针对我们使用html,text,css属性的,一但使用这些属性,就会调用jQuery的上的对应的方法

    $("
  • ", {html: 123}); // 会调用 this.html(123) ,注意这里this中的内容
  • 如果不是jQuery上的方法,就直接添加了

     

    4. 148行

    return this;

    返回this, 里面的是已经创建好的dom对象

     

     

    id选择器的情况(152-164)

    id选择器就比较简单了,毕竟浏览器原生支持id选择器(虽然IE有bug)

    // 调用原生方法
    elem = document.getElementById(match[2]);
    
    // Check parentNode to catch when Blackberry 4.6 returns
    // nodes that are no longer in the document #6963
    // 通过这个索引(#6963)可以去jQuery官网查看更详细的说明
    // 这里也写了原因,是因为黑莓4.6删除了node后,这个node还会存在。因为需要判断下这个node的父节点在不在
    if (elem && elem.parentNode) {
        // Inject the element directly into the jQuery object
        this.length = 1;
        this[0] = elem;
    }
    this.context = document;
    this.selector = selector;
    return this;

    上面的一些属性(如selector, context)是不是比较熟悉呀

     

     

     

    168行

    else if (!context || context.jquery){
        return (context || rootjQuery).find(selector);
    }
    //
    rootjQuery = jQuery(document); 见867行
    
    

    针对这种情况

    $(‘li‘)               // context 不存在 $(document).find(‘li‘)
    
    $(‘li‘, $(‘div‘))     // $(‘div‘).find(‘li‘)

     

    174行

    对于复杂选择器的处理

    return this.constructor(context).find(selector); 
    // $(context).find(selector);

    这个方法要调用就是要Sizzle选择器了(源码很复杂,特别是1.8.3版本后引入了编译,2000行代码)

     

    好了,对于字符串处理的部分终于结束了,好长呀。

     

    情况三

    178行

    如果传入了node节点

    $(document.getElementById("main"))

    else if(selector.nodeType){
    this
    .context = this[0] = selector; this.length = 1; return this;
    }

     

    情况四

    186行

    如果传入了一个函数

    else if (jQuery.isFunction(selector)) {
         return rootjQuery.ready(selector);
    }

    这个需要以后再说,这个形式是我们经常用的。

     

    189-192行

    如果写成这样子 $($("div")), 其实下面的代码就起作用了。

    if (selector.selector !== undefined) {
           this.selector = selector.selector;
           this.context = selector.context;
    }

    $("div")的selector肯定不是undefined,因此需要重新赋值下。因此 $($("div"))的效果与$("div")相同

     

    194行

     return jQuery.makeArray(selector, this)

    这个makeArray工具方法如果只有一个参数的话,将会伪数组转为数组。

    两个参数的话,会将融合另一个属性,并返回这个伪数组

    jQuery.makeArray(selector, {0: 1, length: 1})
    
    返回这样的对象
    
    0: 1
    1: "111"
    length: 2

     

     

     

     

     

    一.3-jQuery.prototype对象中的属性与方法(上)

    标签:context   xpl   简单   索引   body   是什么   长度   eth   core   

    原文地址:https://www.cnblogs.com/re-is-good/p/12906215.html

    上一篇:54、js的数据类型及对象

    下一篇:asp.net core cookies认证过期时间


    评论


    亲,登录后才可以留言!

    热门文章

    推荐文章

    最新文章

    置顶文章