闭包
1、定义函数的两种方法是第一:函数声明;第二:函数表达式。
函数声明:function sayhi(){}
在firfox chrome opera safair有一个name属性。
例如:alert(sayhi.name)//sayhi
函数声明的一大特征就是函数声明提升,意思就是在执行函数之前会先读取函数声明,因此可以吧函数声明放在调用他的后面
sayhi();
function sayhi(){}
函数表达式:var sayhi=function(){};
这种方法这样写:sayhi();var sayhi=function(){}会出错误
理解一下变量提升:变量提升涉及到了作用域的问题。就是把变量提升到一块作用域中或者说是一个函数顶端位置,需要注意的是函数提升只是提升的是变量的声明不会吧这个变量赋的值一同提升上来。
例如:
var v="abc";
alert(v);//abc
(function(){
alert(v);//undefined
var v;// 变量声明提升到函数顶部
v="123;//变量初始化一直保留在原来的位置
alert(v);//123
})()
alert(v)//abc
理解一下函数提升:函数声明语句将会被提升到外部脚本或者外部函数作用域的顶部;
例如:
var getName=function(){<3>
alert(1);
}
function getName(){<2>
alert(2);
}
getName()//1 <1>
在这个例子中涉及到了变量声明提升和函数声明提升,函数声明提升function getName(){}的声明会被提升到顶部,而函数表达式var getName=function(){}则表现出来的是变量声明提升,而变量的赋值依然保持在原来的位置上。函数声明虽然是在函数表达式之后,但是由于函数声明提升会被提升到顶部,又因为getNmae被函数表达式覆盖所以输出1
上面的这个例子写成:
var getName;//变量声明提升
function getName(){//函数变量声明提升到顶部
alert(1);
}
getNmame=function(){//变量复制依然保存在原来的位置
alert(2);
}
getName();//2
2、递归函数:
计算某一个数的阶乘:
function number(num){
if(num<=1){
return 1
}else{
return num*number(num-1)
}
}这种写法是没有任何问题的但是如果换成下面这种写法
var number1;
number1=number;
number=null;
alert(number1(5))//会出错,这种写法就是把number这个函数先保存在number1的这个变量之中,然后再使得number为null为的是使得指向这个函数的指针只有一个,但是无论怎样都要执行number这个函数,但是这个时候number这个函数已经不存在了,所以会出错。在这种情况下用arguments.callee解决问题。arguments.callee是一个指向正在执行函数的指针,因此可以实现对函数递归的调用。
function number(num){
if(num<=1){
return 1
}else{
return num*arguments.callee(num-1)
}
但是在严格模式下会出错,可以用命名函数表达式来达到目的
var number=(function f(num){
if(num<=1){
return 1;
}else{
return num*f(num-1)
}
})
3、闭包:闭包指的就是有权访问另一个作用域中的变量,如何创建作用域链和作用域链有什么作用对理解闭包有很大的作用。当某一个函数被调用的时候首先会创建一个执行环境和作用域链,然后arguements对象和其它命名参数的值来初始化函数的活动对象也就是作用域链中的第一位,但是在作用域链中,外部函数对象的活动对象始终属于第二位,外部函数的外部函数的活动对象处于第三位。
具体来看一下作用域链,例如:
function compare(value1,value2){
if(value1<value2){
return -1;
}else if(value1>value2){
return 1;
}else{
return 0;
}
}
var result=compare(3,2)
上面的这段代码定义了compare函数,然后又在全局中调用它,当调用compare函数的时候会创建一个arguements和value1/value2活动对象,全局变量result和compare是作用域链中的第二位。后台的每个执行环境都有一个表示变量的对象即变量对象,只在函数的执行过程中存在。在创建compare函数的时候会创建一个预先包含全局变量的一个作用域链,这个作用域链包含在内部的scope属性中,当在调用compare函数的时候会创建一个执行环境,然后通过复制scope属性中的值来形成执行环境中的作用域链。在这个例子中其作用域链包括本地活动对象和全局对象,也就是说作用域链本质上就是指向变量对象的指针列表,它只引用但不包含实际的变量对象。无论在什么时候在函数中访问一个变量,就会从作用域链中相应的搜索变量名字,使用之后函数执行完之后局部变量销毁,在内存中只存在全局变量,但是闭包不同。
创建闭包最常用的方法就是在一个函数中间在创建一个函数,例如:
function createcomparisonFunction(propertyName){
return function(object1,object2){
var value1=object1[propertyName];
var value2=object2[propertyName];
if(value1<value2){
return -1
}else if(value1>value2){
return 1;
}else{
return 0;
}
}
}
//创建函数
var compare=createcomparisonFunction("name");
//调用函数
var result=compare({name:"xui"},{name:"aa"})
//解除对匿名函数的引用
compare=null;
解释:在另一个函数内部定义的函数会将外部函数的活动对象添加到自己的作用域链中,因此在函数createcomparisonFunction中定义的匿名函数的作用域链中也包含createcomparisonFunction的活动对象,在匿名函数被返回时它的作用域链被初始化为包含createcomparisonFunction的变量对象的作用域链,因此匿名函数可以访问到外部函数的作用域中的变量。更为重要的就是在createcomparisonFunction函数执行完之后其活动对象不会被销毁,因为匿名函数还在引用,换句话说在createcomparisonFunction函数执行完之后它的作用域中的作用域链会被销毁,但是活动对象不会被销毁,一直会留在内存中,直至匿名函数被销毁他的活动才会被销毁。
总结一下:为什么要是用闭包?
将在创建的变量一直保存在内存中以便于本地使用
函数外部访问函数内部的变量。
4、闭包与变量
闭包也只能获取函数中任何变量的最后一个值
5、关于this
在闭包中使用this对象,我们都知道,this在全局函数中被认为指的是window,如果被当成是某个方法调用的时候会指向那个调用的对象,在事件中指向的是触发这个事件的对象,但是匿名函数的执行环境具有全局性,因此它的this对象通常只得到就是window,所以根据闭包的编写不同,其结果也不同,例如:
var name="the window";
var object={
name:"my object",
getnameF:function(){
return function(){
return this.name
}
}
}
alert(object.getnameF)//the window这个匿名函数就是的 是window
我们可以闭包中保存this来使得它可以指向外部函数
var name="the window";
var object={
name:"my object",
getnameF:function(){
var that=this;
return function(){
return that.name
}
}
}
alert(object.getnameF)//my object
6、内存泄漏:指任何对象在不在拥有或者需要他的时候仍然存在。
什么操作会引起内存泄漏?
闭包。为什么闭包会导致内存泄漏?正是因为闭包的那一大特性,外部函数就算执行完之后其对象也不会销毁,依然存在在内存中,只有匿名函数销毁之后才会销毁。
循环引用。在垃圾回收器进行垃圾回收的时候,该变量的引用次数永不为0.
控制台日志
setTimeout的第一个参数使用字符串而不是函数
7、模仿块级作用域
(function(){
//这里是块级作用域也叫做私有作用域
})()
无论在什么地方,当临时需要一些变量的时候就可以使用私有作用域
例如:function abc(count){
(function(){
for(var i=0;i<count;i++){
alert(i)
}
})();
alert(i);
}变量i只能在for循环中使用,在这个私有作用域中可以访问到count那是因为这是形成了一个闭包
这种技术通常在全局作用域中被用在函数外部。在一个由很多人合作的项目中可以通过创建私有作用域来达到不扰乱全局作用域的目的。私有作用域可以减少闭包所占用的内存问题。
例如:(function(){
var now=new Date();
if(now.getMonth()==0&&now.getDate()==1){
alert("happy new year")
}
})()
8、私有变量
在js中是没没有私有变量这一个说法的,但是在一个函数中所创建的变量在全局作用域终是不能访问到的,这其实也是一种私有说法。我们把有权访问私有变量和私有方法的共有函数成为特权方法。有两种创建特权方法的方法,第一种:构造函数中定义特权方法,例如
function myobject(){
//私有变量和私有函数
var private=10;
function privatefunction(){
return false;
}
//特权函数
this.publicfunction=function(){
private++;
return privatefunction();
}
}
var object=new myobject();
alert(object.publicfunction());//false
解释:能够在构造函数中创建特权方法,那是因为这个特权方法是一个闭包可以访问外部函数的两个私有变量。对于这个例子来说,变量private和私有函数privatefunction只能通过特权方法publicfunction来访问,其他是不能访问到这两个私有变量的。
利用私有和特权方法可以隐藏那些不应该被直接修改的数据。
例如:function Person(name){
this.getname=function(){
return name;
}
this.setname=function(value){
name=value;
}
}
var person=new Person("xuqing");
alert(person.getname)//xuqing
person.setname("chang");
alert(person.getname)//chang
这两个特权方法作为闭包可以通过作用域链来访问到name的这个属性,使用构造函数的缺点啊就在于你只能用构造函数的这个模式,并且针对每一个实例都会创建相同的 方法,第二种方法静态私有变量可以解决这个问题
第二种方法:静态私有变量:在私有作用域中定义私有变量和函数也可以创建特权函数和方法
模式:(function(){
//私有变量和私有函数
var private=10;
function privatefunction(){
return false;
}
//构造函数
myobject=function(){};//未声明的变量会变成全局变量
//创建特权方法
myobject.prototype.publicfunction=function(){
private++;
return privatefunction()
}
})()
这个模式和在构造函数中创建特权方法的区别就在于私有变量和函数是由实例共享的,因为这个特权方法是在原型上的所以所有的实例都享用同一个函数方法。
(function(){
var name="";
person=function(value){
name=value;
};
person.prototype.getName=function(){
return name;
};
person.prototype.setName=function(value){
name=value;
};
})()
var person1=new person("xuqing");
alert(person1.getName());
person1.setName("changjiang");
alert(person1.getName());
9、模块模式:是为单例创建的私有变量和特权方法,所谓单例指的就是只有一个实例的对象。在js中是通过对象字面量的方法来创建单例对象的。
var single={
name:value;
method:function(){
//这里是方法代码
}
}
利用模块模式来创建单例的私有变量和特权方法。
var application=function(){
//私有变量和函数
var components=new Array();
//初始化
components.push(new BaseCompnent());
//公共
return {
getcount:function(){
return components.length;
};
rigister:function(component){
if(typeof component=="object"){
components.push(component);
}
}
};
}
在web程序中经常需要一个单例来管理应用程序级的信息,这个例子创建了一个管理组件的application对象。返回的那两个方法都是application对象的特权方法。
10、增强的模块模式,其实改变return 那一段函数,创建代替那段函数的额副本之后把这个副本返回回来。
var application=function(){
//私有变量和函数
var components=new Array();
//初始化
components.push(new BaseCompnent());
//创建application的一个副本
var app=new BaseCompnent();
//公共接口
那两个方法
app.---=function(){}
//返回这个副本
return app
}