jQuery源码分析(一):立即调用函数

前端基础 2016-10-24

起步

想看jquery源码已经很久了,用它来dom、动画、事件等操作真是太方便了。终于有个时间来看看源码,结构清晰,模块紧凑,很适合有点小空闲就拿出来阅读,第一次读觉得枯燥无味,多次看就会赞叹它的精妙无比。

应该是会有版本之间的差异,我看的版本是2.14。

依赖结构网

引用一张图:

jquery结构网络.jpg

jQuery一共13个模块。略过中间的代码,可以留下最基础的结构:

(function(global, factory) {
    factory(global);
}(window, function(window, noGlobal) {
    var jQuery = function( selector, context ) {
        return new jQuery.fn.init( selector, context );
    };
    jQuery.fn = jQuery.prototype = {};
    // code ...
    return jQuery;
}));

分析

采用了(function(){})();这中立即执行函数,函数体被执行一次,匿名函数也省略了变量污染。作者用了两层函数,换个方式也许更好理解。

(function(window, noGlobal){
    var jQuery = function() {};
    //code ...
})(window, noGlobal);

效果是一样的,这里有个疑问,window不是最大的对象吗,使用它的属性或方法是不需要window.xxx而直接使用xxx吗,为什么要作为参数传进去?这是没错的,但是这么做事有原因的,其实,是为了减少变量查找所花的时间,如果变量在函数体内就不用经过scope作用域大范围查找。因此,显然把它当成一个局部变量来查找要快一些。

刚开始我一直疑惑noGlobal这是干嘛,alert出来是undefined。找了一些资料才知道。undefined其实不是js的保留字,它是可以被赋值的,在ECMAScript5之前undefined都是可写的。换句话说就是作者为了防止有个2B程序员对undefined进行赋值而使用一个未定义的变量。

变量污染与变量冲突

任何库与框架设计的第一个要点就是解决命名空间与变量污染的问题。jq就是利用js本身函数的作用域采用立即调用表达式将变量包裹的函数里,对外开放的只有jQuery$作为入口。

window.jQuery = window.$ = jQuery;

但是$这么轻便的变量,如果其他框架也将它作为简写怎么办,所以jq提供了noConflict()方法来让出$这个词,用法是先引入其他框架的js,再引入jq。

<script src="other_lib.js"></script>
<script src="jquery.js"></script>

<script type="text/javascript">
  $.noConflict();
  jQuery(document).ready(function() {
      // 使用 jQuery 的代码
  });
  // 使用其他库的 $ 的代码
</script>

可以看看源码是怎么解决变量冲突的:

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;
};

原来它连jQuery变量名冲突的准备都做好了,哈哈。做法是先用_$保留前面的值,再恢复。这也是为什么要先引用其他框架的原因了。

另外:我有个方法不用让出$也能解决冲突问题。哪怕你的项目要用新框架的$而你代码已充斥大量的jq的$,方法其实也是利用立即调用表达式来实现的。

<script>
(function($){
    //这里的$都是jq的
    /*
     * code...
     */
})(jQuery); 
</script>

总结

通过立即执行函数有效保证了不造成全局变量污染,形成了一个"单例"模式的效果并且只会运行一次;通过传入window对象来减少变量查找时间。


本文由 hongweipeng 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。

如果对您有用,您的支持将鼓励我继续创作!