JQuery源码01 -- 入口

247 阅读1分钟

在最终导出的dist文件中,最外层是自执行函数执行,IIFE两个形参global, factory,用于区分环境。 jquery导入相当于自执行函数执行,将内部所有内容挂载到window的某个属性上,形成一个闭包空间,用户可以通过window.$访问jQuery暴露的东西

基本骨架

监测环境

/**
 * 检测环境:利用暂时性死区
 * 如果当前环境中没有window,typeof检测不会报错,会返回undefined。
 *    若当前环境中没有window(node环境),则global = this
 *    若当前环境中有window(浏览器、webview、webpack环境),则global = window
 */
    let params1 = typeof window !== 'undefined' ? window : this



/**
 * jq核心代码
 * commonJS的webpack环境:window = window,noGlobal = true
 * 浏览器环境:window = window,noGlobal = undefined
 */
    let params2 = function (window, noGlobal) { ... }


// =========================================环境区分===================================================
/**
 * jquery环境区分
 * 使用严格模式
 */
    (function (global, factory){
      "use strict"
      /**
       * && 检测当前环境是否支持CommonJS规范,同样利用了暂时性死区
       *    若支持:则是node环境或webpack环境
       *    若不支持:则是浏览器/webview环境
       */
          if( typeof module === "object" && typeof module.exports === "object" ){
                /**
                 * 支持commonJS规范
                 */
                module.exports = global.document ? 
                    // 有document,则说明global是window,所以webpack环境
                    factory(global, true) : 
                    // 无document, 则是node环境,导出代理函数
                    // 没window的情况下导出函数,函数也需要可以接收一个window,正常执行
                    // 纯node环境不支持jquery(node没必要操作DOM)
                    function (w){
                      if ( !w.document ) {
                          throw new Error( "jQuery requires a window with a document" );
                        }
                        return factory( w );
                    }
          } 
          else {
            /**
             * 不支持commonJS规范
             * 浏览器环境,webview环境
             * &&& params2回调函数执行 
             */
                factory( global )
          }
    })(params1, params2)

总结

在自己写插件/组件时,需要让组件支持SCRIPT导入,commonJS/ES6Module,可以采用上方的方式区分。或在老版本的某些库进行重构,添加一层环境兼容。

(function (){
    function xxx(){};
    ...
    // 判断window环境
    if(typeof window !== undefined) {
        window.xxx = xxx;
    }
    // 判断commonJS规范
    if(typeof module === 'object' && typeof module.exports === 'object') {
        module.exports = xxx;
    }
})()

jQ挂载

// 主函数
let params2 = function(window, noGlobal){
    // 一些操作
    ...
  /**
   * 声明jQuery这个函数
   */
      var jQuery = function (selector, context) {}
   // 一些操作
   ...
   
   
//==========================================多库共存============================================
 /**
   * 解决全局属性名jQuery和$命名和别的类库冲突
   * eg:Zepto向全局暴露的是$,jQuery向全局暴露的也是$,后写的$覆盖前面的$
   *      <script src = "js/Zepto.js"></script>
   *      <script src = "js/jQuery.js"></script>
   * var _jQuery、_$ 保存挂载前的window.jQuery(= undefined) -->
   * 和window.$(等于之前导入过模块的冲突$的代表或undefined)
   * 声明转让$的函数noConflict
   * 解决冲突:let jq = $.noConflict(),jq代表jquery,此时jq-->jQuery,$-->Zepto
   */
        var
        // 若引入不同版本jquery冲突,则此时获得的_jquery不再等于undefined,而是上一个引入的版本jquery
        _jQuery = window.jQuery,
        _$ = window.$;
       // noConflict函数暴露给用户,让用户解决window上的命名冲突
        jQuery.noConflict = function( deep ) {
          if ( window.$ === jQuery ) {
            // 调用noConflict方法,则jQuery一定占用了$,则把$归还给别人的$
            // _$存储的是被覆盖的$存的内容
            // 最后方法返回jquery,可以被用户以别的变量引用
            window.$ = _$;
          }
          // 当一个产品导入不同版本的jquery时,不仅$冲突了,jquery也冲突了
          //deep参数控制版本冲突时是否转让现有版本的jQuery名字
          if ( deep && window.jQuery === jQuery ) {
            //同上归还,
            window.jQuery = _jQuery;
          }
          // 解决冲突时,自己起一个别名来代表jquery,特殊符不强占别人。let jq = $.noConflict()。
          return jQuery;
        };


//==========================================挂载============================================
   /**
    * 挂载jq到浏览器环境使用
    */
      if ( typeof noGlobal === "undefined" ) {
         window.jQuery = window.$ = jQuery;
      }
      
   /**
    * amd环境使用
    */
     if ( typeof define === "function" && define.amd ) {
         define( "jquery", [], function() {
             return jQuery;
         });
    }      
    
   /**
    * & 挂载jq到commonJS使用
    * module.exports = global.document ? factory(global, true) : function (w) {...}
    * 即jquery.js文件的module.exports = factory(global, true)返回值 = jQuery
    */
      return jQuery
}