JQuery源码阅读(一)整体架构

很早就听一些大神说要读源码,直到前一段时间春招受挫才立下决心,先入手一个JQuery吧,以后有机会可以摸一摸Vue。

jQuery,说起都有一种张国荣、陈百强的感觉了,但是还是可以重温的,面试官教育我,不能盲目跟风,人云亦云。
其实看了几天了,一行一行看没有重点,直到看了几个大佬的博文(比如下面这位),觉得可以尝试了。


一位大佬.png

还有一位出了一个系列的,大家在中文社区应该看得到(他也参加过蚂蚁金服17春招,而且通过了,敬佩之心油然而生!)。

从外层入手

( function( global, factory ) {
  "use strict";
} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {

  if ( !noGlobal ) {
    window.jQuery = window.$ = jQuery;
  }// 在node环境非全局执行时不会暴露为接口
});

首先把映入眼帘的最外层拿出来,就是一个自执行函数,大佬也叫闭包。可以做到‘私有’和暴露到执行环境指定的jQuery接口(noGlobal控制)。
自执行函数里放的啥?

if ( typeof module === "object" && typeof module.exports === "object" ) {
// 自执行函数(模块化)commenJs?(存在document属性?
// 执行函数:暴露包装函数待传入一个含document的对象):执行函数
    module.exports = global.document ?
      factory( global, true ) :
      function( w ) {
        if ( !w.document ) {
          throw new Error( "jQuery requires a window with a document" );
        }
        return factory( w );
      };
  } else {
    factory( global );
  }  // node环境检查是否是全局执行,非node环境直接执行

判断了是不是commonJS(node的模块化标准),传入的执行环境作用域变量是不是有document。

进入正题

现在可以正视那个被严格控制的神秘又长的函数了。JQuery是啥?我找一下,看起来一时半会没有一个全面的答案。

var
    version = "3.3.1", //版本号呗,忽视一下。
    jQuery = function( selector, context ) {
      return new jQuery.fn.init( selector, context );//jQuery ()实际实例化了init
    };
  jQuery.fn = jQuery.prototype = { };

jQuery就是一个起构造作用的函数,实例化了 jQuery.fn.init。fn是一个私有属性,指向jQuery的原型。

init是啥
var rootjQuery,
    rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,
    init = jQuery.fn.init = function( selector, context, root ) {
  var match, elem;
  // 规避: $(""), $(null), $(undefined), $(false)
  if ( !selector ) {
    return this;
  }
  // Method init() accepts an alternate rootjQuery
  // so migrate can support jQuery.sub (gh-2101)
  root = root || rootjQuery;
  // Handle HTML strings
  if ( typeof selector === "string" ) {
    // selector是string
  } else if ( selector.nodeType ) {
    // selector是DOM节点
  } else if ( isFunction( selector ) ) {
    // selector是函数
  }
  return jQuery.makeArray( selector, this );
};

开始有些东西试图阻止我顺藤摸瓜了,先记下来。

rootjQuery、jQuery.makeArray|init私有match,elem

注意到了return this;如果被作为一个构造函数使用,这个return是不言自明的,但是他明说了。猜测,有非new的调用,或者定制无论何种调用的return(返回一个空对象可能想得到其原型?)。
先往下瞄一眼 眼熟吧,一句委托,return的this就是prototype指向jQuery原型的空对象。

init.prototype = jQuery.fn;

有三个流程判断语句,我要捡软的捏!

selector是DOM节点
// HANDLE: $(DOMElement)
} else if ( selector.nodeType ) {
    this[ 0 ] = selector;
    this.length = 1;
    return this;
}

第一个参数如果传入的DOM节点,放入0属性,length属性置1,返回了一个像数组的对象。

selector是函数
// HANDLE: $(function)
// Shortcut for document ready
} else if ( isFunction( selector ) ) {
    return root.ready !== undefined ?
        root.ready( selector ) :
        // Execute immediately if ready is not present
        selector( jQuery );
}

init参数root.ready

在前面jQuery实例化的时候并没有传入root参数,在没有root的情况下执行selector( jQuery ),应该可以在传入的函数里扩充jQuery函数的内容吧。

