JavaScript · 有关闭包

javascript/jquery

浏览数:64

2019-9-7

概念:什么是闭包(Closure)

  • MDN:闭包是函数和声明该函数的++词法环境++的组合。
    A closure is the combination of a function and the lexical environment within which that function was declared.
  • 百度百科1:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数)。
  • 百度百科2:闭包就是能够读取其他函数内部变量的函数,是“定义在一个函数内部的函数”。
  • 其他说法1:闭包是词法闭包的的简称,是引用了++自由变量++的函数。
  • 其他说法2:闭包就是通过返回一个函数来保留某段作用域的一种方法,通过返回函数把本该消失的作用域保留到这个函数中。

可见:闭包并没有一个明确的、易于理解的概念,泛泛来看,闭包本质上是函数及作用域的一类问题。而实际上,我们也不太需要关注闭包的概念是什么,只需知道其表现、原理及常见的应用场景即可。

自由变量与作用域

  • 作用域分:全局作用域、函数作用域和块作用域(ES6后);
  • 作用域是在函数定义时确定的,而不是在函数执行时确定的;
  • 在A作用域中使用的变量X,却没有在A中声明,那么对于A作用域来说,变量X即自由变量;
  • 按照作用域链向上查找自由变量时,要先到创建该函数的作用域中去查找,而不是调用该函数的作用域中去查找;
var a = 10;
function sum(x) {
    return x + a;
}
sum(1);
// 11
// 在函数sum的作用域里,a就是自由变量
var a = 100;
function fn() {
    var a = 10;
    return function(x) {
        return a + x;
    }
}
var f = fn();
a = 200;
f(1);
// 11
// 自由变量a的值是沿着作用域链向上一级一级找到的
var x = 10;
function fn() {
    console.log(x);
}
function show(f) {
    var x = 20;
    (function() {
        f();
    })()
}
show(fn);
// 10
// 自由变量x在函数fn中执行,而fn在全局中定义
// 按上文总结:x的值要到全局中(定义上下文)查找,而不是到函数show的作用域(执行上下文)中查找
var x = 10;
function show(y) {
    var x = 20;
    (function(z) {
        console.log(x+z)
    })(y)
}
show(30);
// 30 + 20 = 50

闭包的表现、原因及其应用

  • 闭包的两种表现:
// 函数作为返回值
function fn() {
    var max = 10;
    return function bar(x) {
        if (x > max) {
            console.log(x);
        }
    };
}
var f = fn();
f(20); 
// 20
// 函数作为参数
var max = 10;
function bar(x) {
    if (x > max) {
        console.log(x);
    }
}
(function(f) {
    var max = 100;
    f(15);
})(bar)
// 15
  • 闭包产生的原因:

正常来讲,一个函数fn执行完成后,其上下文环境会销毁。但存在一种意外:
fn执行完后返回了另一个函数f,而正巧,返回的函数体中存在一个属于fn上下文环境的自由变量,那么fn执行完后,其上下文环境不能销毁。
很显然,闭包会增加内容开销。

  • 闭包的实际应用:

首先需要知道每次调用外部函数,都会返回一个新的函数;而且返回函数的执行互不影响。

function lazy_sum(arr) {
    return function() {
        return arr.reduce((x,y) => {
            return x+y;
        })
    }
}
var f1 = lazy_sum([1,2,3,4,5]);
var f2 = lazy_sum([1,2,3,4,5]);
f1 === f2; // false

以上便是闭包的一种应用:返回一个函数,并延迟执行。

闭包更强大的一个功能是:模拟面向对象的程序设计语言,封装一个private变量

// 使用js创建一个开放的计数器
function create_counter(initial) {
    var x = initial || 0;
    return {
        inc: function() {
            return ++x;
        }
    }
}
var c1 = create_counter();
var c2 = create_counter(10);
c1.inc(); // 1
c1.inc(); // 2
c2.inc(); // 11
c2.inc(); // 12
c1.inc(); // 3
c2.inc(); // 13

闭包还可以把一个多参数的函数转化成单参数的函数:

function make_pow(n) {
    return function (x) {
        return Math.pow(x, n);
    }
}
var pow2 = make_pow(2);
var pow3 = make_pow(3);
console.log(pow2(5)); // 5^2=25
console.log(pow3(7)); // 7^3=343

文章大部分内容引自:

如有侵犯您的权益,请留言,我会第一时间删除

作者:時雨