JS闭包

前景知识

函数每被调用一次,都会产生一个新的执行上下文环境。

函数在定义的时候(不是调用的时候),就已经确定了函数体内部自由变量的作用域。

在执行代码之前,把将要用到的所有的变量都事先拿出来,有的直接赋值了,有的先用undefined占个空。

处于活动状态的执行上下文环境只有一个。其实这是一个压栈出栈的过程——执行上下文栈。

作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。

作用域在函数定义时就已经确定了。而不是在函数调用时确定。

作用域只是一个“地盘”,一个抽象的概念,其中没有变量。要通过作用域对应的执行上下文环境来获取变量的值。

作用域中变量的值是在执行过程中产生的确定的,而作用域却是在函数创建时就确定了。

所以,如果要查找一个作用域下某个变量的值,就需要找到这个作用域对应的执行上下文环境,再在其中寻找变量的值。

在A作用域中使用的变量x,却没有在A作用域中声明(即在其他作用域中声明的),对于A作用域来说,x就是一个自由变量。

如果跨了一步,还没找到呢?——接着跨!——一直跨到全局作用域为止。要是在全局作用域中都没有找到,那就是真的没有了。

这个一步一步“跨”的路线,我们称之为——作用域链。

我们拿文字总结一下取自由变量时的这个“作用域链”过程:(假设a是自由量)

第一步,现在当前作用域查找a,如果有则获取并结束。如果没有则继续;

第二步,如果当前作用域是全局作用域,则证明a未定义,结束;否则继续;

第三步,(不是全局作用域,那就是函数作用域)将创建该函数的作用域作为当前作用域;

第四步,跳转到第一步。

要到创建这个函数的那个作用域中取值——是“创建”,而不是“调用”,切记切记

闭包

简单来说,闭包就是能够读取其他函数内部变量的函数。

由于在JS中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”。

闭包的作用:封装变量,收敛权限

闭包应用的两种情况:函数作为返回值,函数作为参数传递。

1.函数作为返回值

function fn(){
  var max = 10;
  
  return function bar(x){
    if(x>max){
      console.log(x);
    }
  }
}

var f1 = fn();
f1(15);  //15

2.函数作为参数被传递

var max = 10,
    fn = function(x){
      if(x>max){
        console.log(x);
      }
    };


(function(f){
  var max = 100;
  f(15);
})(fn);      //15 max取10,要注意

要去创建这个函数的作用域取值,而不是“父作用域”。

有些情况下,函数调用完成之后,其执行上下文环境不会接着被销毁。这就是需要理解闭包的核心内容。

使用闭包会增加内容开销,因为有些函数执行完了,但上下文环境并不能释放,依旧保存在执行上下文栈中。

闭包的神奇特性:闭包可以捕获到局部变量和参数的外部函数绑定,即便外部函数的调用已经结束。

var scope = "global scope"; 
function checkScope() {
    var scope = "local scope";
    function f() {
        return scope;
    }
    return f;
}
checkScope()();   //=> "local scope"

闭包的应用场景

//全局变量,test1是全局变量
var test1=111 
function outer(){
    alert(test1);
}
outer(); //111
alert(test1); //111
 
//闭包,test2是局部变量,这是闭包的目的
//我们经常在小范围使用全局变量,这个时候就可以使用闭包来代替。
(function(){
    var test2=222;
    function outer(){
        alert(test2);
    }
    function test(){
        alert("测试闭包:"+test2);
    }
    outer(); //222
    test(); //测试闭包:222
})(); 

alert(test2); //未定义,这里就访问不到test2
for(var i = 0, len = btns.length; i < len; i++) {
    (function(i) {
        btns[i].onclick = function() {
            alert(i);
        }
    }(i))
}

参考原文:王福朋的博客园

Table of Contents