JavaScript的闭包究竟是什么?

引言

看以下例子,注意内部函数form是如何引用定义定义在外部函数formFamily内的requiredWho变量的?咱们须要知道JavaScript容许你引用当前函数之外定义的变量javascript

function formFamily (){
    var requiredWho = "Father and Mother ";
    function form (who){
        return requiredWho + "need " + who ;
    }
    return form ("YingBao and MaoTan");
}
formFamily(); // "Father and Mother need YingBao and MaoTan"复制代码

例子演进,这个例子和上面几乎彻底相同,惟一区别是,不是在外部函数formFamily中当即调用f("YingBao and MaoTan"),而是返回form函数自己。所以f的值为内部的函数form,调用f实际是调用form函数。但即便formFamily函数已经返回,form仍能记住requiredWho的值。java

这里咱们须要知道,即便外部函数返回,当前函数仍然能够引用在外部函数所定义的变量。这就意味着,你能够返回一个内部函数,并在稍后调用它。web

function formFamily (){
    var requiredWho = "Father and Mother ";
    function form (who){
        return requiredWho + "need " + who ;
    }
    return form;
}
var f = formFamily(); 
f("YingBao"); // "Father and Mother need YingBao"
f("MaoTan"); // "Father and Mother need MaoTan"
f("YingBao and MaoTan"); // "Father and Mother need YingBao and MaoTan"复制代码

这是如何工做的?编程

在外部函数 formFamily 中定义内部函数 form,而 form 又引用了 formFamily 做用域内的俩个变量requiredWhowho。每当form函数被调用时,其代码都能引用到这俩个变量,由于该闭包存储了这个俩个变量。form函数就是一个闭包。bash

在外部函数 formFamily 以外使用内部 form 函数而且引用外部函数的变量,则造成了闭包。闭包

定义

JavaScript的函数值包含了比调用它们时执行所须要的代码还要多的信息。并且JavaScript函数值会在内部存储它们可能会引用的定义在其封闭做用域的变量。那些在其所涵盖的做用域内跟踪变量的函数被称为闭包。app

tips1:函数能够引用定义在其外部做用域的变量。tips2:闭包比建立它们的函数有更长的生命周期。函数式编程

优化

函数能够引用其做用域内的任何变量,包括参数和外部函数变量。利用这点咱们编写更加通用的formFamily函数。函数

function formFamily (requiredWho){
    function form (who){
        return requiredWho + "need " + who ;
    }
    return form;
}
var f = formFamily("Father "); 
f("YingBao and MaoTan"); // "Father need YingBao and MaoTan"
var m = formFamily("Mother "); 
m("YingBao and MaoTan"); // "Mother need YingBao and MaoTan"复制代码

该例子建立了fm俩个彻底不一样的函数。尽管它们都是由相同的form函数定义的,可是它们时俩个大相径庭的对象。优化

闭包是JavaScript最优雅、最有表现力的特性之一。JavaScript甚至还提供了一种更为方便的构建闭包字面量语法——函数表达式。

function formFamily (requiredWho){
    return function(who){
        return requiredWho + "need " + who ;
    }
}复制代码

请注意,该函数表达式是匿名的。咱们只需其能产生一个新的函数值,没打算在局部调用它,所以根本不必给该函数命名。

扩展

闭包能够更新外部变量的值。实际上,闭包存储的是外部变量的引用,而不是它们的副本。所以,对于任何具备访问这些外部变量的闭包,均可以进行更新。以下例子。

function box(){
    var val = undefined;
    return {
        set: function(newVal) { val = newVal; },
        get: function() { return val; },
        type: function() { return typeof val; }
    };
}
var b = box();
b.type();  // "undefined"
b.set(13.2);
b.get();  // 13.2.6
b.type(); // "number"复制代码

box函数存储了一个可读写的内部值。该例子产生了一个包含三个闭包的对象,这三个闭包是setgettype属性。它们都共享访问val变量。set闭包更新val的值,随后调用gettype查看更新的结果。

知识补充

function outter(){   
    var n = 0; 
    return function (){
        return n++;
    } 
}   

var o1 = outter();  
o1();//n == 0 
o1();//n == 1 
o1();//n == 2 

var o2 = outter();  
o2();//n == 0 
o2();//n == 1复制代码

外部函数outter执行后返回的是一个function,赋值给了 o1o1 能够访问 nreturn 回来的function能够访问里面的变量,就会致使 n 不能被回收,由于有o1有用到它。

匿名函数 function(){return n++;} 中包含对外部函数 outter 的局部变量 n 的引用,所以当外部函数 outter 返回时,n 的值被保留 ( 不会被垃圾回收机制回收 ),持续调用 o1(),将会改变 n 的值。而 o2 的值并不会随着 o1() 被调用而改变,第一次调用 o2 会获得 n==0 的结果,用面向对象的术语来讲,就是 o1o2 为不一样的实例,互不干涉。

须要注意闭包的一个缺陷:闭包会致使内存泄漏。

参考资料

  1. 书籍:《Effective JavaScript 编写高质量JavaScript代码的68个优效方法》
  2. 博客:《JavaScript 中的函数式编程实践》
相关文章
相关标签/搜索