【《你不知道的JS(上卷①)》】五、作用域闭包

五、作用域闭包:

​ 闭包不是神奇的魔法,它只是遵循我们前几章一直介绍的 词法作用域书写代码的自然结果。

​ 闭包是由函数以及声明该函数的词法环境组合而成的。该环境包含了这个闭包创建时作用域内的任何局部变量。

一)、回调函数与闭包:

  • 回调函数,将一个函数的引用作为参数传递给另一个函数。
function wait(meesage) {
	setTimeout(function timer() {
		console.log(meesage);
	}, 1000);
}
wait("Hello, closure!")

​ 无论何时何地,如果将函数(访问他们各自的词法作用域)当作第一级的值类型并到处传递,就可以看到闭包的存在。在定时器、事件监听器、Ajax请求、跨窗口通信等异步或同步任务中,只要使用了回调函数,实际上就是在使用闭包。

二)、循环与闭包:

// 使用var
for (var i = 1; i <= 5; i++) {
	setTimeout(function timer() {
		console.log(i);
	}, i * 1000)
} // 6 6 6 6 6 

// 使用let
for (let i = 1; i <= 5; i++) {
	setTimeout(function timer() {
		console.log(i);
	}, i * 1000)
} // 1 2 3 4 5

​ 比较上面两段代码与输出结果。发现使用var在循环中声明变量不能达到我们想要的效果。这是因为尽管每次循环都定义了一个新的函数,但是对于这些函数,是共享同一个作用域的,因此i也是相同的。而使用let,会在每次迭代时都创建一个新的作用域。并且使用let会让i这个变量在每次迭代都会声明,且会使用上一个迭代结束时的值来初始化这个变量。

三)、模块模式:

​ 在许多面向对象语言(如Java)中,都支持将方法声明为私有,即只能被同一个类中的方法调用。在JavaScript中并没有这种原生支持。我们可以使用闭包来模拟私有方法,这种方式也被称作 模块模式

​ 这种方式不仅有利于限制对代码的访问:还提供了管理全局命名空间的强大能力,避免非核心的方法弄乱代码的公共接口部分。

var foo = (function CoolModule() {
  var something = "cool";
  var another = [1, 2, 3];

  function doSomething() {
    console.log(something);
  }

  function doAnother() {
    console.log(another.join("!"));
  }

  return {
    doSomething: doSomething,
    doAnother: doAnother
  }

})();

foo.doSomething(); // cool
foo.doAnother(); // 1!2!3

ES6中的模块机制:

​ ES6中为模块增加了一级语法支持。但通过模块系统进行加载时,ES
6会将文件当作独立的模块来处理。每个模块都可以导入其他模块或特定的API成员,同样也可以到处自己的API成员。

  • import可以将一个模块中的一个或多个API导入到当前作用域中,并分别绑定在一个变量上。
  • module会将整个模块的API导入并绑定到一个变量上。
  • export会将当前模块的一个标识符(变量、函数)导出为公共API。

四)、小结:

  • 闭包虽然随处可见,但是不要滥用。使用闭包在处理速度和内存消耗方面对脚本性能具有负面影响。
  • 理解闭包的关键还是前几章讲述的词法作用域,因此不能跳过前几章!
posted @ 2020-06-10 23:22  macguz  阅读(183)  评论(0编辑  收藏  举报