jQuery源码解析(一):读懂源码结构、改变固有思惟方式

前端必备:熟读框架源码,今天来分析jQuery的源码css

下载源码

咱们想要读一个框架的源码,那第一步确定是下载源码(这是废话)。前端

地址:jQuery.js(本文代码版本为jquery3.4.1jquery

点击上面地址,将代码粘贴到本身的本地编辑器中,而后开始进行下一步工做。git

源码结构

这一步,咱们分析代码,研究代码结构es6

咱们经过观察源码的结构,将代码分割成了三个部分github

/*
    * 第一部分:匿名函数结构
    */
    /*!
     * jQuery JavaScript Library v3.4.1
     * https://jquery.com/
     *
     * Includes Sizzle.js
     * https://sizzlejs.com/
     *
     * Copyright JS Foundation and other contributors
     * Released under the MIT license
     * https://jquery.org/license
     *
     * Date: 2019-05-01T21:04Z
     */
    ( function( global, factory ) {

    	"use strict";
    
    	if ( typeof module === "object" && typeof module.exports === "object" ) {
    
    		// For CommonJS and CommonJS-like environments where a proper `window`
    		// is present, execute the factory and get jQuery.
    		// For environments that do not have a `window` with a `document`
    		// (such as Node.js), expose a factory as module.exports.
    		// This accentuates the need for the creation of a real `window`.
    		// e.g. var jQuery = require("jquery")(window);
    		// See ticket #14549 for more info.
    		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 );
    	}

    // Pass this if window is not defined yet
    } )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
    /*
    * 第二部分:功能代码(省略)
    */
    
    
    // 第三部分:返回
    // Register as a named AMD module, since jQuery can be concatenated with other
    // files that may use define, but not via a proper concatenation script that
    // understands anonymous AMD modules. A named AMD is safest and most robust
    // way to register. Lowercase jquery is used because AMD module names are
    // derived from file names, and jQuery is normally delivered in a lowercase
    // file name. Do this after creating the global so that if an AMD module wants
    // to call noConflict to hide this version of jQuery, it will work.
    
    // Note that for maximum portability, libraries that are not jQuery should
    // declare themselves as anonymous modules, and avoid setting a global if an
    // AMD loader is present. jQuery is a special case. For more information, see
    // https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon
    
    if ( typeof define === "function" && define.amd ) {
    	define( "jquery", [], function() {
    		return jQuery;
    	} );
    }




    var

	// Map over jQuery in case of overwrite
	_jQuery = window.jQuery,

	// Map over the $ in case of overwrite
	_$ = window.$;

    jQuery.noConflict = function( deep ) {
    	if ( window.$ === jQuery ) {
    		window.$ = _$;
    	}

    	if ( deep && window.jQuery === jQuery ) {
    		window.jQuery = _jQuery;
    	}
	    return jQuery;
    };

    // Expose jQuery and $ identifiers, even in AMD
    // (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
    // and CommonJS for browser emulators (#13566)
    if ( !noGlobal ) {
    	window.jQuery = window.$ = jQuery;
    }




    return jQuery;
});
复制代码

首先介绍一下匿名函数:编程

①匿名函数的结构json

/*
    * 第一个括号内为匿名函数
    * 第二个括号用于调用函数,并传参
    */
    (function(x,y){
        
    })(x,y)
复制代码

②匿名函数的做用浏览器

  • 匿名函数最主要的做用是建立闭包【为何要使用闭包】
  • 闭包指的是有权访问另外一个函数做用域中的变量函数,也就是说函数内部再建立一个函数
for(var i = 0;i <= 10;i++) {
        setTimeout(() => {
            console.log(i);
        }, 1000)
    }
复制代码

咱们执行上面这一段代码,结果是输出了1111,以下图:bash

那为何会这样?咱们知道, for循环执行的速度要比 setTimeout快得多,因此当跳出for循环时才能够执行到 setTimeout里面的函数中( console.log(i)),因此执行上述代码片断,输出的是咱们所看到的结果。

因此,如今咱们想输出1-10,那咱们应该怎么作?

通常有两种方式:

I、一种是用es6中的声明变量关键字let代替var,咱们来看一下效果

II、另外一种就是使用匿名函数