selector非DOM、function、string
return jQuery.makeArray( selector, this );

那就return 让这个未知的函数执行呗!

selector是string
if ( typeof selector === "string" ) {
  if ( selector[ 0 ] === "<" &&
    selector[ selector.length - 1 ] === ">" &&
    selector.length >= 3 ) { // /^<[\w\W]+>$/
    match = [ null, selector, null ];
  } else {
    match = rquickExpr.exec( selector );
  }
  if ( match && ( match[ 1 ] || !context ) ) {

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

  } else {

  }
}

这段的结构也被抽离出来了。有必要回看rquickExpr了/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,就是它,匹配到以空格或<[\w\W]+>开头的字符串。
这样match就有两种样子了,[ null, selector, null ]或正则匹配后的(数组第二项为去空格的标签字符串)。

if ( match && ( match[ 1 ] || !context ) ) {
  // HANDLE: $(html) -> $(array)
  if ( match[ 1 ] ) {
    // HANDLE: $(#id)
  } else {
    elem = document.getElementById( match[ 2 ] );
    if ( elem ) {
      // Inject the element directly into the jQuery object
      this[ 0 ] = elem;
      this.length = 1;
    }
    return this;
  }
}

如果match的第二项空,则按照第三项获取全局的对应id的节点,存入类数组对象返回。

if ( match[ 1 ] ) {
    context = context instanceof jQuery ? context[ 0 ] : context;
    jQuery.merge( this, jQuery.parseHTML(
                match[ 1 ],
                context && context.nodeType ?context.ownerDocument || context 
                : document,
                true
                ) );
    // HANDLE: $(html, props)
    if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context )) {
        for ( match in context ) {
            // Properties of context are called as methods if possible
            if ( isFunction( this[ match ] ) ) {
                this[ match ]( context[ match ] );
                // ...and otherwise set as attributes
            } else {
                this.attr( match, context[ match ] );
            }
        }
    }
    return this;
}

要补充一句了

var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i );

jQuery.merge jQuery.isPlainObject init的方法attr

其中context.ownerDocument得到页面根节点,而rsingleTag 可以匹配一个无文本的完整标签。
后面的for-in循环大概是可以判断出操作的合法性了后,对初始化的对象拷贝属性,在原基础上重写DOM节点的一些方法。

else if ( !context || context.jquery ) {
    return ( context || root ).find( selector );
    // HANDLE: $(expr, context)
    // (which is just equivalent to: $(context).find(expr)
} else {
    return this.constructor( context ).find( selector );
}

find()可能就是我们用到的find方法

其他情况则以第二参数构造,再find第一参数。
另外,向下瞄一眼rootjQuery 找到了(init了document)。

// Initialize central reference
rootjQuery = jQuery( document );

整体架构的入手和init函数就完成了,希望能坚持下去吧。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,294评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,780评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,001评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,593评论 1 289
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,687评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,679评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,667评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,426评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,872评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,180评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,346评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,019评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,658评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,268评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,495评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,275评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,207评论 2 352

推荐阅读更多精彩内容

  • jQuery jQuery是JavaScript世界中使用最广泛的一个库。 jQuery这么流行,肯定是因为它解决...
    星腾_范特西阅读 2,089评论 0 27
  • 概要 64学时 3.5学分 章节安排 电子商务网站概况 HTML5+CSS3 JavaScript Node 电子...
    阿啊阿吖丁阅读 9,171评论 0 3
  • 1.JQuery 基础 改变web开发人员创造搞交互性界面的方式。设计者无需花费时间纠缠JS复杂的高级特性。 1....
    LaBaby_阅读 1,334评论 0 2
  • 1.JQuery 基础 改变web开发人员创造搞交互性界面的方式。设计者无需花费时间纠缠JS复杂的高级特性。 1....
    LaBaby_阅读 1,169评论 0 1
  • 今早拉开窗帘阳光便落在了脸上,初冬季节能拥有这样的阳光实属不易。这些天扭伤了脚,医生交代需在家里静养,吃完午饭一直...
    镜中的我阅读 394评论 0 0