1. 什么是闭包
MDN定义:Closures are functions that refer to independent (free) variables (variables that are used locally, but defined in an enclosing scope). In other words, these functions 'remember' the environment in which they were created.
You Don't Know JS: Closure is when a function is able to remember and access its lexical scope even when that function is executing outside its lexical scope.
我所理解的闭包就是,即使外部函数已经运行完毕,内部函数仍能访问外部函数的作用域中的变量。
抓重点: 函数, 作用域。
2. 闭包的运行机制
2.1 词法作用域查找规则
在闭包的使用中,为什么我们能够通过闭包访问外部函数的作用域中的变量?其一,词法作用域的查找规则是“冒泡”的,即向外层一层层查找,直到全局作用域,所以能够访问外部函数的作用域。其二,函数的作用域是定义时所在的作用域,而不是运行时的作用域。
function foo() {
var a = 1;
function bar() {
console.log(a);
}
return bar;
}
var a = 2;
var baz = foo();
baz(); //1
在上面的代码中,由于bar定义在foo的内部,因此能够向外“冒泡”访问foo的作用域。当运行baz时,a的值为1而不是2,也说明了函数的作用域是定义时的作用域,是静态的。
2.2 垃圾回收 + 引用
当函数执行完毕后,引擎的垃圾回收机制会释放不再使用的内存空间。因此,当外部函数执行完毕时,外部函数的内部作用域理应是该被销毁的。然而,由于闭包存在对外部函数作用域的引用,因此此作用域仍然存在,所以内部函数仍能在外部函数执行结束之后访问外部函数定义的变量,此之为“记住”。
3. 闭包的应用场景
3.1 私有变量 + 模块
需求:只能通过函数提供的方法访问函数内部的变量——隐藏。只能内部访问——私有。
function bookInfo() {
var book = {
name: "You Don't know JS",
price: 66
};
function getPrice() {
console.log(book.price);
};
function getName() {
console.log(book.name);
};
function setPrice(price) {
book.price = price;
};
return {
getPrice,
getName,
setPrice
};
};
var book = bookInfo();
book.getPrice(); //66
book.getName(); //"You Don't know JS"
book.setPrice(100);
book.getPrice(); //100
在以上的代码中,bookInfo
通过返回一个对象,该对象的值是对内部函数的引用,而不是对变量的引用。因此,实现了函数内部变量是隐藏的(只能通过返回的对象方法访问)且私有的(只有函数内部才能访问)。
在模块中,返回的变量就被称为模块的公共API
,模块内部的变量只能通过这些方法去使用。
3.2 偏函数应用
需求:函数需要先接受一些参数,随后再接受另一些参数的时候。
比如,当我计算商品的总价格时,我想先设定商品的单价,随后根据购买数量算出总的商品价格。
function partialApply(fn, ...fixedArgs) {
return function (...remainingArgs) {
return fn.apply(this, fixedArgs.concat(remainingArgs));
}
}
function calTotalPrices(price, count) {
console.log(price * count);
}
var pay = partialApply(calPrice, 10);
pay(5);
在上面的代码中,pay
就是在partialApply
的外部访问了partialApply
的内部变量(函数参数)。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。