您的位置:首页 > Web前端 > JavaScript

js闭包

2016-09-13 16:10 239 查看
在js中,外部函数是不能访问函数内部变量的,但是在实际编程中,往往会需要去访问函数的内部变量。js中怎么才能访问到函数的内部变量呢?这里就涉及到js的一大特点----闭包。

闭包是js中的一大特色,也是一个难点,很多高级应用都要依靠闭包实现。

var fn=(function(){
var i=0;
return function (){
return i++;
}
})();
console.log(fn());//0
console.log(fn());//1
console.log(fn());//2
闭包函数执行完毕后内部变量i并没有被回收,fn函数实现了闭包函数中变量i的自加,成功的访问到了闭包函数中的变量i;

一、什么是闭包?

闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。

闭包的概念非常抽象,简单的来说就是函数内部的函数被函数外部的变量应用,就形成了一个闭包。

二、闭包书写格式

闭包的书写形式灵活多变,只要能满足函数外部能访问到函数内部变量都能说是函数闭包
最常见的书写格式:
(function fn(){
console.log("a");
})();
(function(){
alert("b");
}());


可能会有新手会对这种书写方式感到奇怪,下面就说下函数命名的两种方式:
1.函数声明:function关键字开头。

fn() // a
function fn(){
console.log('a');
}


 特点:这种方式是声明的函数存在变量提示(函数整体提前),不能直接调用。
 2.函数表达式:函数表达式有多种书写方式,(闭包就是其中特殊的一种),通常是一下这种形式。  
          

var fn2=function(){
console.log('b');
}();   //b


  特点:1函数声明的函数存在变量声明提前
            2能直接调用该函数
特殊的函数表达式:函数表达式可以以个别特殊字符来声明,如:“!”,“—”,“+”,“()”等;

!function(){
console.log('a');
}();
(function(){
console.log('b');
})();


三、闭包的作用:
1.局部变量始终保持在内存中,外部函数可以访问函数内部变量。

2.模拟了块级作用域,避免了全局变量的污染。
3.私有成员的存在。
闭包最主要的作用就是让局部变量始终保持在内存中,js中在ES6之前版本是没有块级作用域的,但是闭包通过函数作用域模拟了块级作用域,避免了全局变量的污染。
顺便说下什么是私有成员:

var fn=function(name){
this.name=name;
this.setName=function(){  //公共方法
console.log('公共方法');
};
this.f=function(){        //特权方法
this.getName();
}
function private(){}  //私有方法

}
fn.static=function(){}//静态方法
fn.prototype.getName=function(){
this.setName();
}
公共方法:实例对象可以条用的方法。

特权方法:函数中有调用公共方法。
私有方法:构造函数中有var声明或者是function声明的方法
静态方法:构造函数所能调用的方法。

四、闭包中的作用域以及作用域链
为什么闭包中的变量没有被回收?
当函数被创建时,也会创建相应的作用域链(scope chain),保存在该对象的[[scope]]属性中。当函数被调用时,会进入相应的执行环境,通过复制[[scope]]属性中的对象,构建执行环境中的作用域链。每一个执行环境都有一个关联的变量对象。执行环境中的所定义的变量和方法都保存在这个变量对象上。然后会创建一个活动对象(如果被激活的对象是一个函数,那么它的活动对象就是变量对象)。函数在执行中所调用到的方法,和属性都会在当前的活动对象中去检索,如果没有检索到相应的方法或者属性,会向上一个活动对象中检索。每一个函数都有自己的执行环境和相关联的变量对象。全局环境的变量对象会一直存在。

接下来看一段代码:

function fn(){
var i=0;
return function d (){
return console.log(i++);
}
}
var add=fn();
add();  //0
add();  //1
add();	//2

当函数fn被创建时,同时会创建[[scope chain]]作用域链,这个作用域链中包含两个变量对象:一个是全局变量对象,一个是fn变量对象。当fn被调用时,会进入到该fn的执行环境,活动对象被激活,函数fn中定义的所有变量都以活动变量的方法和属性存在,此时fn的活动对象中包括(arguments,i,内部function),内部函数d存储到全局变量add中。当add函数被调用时,内部函数d活动对象被激活,当前活动对象并没有变量i,则会到fn活动对象中去查找变量i,匿名函数中引用了fn活动对象中的变量i,所以当退出fn的执行环境是,fn的活动对象任然被引用,所以不会被销毁。
如果想了解跟多闭包中的作用域作用域两以及执行上下文什么的可以参考:

https://www.zhihu.com/question/36393048/answer/71859125 http://www.cnblogs.com/liudaxia/p/4859366.html http://www.cnblogs.com/mqliutie/p/4422247.html
以上只是个人对js的浅解,水平有限,有错误的地方还请大神指正。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息