深入学习jquery源码之闭包

深入学习jquery源码之闭包

开发jQuery插件的时候会用(function($){})(jQuery); 这个闭包函数来包裹

(function($) {
  // all JS code here
})(jQuery);

自执行函数写法, 函数声明后立即执行. 并且设置函数执行时context为this, 这里的this因执行环境会有所不同, 浏览器环境应该是window.

这里面的$只是形参,但jquery是全局变量,所以不需要调用该函数就会自动执行

整个结合起来意思就是,定义了一个匿名函数,然后又调用该函数,该函数的实参为jQuery。

相当于:

function fun($){…};fun(jQuery);

 

因为这样写有以下三个好处:

  1. 避免全局依赖
  2. 避免第三方破坏
  3. 兼容jQuery操作符$和jQuery

为什么用闭包,是因为需要局部变量

函数外部不能访问函数内部局部变量(私有属性)。因为,函数内部的变量,在函数执行完毕以后,就会被释放掉
使用闭包,可以访问函数的私有变量!

闭包的作用:
①可以在函数外部访问函数的私有变量
②让函数内部的变量可以始终存在于内存中,不会再函数调用完成后立即释放。

闭包是一个拥有父范围访问权限的函数,换名话就是,它可以使函数使用其定义外的变量。

我们不能让别人「直接访问」这个变量。怎么办呢?用局部变量。

但是用局部变量别人又访问不到,怎么办呢?暴露一个访问器(函数),让别人可以「间接访问」。

正是由于 JS 的函数内部可以使用函数外部的变量

function foo(){
  var local = 1
  function bar(){
    local++
    return local
  }
  return bar
}

var func = foo()
func()

如果不 return,你就无法使用这个闭包。把 return bar 改成 window.bar = bar 也是一样的,只要让外面可以访问到这个 bar 函数就行了。所以 return bar 只是为了 bar 能被使用

 

闭包的好处:
不增加额外的全局变量,执行过程中所有变量都是在匿名函数内部。

var add = (function () {
    var counter = 0;
    return function () {return counter += 1;}
})();

add();
add();
add();

// the counter is now 3

 

闭包中的this和外的this都是window

 

 

插件不会污染顶级对象和全局变量,因为两个插件,或者多个js库同时使用,万一有变量函数对象是同名的就会产生冲突。

Javascript变量有本地和全局访问范围,而闭包可以使全局的变量“变成私有”。 

$(function(){

var a=1;

     })

   $(function(){

     alert(a);//undefined

   })
(function(){do someting})();
//这个你就理解为定义一个匿名函数并立即执行
带参数的话就这样:
(function(形参){do someting})(实参);
另外
(function(){var upc="i am upc"})();
alert(upc);
会提示undefined。
因为闭包后,里面的变量就相当于局部了。

定义一个公用的变量

方法一:

(1)$(function(){

            window.a=1;

     })

   $(function(){

          alert(a);

   })

方法二:

(2)

   var a=1;

   $(function(){

        alert(a);

    })

 

变量定义时不使用var关键字,总会被视为全局变量,即使它在函数内定义。 

var a = 4;
function myFunction() {
    a = 10;
}
alert(a);

 

$(function(){}) 与(function($) { })(jQuery);

(function($) { })(jQuery);执行其中的代码时,Dom对象并不一定加载完毕。于此相反的是$(function(){}),这种方法在使用时页面的Dom对象已经加载完毕了。事实上该方法的全写是:$(document).ready(function(){});

页面加载执行$(function () { });一个自调用函数会在定义的时候被自动调用,因为其后面跟着一对括号(方法执行的方式)。

当new实例化或$.fn后会执行window中原先closure中预加载和定义的方法或对象

 

对象工厂函数

var jQuery = (function(){  
  
    var jQuery = function(){  
        return new jQuery.fn.init(selector, context);  
    }  
    return (window.$ = window.jQuery = jQuery);       
});  

这里的jQ函数返回一个新new出来的对象,相当于类的构造函数,而这个对象则是自己prototype里的一个方法的对象,这边的fn属性是jQuery的原型而已,而下面的init则是其初始化函数

var jQuery = (function(){  
  
    var jQuery = function(){  
        return new jQuery.fn.init(selector, context);  
    }  
  
    jQuery.fn = jQuery.prototype = {  
        init: function(){}  
    };  
  
    return (window.$ = window.jQuery = jQuery);   
});  

我们平时用的fn的插件实际是jQ的原型(注意:这边返回的必须是构造函数而不是new好的对象)

上面的便是返回了一个init为构造函数的一个对象,相当于说是我们平时用的jQuery对象实质上是jQ对象自己原型中的一个构造函数对象,这个对象是init对象,并不是jQuery对象本身,所以它的原型是空的,并不包含jQ的原型,所以下面这句话异常重要。

将init的原型指向fn,而fn是指向jQ的原型,相当于init的原型是指向jQ的原型的

jQuery.fn.init.prototype = jQuery.fn;

之后代码变成了这样:

var jQuery = (function(){  
  
    var jQuery = function(){  
        return new jQuery.fn.init(selector, context);  
    }  
  
    jQuery.fn = jQuery.prototype = {  
        init: function(){}  
    };  
  
    jQuery.fn.init.prototype = jQuery.fn;  
  
    return (window.$ = window.jQuery = jQuery);       
});  

再然后便可以往jQ类中加入静态对象,放在内核内的这里的对象或方法往往是要调用对象内私有属性的,下面是对jQ命名空间的工厂函数:

jQuery.extend = jQuery.fn.extend = function(){}

实际上也是jQ原型中定义的一个扩展函数

至此jQ的内核已经结束。接下来时外层闭包中的jQuery对象,这个对象首先持有了内层返回的jQ对象,而这个对象同样其实是一个函数,或者更确切的说是一个类构造函数,然后就可以在外闭包中放一些无需依赖jQ类私有变量的库函数。再对这个类附上库函数。

我们平时用的$(//TODO)或是jQuery(//TODO)则是在用jQ中的init方法new 出一个jQ的对象(更确切说是init对象)

(function(window, undefined){  
    //内核  
    var jQuery = (function(){})();  
  
        //非依赖私有变量的类方法  
        (function(){  
  
              //可以直接调用jQuery外层应用类对其进行扩展  
              jQuery.support = {};   
              jQuery.extend({/*TODO*/}  
  
        })();  
  
})();  

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(Jquery源码)