前文已经简单的介绍了函数的闭包。函数的闭包就是有权访问另一个函数作用域的函数,也就是函数内部又定义了一个函数。
1 var Super=function(num){
2 var count=num;
3 return function(){
4 console.log(count);
5 }
6 }
7 var result=Super(3);//此时result是一个函数
8 result();//输出3
上面的代码定义了一个函数Super,同时在Super函数内部又定义了一个匿名函数作为返回值。第七行调用Super函数,此时result是一个函数。第8行执行了result函数,输出为3。这就是闭包的体现,因为上面的Super函数已经执行结束,但是它的内部变量count的值依然没有被释放,count的还在被匿名函数引用,所以没有办法释放。如果要释放count,我们需要 result=null ,将null赋值给result。
在前文已经介绍了函数的作用域链,当函数第一次被调用时会创建一个作用域链,并作用域链赋值给一个特殊的内部属性。在作用域链中,函数的外部函数的活动对象位于第二位,外部函数的外部函数的活动对象位于第三位,以此类推,全局变量的作用域链位于最底部。
1 var count=2;
2 console.log(count);//2
3 count=3;
4 console.log(count);//3
5 var Super=function(num){
6 var count=num;//作用域链第二位
7 console.log(count);//1
8 return function(){
9 var count=5;//作用域链第一位
10 console.log(count);
11 }
12 }
13 var result= new Super(1);
14 result();//输出5
上面的代码中,能够清晰地了解到变量的作用域。闭包函数的内部变量位于最顶端,全局变量位于最底部。
在闭包中使用this对象也可能会导致一些问题,this对象是在函数运行时,基于函数的执行环境绑定的。在全局函数中,this指向window对象。而函数作为某个对象的方式调用时,this等于那个对象。不过匿名函数的执行环境具有全局性,因此this指向window
1 var obj={
2 name:"heh",
3 getName:function(){
4 var that=this;
5 return function(){
6 return that.name;
7 }
8 }
9 };
10 var one=obj.getName();
11 var name=one();//heh
上面的代码中,通过字面量的方式创建了对象obj,定义了对象的属性name和方法getName。但是在getName内部,我们定义了闭包函数。如果想在闭包函数中访问name,通过this是访问不到的。所以需要在闭包函数的外部定义一个变量that,指向this。在getName中定义的变量,在闭包函数中仍然可以继续使用。
JavaScript中没有块级作用域的概念,这意味着块级中定义的变量,实际在函数内部都是可以使用的。
1 for(var i=0;i<10;i++){
2 console.log(i);
3 }
4 console.log(i);//输出10
上面的代码中,我们在for循环中定义了变量i,但是我们在for循环外部依然可以使用i。for循环结束后,i变量并没有被销毁。
JavaScript可以使用匿名函数来模仿块级作用域,从而避免该类问题的发生。
1 (function(){
2 for(var i=0;i<10;i++){
3 console.log(i);
4 }
5 })();
6 console.log(i);
上面的代码中,我们将块级作用域放在了一个匿名函数中,同时将匿名函数放在一对括号中,这表示一个函数表达式。在函数表达式外部的括号,表示立即调用该函数。在第六行调用该函数的时候,会发生报错,因为i并没有定义。
1 var testFunc=function(){
2 for(var i=0;i<10;i++){
3 console.log(i);
4 }
5 };
6 testFunc();
7 console.log(i);
这个代码,和我们上面的代码是一样的,都是通过函数表达式来定义函数。
1 function(){
2 for(var i=0;i<10;i++){
3 console.log(i);
4 }
5 }();
上面的代码是错误的。我们知道函数的定义方法,可以通过function和函数表达式。通过function声明函数的时候,function后面不能跟圆括号。函数表达式的后面可以跟圆括号。
1 var testFunc=function(){
2 for(var i=0;i<10;i++){
3 console.log(i);
4 }
5 }();
一般来说在开发过程中,应该尽量少向全局对象中添加函数和变量。太多的全局函数和变量容易导致命名的冲突以及内存的泄露。我们可以在块级函数中完成所有的操作。
1 (function(){
2 var now = new Date();
3 console.log(now.getFullYear()+"-"+(now.getMonth()+1)+"-"+now.getDate()+" "+now.getHours()+":"+now.getMinutes()+":"+now.getSeconds());
4 })();
通过上面的代码,我们能够当前的日期和时间。