Effective JavaScript Item 11 掌握闭包

本系列作为Effective JavaScript的读书笔记。

掌握闭包,需要知道以下几个关键点:

  1. JavaScript允许在当前的function中访问该function外部的变量。
function makeSandwich() {
	var magicIngredient = "peanut butter";
	function make(filling) {
		return magicIngredient + " and " + filling;
	}
	return make("jelly");
}
makeSandwich(); // "peanut butter and jelly"

以上的make function访问了外部的magicIngredient变量。

  1. 一个function能够访问该function外部的变量,即使该外部的function已经返回了。听起来有些不可思议,但是别忘了在JavaScript中,function是first-class
    object(参见Item 19)。
function sandwichMaker() {
	var magicIngredient = "peanut butter";
	function make(filling) {
		return magicIngredient + " and " + filling;
	}
	return make;
}
var f = sandwichMaker();
f("jelly"); // "peanut butter and jelly"

以上的代码是如何工作的:

实际上,JavaScript中的function不仅仅只记录了需要执行的代码的信息,还保存了所有它引用到的变量的信息,这实际上就是闭包的概念。

那么以上的makefunction就是一个闭包,它保存了magicIngredient以及filling这两个变量的信息。

因此,可以利用这一点来声明更加general-purpose的function:

function sandwichMaker(magicIngredient) {
	function make(filling) {
		return magicIngredient + " and " + filling;
	}
	return make;
}
var hamAnd = sandwichMaker("ham");
hamAnd("cheese"); // "ham and cheese"
hamAnd("mustard"); // "ham and mustard"

闭包是JavaScript中最显著和优雅的特性,JavaScript甚至提供了一个更简洁的方式来创建闭包,叫做Function
Expression:

function sandwichMaker(magicIngredient) {
	return function(filling) {
		return magicIngredient + " and " + filling;
	};
}

注意到以上的functionexpression是匿名的,因为这里需要的只是返回一个function(也就是闭包),用来引用一些信息。当然,function
expression是可以有名字的,在Item 14中会进行介绍。

  1. 闭包能够更新外部值。实际上闭包只是保存了外部值的一个引用,而不是拷贝了它们的值。

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(98.6);
b.get(); // 98.6
b.type(); // "number"

以上的box函数返回了一个对象,其中含有三个闭包,每个闭包都引用到了其外部的val变量,其中set闭包也能够对val变量进行修改。

总结:

  1. 函数能够访问到其外部的变量。
  2. 闭包在创建它们的函数返回之后,还能够被访问到。(Closures can outlive the function that creates them.)
  3. 闭包内部会存储所使用的外部变量的引用,并且能够获取和修改它们的值。
时间: 2024-10-08 18:45:07

Effective JavaScript Item 11 掌握闭包的相关文章

Effective JavaScript Item 27 使用闭包而不是字符串来封装代码

本系列作为Effective JavaScript的读书笔记. 对于代码封装,在JavaScript中有两种方式可以办到.第一种就是使用function,第二种则是利用eval()函数,传入到该函数的字符串参数可以是一段代码. 当对使用哪种方式犹豫不决时,使用function.因为使用字符串的一个重要缺点是,传入的字符串并不是一个闭包,而function则可以代表一个闭包.关于闭包的特点,在Item 11中进行了描述. 下面是一段使用字符串来封装代码的例子: function repeat(n,

Effective JavaScript Item 35 使用闭包来保存私有数据

本系列作为EffectiveJavaScript的读书笔记. JavaScript的对象系统从其语法上而言并不鼓舞使用信息隐藏(Information Hiding).由于当使用诸如this.name.this.passwordHash的时候,这些属性默认的訪问级别就是public的.在不论什么位置都可以通过obj.name,obj.passwordHash来对这些属性进行訪问. 在ES5环境中,也提供了一些方法来更方便的訪问一个对象上全部的属性,比方Object.keys(),Object.g

Effective C++ Item 11 在operator= 中处理“自我赋值”

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:确保当对象自我赋值时operator=有良好行为.其中技术包括比较"来源对象"和"目标对象"的地址.精心周到的语句顺序.以及copy-and-swap. 示例:没有"证同测试" #include <iostream> #include <string> using namespace std; class Bi

Effective JavaScript Item 13 使用即时调用的函数表达式(IIFE)来创建局部域

本系列作为Effective JavaScript的读书笔记. 所谓的即时调用的函数表达式,这个翻译也许不太准确,它对应的英文原文是Immediately Invoked Function Expression (IIFE).下文也使用IIFE来表达这一概念. 首先看一个程序: function wrapElements(a) { var result = [], i, n; for (i = 0, n = a.length; i < n; i++) { result[i] = function

Effective JavaScript Item 62 在异步调用中使用嵌套或者命名的回调函数

在一开始,理解异步程序的调用顺序会有些困难.比如,下面的程序中,starting会先被打印出来,然后才是finished: downloadAsync("file.txt", function(file) { console.log("finished"); }); console.log("starting"); downloadAsync方法在执行之后会立即返回,它只是为下载这个行为注册了一个回调函数而已. 由于JavaScript"

Effective JavaScript Item 50 优先使用遍历方法而非循环

优先使用遍历方法而非循环 在使用循环的时候,很容易违反DRY(Don't Repeat Yourself)原则.这是因为我们通常会选择复制粘贴的方法来避免手写一段段的循环语句.但是这样做回让代码中出现大量重复代码,开发人员也在没有意义地"重复造轮子".更重要的是,在复制粘贴的时候很容易忽视循环中的那些细节,比如起始索引值,终止判断条件等. 比如以下的for循环就存在这个问题,假设n是集合对象的长度: for (var i = 0; i <= n; i++) { ... } //

Effective JavaScript Item 37 认识this的隐式指向

本系列作为Effective JavaScript的读书笔记. CSV数据通常都会被某种分隔符进行分隔,所以在实现CSV Reader时,需要支持不同的分隔符.那么,很自然的一种实现就是将分隔符作为构造函数的参数. function CSVReader(separators) { this.separators = separators || [","]; this.regexp = new RegExp(this.separators.map(function(sep) { retu

Effective JavaScript Item 24 使用一个变量来保存arguments的引用

本系列作为Effective JavaScript的读书笔记. 假设需要一个API用来遍历若干元素,像下面这样: var it = values(1, 4, 1, 4, 2, 1, 3, 5, 6); it.next(); // 1 it.next(); // 4 it.next(); // 1 相应的实现可以是: function values() { var i = 0, n = arguments.length; return { hasNext: function() { return

Effective JavaScript Item 25 使用bind方法来得到一个固定了this指向的方法

本系列作为Effective JavaScript的读书笔记. 当需要将方法抽取出来作为回调函数使用的时候,常常会因为this的指向不明而发生错误,比如: var buffer = { entries: [], add: function(s) { this.entries.push(s); }, concat: function() { return this.entries.join(""); } }; 如果想利用其中的add作为回调函数对一组数据进行添加: var source