for(var i = 0;i <= 10;i++) {
        (function(i){
            setTimeout(function(){
                console.log(i);
            }, 1000)
        })(i)
    }
复制代码

在这个方法中,咱们使用了匿名函数,匿名函数传入参数i,并接收,这个时候也获得了咱们想要的0-10的结果,以下图

  • 为何要使用匿名函数

①匿名函数能够自动执行

②提供模块化,将变量传入,为全局变量提供局部范围,能够减小查找时的开销量等。

介绍完上述基本概念,下面进行代码解析

代码分析

1、第一部分

1.1 先来看第一大段注释部分

这么一段很长的注释包括了:jQuery库的版本以及地址、包含的库Sizzle.js及官网地址,其中这个Sizzle.js是一个纯JavaScript css选择器引擎、版权说明、以及发布日期

1.2 接下来看匿名函数部分

1.2.1 它传入了两个参数并接收

  • typeof window !== "undefined" ? window : this 这里对浏览器是否支持window环境作了一个判断,假如不支持,则传入this对象

  • function( window, noGlobal ) {} 这里传入了一个函数,windownoGloabl做为参数,其实这个函数就是jQuery的功能函数部分

  • 同时接收了上面两个参数(global对象和factory函数)。

1.2.2 "use strict"

  • 这两个单词的意思是,使用了严格模式,即在严格模式下运行代码(建议加上~)。

1.2.3 咱们看下面这一段代码,if-else语句中判断了module的类型,其实这段代码的含义,就是咱们在使用jQuery框架的时候,是用了哪一种方式引入:

if ( typeof module === "object" && typeof module.exports === "object" ) {
		// 先省略
	} else {
		factory( global );
	}
复制代码
  • 假如,咱们这样用script标签引入:

<script src="jQuery.js"></script>

这个时候,是检测不到module的,也就是module的值为undefined,那么这个时候代码天然而然的就执行了else语句,也就是直接调用了factory函数,并在函数中传入this或是Windows对象。

  • 而后,咱们这样用require引入jQuery
const jquery = require('jquery');
复制代码

咱们能够尝试输出一下module,获得了这样的结果

能够看到, module再也不是 undefined,而是一个 json类型的数据,这个时候也就执行了 if-else语句中的 if条件下的语句

1.2.4 接下来,咱们看if条件下到底写了些什么?

// For CommonJS and CommonJS-like environments where a proper `window`
    // is present, execute the factory and get jQuery.
    // For environments that do not have a `window` with a `document`
    // (such as Node.js), expose a factory as module.exports.
    // This accentuates the need for the creation of a real `window`.
    // e.g. var jQuery = require("jquery")(window);
    // See ticket #14549 for more info.
    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 );
    	};
复制代码
  • 先看注释
//对于CommonJS和相似于CommonJS的环境,其中有一个适当的window
    //当前,执行factory并得到jQuery。
    //对于没有带有document的window的环境
    //(例如Node.js),将工厂公开为module.exports。
    //这强调了建立一个真正的window的必要性。
    //例如var jQuery = require("jQuery")(window);
复制代码

这里会判断global里面有没有document属性,若是有,就会执行factory函数,若是没有就会抛出一个异常(jQuery requires a window with a document[jQuery须要一个带有documentwindow]),这个时候一般是所处的浏览器环境不支持使用jQuery框架。

2、第三部分

咱们仔细观察这一部分,就会发现:这一部分其实就是处理向外暴露jQuery和$标识符,代码在这一部分判断了不少种状况,这里的处理便于大众使用jQuery框架。

3、第二部分

这一部分是功能代码部分,大概有一万多行的代码,看这一部分的代码,要学会人家的编程思想以及编写一个框架的思惟方式

PS: 建议像我这样没有把jQuery API全记下来的群体,能够对照着API文档看源码,这样可以更好的理解、了解功能的实现过程,也可以更好的记住这些API

总结

咱们经过上述的步骤,分析了jQuery源码,了解了一个框架的基本结构,也知道了它为何会这样写框架。咱们也就能够经过上面的方法,不妨能够尝试写出来一个属于本身的库~

对于仍是一个小白的我来说,写出来的文章会有很差、甚至错误的地方,还但愿你们积极指正~让小白快速成长~

相关文章
相关标签/搜索