菜单

JavaScript中闭包的详解

2019年8月10日 - 计算机教程

但是,如果不需要立刻求和,而是在后面的代码中,根据需要再计算怎么办?可以不返回求和的结果,而是返回求和的函数!

我们可能会简单的以为控制台会打印出 0 1 2 3,可事实却打印出了 4 4 4
4,这又是为什么呢?我们发现,setTimeout
函数时异步的,等到函数执行时,for循环已经结束了,此时的 i 的值为 4,所以
function() { console.log(i) } 去找变量 i,只能拿到 4。

function sum(arr) {
  return arr.reduce(function (x, y) {
    return x + y;
  });
}

sum([1, 2, 3, 4, 5]); // 15

我们想起上一个例子中,闭包使 a
变量的值被保存起来了,那么这里我们也可以用闭包把 0 1 2 3 保存起来。

它用起来像这样:

function func() {
  var test = document.getElementById('test');
  test.onclick = funcTest;
}
function funcTest(){
  console.log('hello world');
}

注意到返回的函数在其定义内部引用了局部变量arr,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用,所以,闭包用起来简单,实现起来可不容易。

闭包还可以把多参数的函数变成单参数的函数。例如,要计算xy可以用Math.pow(x, y)函数,不过考虑到经常计算x2或x3,我们可以利用闭包创建新的函数pow2pow3

全局变量,生命周期是永久的。局部变量,当定义该变量的函数调用结束时,该变量就会被垃圾回收机制回收而销毁。再次调用该函数时又会重新定义了一个新变量。

function (x) { return x * x } (3);

在闭包中调用局部变量,会导致这个局部变量无法及时被销毁,相当于全局变量一样会一直占用着内存。如果需要回收这些变量占用的内存,可以手动将变量设置为null。

理论上讲,创建一个匿名函数并立刻执行可以这么写:

function func() {
  var test = document.getElementById('test');
  test.onclick = function () {
    console.log('hello world');
  }
}
f(); // 15

好像有点清晰了,但是使用在它外面定义的变量是什么意思,我们先来看看变量作用域。

说了这么多,难道闭包就是为了返回一个函数然后延迟执行吗?

可以看出,在第一次调用完 func2 之后,func 中的变量 a 变成 ‘linxin
a’,而没有被销毁。因为此时 func1 形成了一个闭包,导致了 a
的生命周期延续了。

很久很久以前,有个叫阿隆佐·邱奇的帅哥,发现只需要用函数,就可以用计算机实现运算,而不需要0、1、2、3这些数字和+、-、*、/这些符号。

var func = function(){
  var a = 'linxin';
  console.log(a);     // linxin
}
func();
console.log(a);       // Uncaught ReferenceError: a is not defined

注意这里用了一个“创建一个匿名函数并立刻执行”的语法:

变量可分为全局变量和局部变量。全局变量的作用域就是全局性的,在 js
的任何地方都可以使用全局变量。在函数中使用 var
关键字声明变量,这时的变量即是局部变量,它的作用域只在声明该变量的函数内,在函数外面是访问不到该变量的。

您可能感兴趣的文章:

下面我们通过一个简单而又经典的例子来进一步熟悉闭包。

请再注意一点,当我们调用lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数:

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持脚本之家!

function make_pow(n) {
  return function (x) {
    return Math.pow(x, n);
  }
}

// 创建两个新函数:
var pow2 = make_pow(2);
var pow3 = make_pow(3);

pow2(5); // 25
pow3(7); // 343

在 JavaScript 中,闭包是一个让人很难弄懂的概念。ECMAScript
中给闭包的定义是:闭包,指的是词法表示包括不被计算的变量的函数,也就是说,函数可以使用函数之外定义的变量。

我们来实现一个对Array的求和。通常情况下,求和的函数是这样定义的:

var func = function(){
  var a = 'linxin';
  var func1 = function(){
    a += ' a';
    console.log(a);
  }
  return func1;
}
var func2 = func();
func2();          // linxin a
func2();          // linxin a a
func2();          // linxin a a a

另一个需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了f()才执行。我们来看一个例子:

for (var i = 0; i < 4; i++) {
  setTimeout(function () {
    console.log(i)
  }, 0)
}
var c1 = create_counter();
c1.inc(); // 1
c1.inc(); // 2
c1.inc(); // 3

var c2 = create_counter(10);
c2.inc(); // 11
c2.inc(); // 12
c2.inc(); // 13

这下子闭包就比较明朗了。

返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

您可能感兴趣的文章:

如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:

是不是看完这个定义感觉更加懵逼了?别急,我们来分析一下。

当我们调用lazy_sum()时,返回的并不是求和结果,而是求和函数:

内存管理

调用函数f时,才真正计算求和的结果:

a 为局部变量,在 func 调用完之后,a 就会被销毁了。

在返回的对象中,实现了一个闭包,该闭包携带了局部变量x,并且,从外部代码根本无法访问到变量x。换句话说,闭包就是携带状态的函数,并且它的状态可以完全对外隐藏起来。

作用域相对比较简单,我们不多讲,来看看跟闭包关系比较大的变量生存周期。

函数作为返回值

for (var i = 0; i < 4; i++) {
  (function (i) {
    setTimeout(function () {
      console.log(i)
    }, 0)
  })(i)
}
f1(); // 16
f2(); // 16
f3(); // 16

变量生存周期

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图