深入学习jquery源码之闭包
开发jQuery插件的时候会用(function($){})(jQuery); 这个闭包函数来包裹
(function($) {
// all JS code here
})(jQuery);
自执行函数写法, 函数声明后立即执行. 并且设置函数执行时context为this, 这里的this因执行环境会有所不同, 浏览器环境应该是window.
这里面的$只是形参,但jquery是全局变量,所以不需要调用该函数就会自动执行
整个结合起来意思就是,定义了一个匿名函数,然后又调用该函数,该函数的实参为jQuery。
相当于:
function fun($){…};fun(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*/}
})();
})();