作用域
通过 var 创建的变量只有函数作用域,而通过 let 和 const 创建的变量既有函数作用域,也有块作用域。
嵌套作用域
闭包就是内部函数,我们可以通过在一个函数内部或者 {} 块里面定义一个函数来创建闭包。
外部函数作用域
内部函数可以访问外部函数中定义的变量,即使外部函数已经执行完毕
内部函数还可以访问外部函数中定义的形参
外部块作用域
内部函数可以访问外部块中定义的变量
词法作用域
内部函数在定义的时候就决定了其外部作用域
闭包的外部作用域是在其定义的时候已决定,而不是执行的时候。
因为是(词法)静态作用域链,不是动态的
作用域链
当我们使用一个变量的时候,Javascript引擎 会通过变量名在当前作用域查找,若没有查找到,会一直沿着作用域链一直向上查找,直到 global 全局作用域。
外部作用域执行完毕后
当外部作用域执行完毕后,内部函数还存活(仍在其他地方被引用)时,闭包才真正发挥其作用。譬如以下几种情况:
- 在异步任务例如 timer 定时器,事件处理,Ajax 请求中被作为回调
- 被外部函数作为返回结果返回,或者返回结果对象中引用该内部函数
除了 timer 定时器,事件处理,Ajax 请求等比较常见的异步任务,还有其他的一些异步 API 比如 HTML5 Geolocation,WebSockets , requestAnimationFrame()也将使用到闭包的这一特性。
变量的生命周期取决于闭包的生命周期。被闭包引用的外部作用域中的变量将一直存活直到闭包函数被销毁。如果一个变量被多个闭包所引用,那么直到所有的闭包被垃圾回收后,该变量才会被销毁。
闭包与循环
闭包只存储外部变量的引用,而不会拷贝这些外部变量的值
闭包与封装性
封装性意味着信息隐藏。
函数与私有状态
通过闭包,我们可以创建拥有私有状态的函数,闭包使得状态被封装起来。
工厂模式与私有原型对象
原型创建对象的常规方式:
object.create()
这里,Todo() 就是一个拥有私有状态的函数。
工厂模式与私有构造函数
翻译功能与私有map
自增生成器函数
对象与私有状态
这个例子中,add() 和 get() 函数是闭包,而 isPriorityTodo() 和 toTodoViewModel() 则是纯函数。
闭包 vs 纯函数
- 闭包是那些引用了外部作用域中变量的函数。
- 纯函数是那些没有引用外部作用域中变量的函数,它们通常返回一个值并且没有副作用。
闭包在函数式编程中的应用
装饰器函数也使用了闭包的特性。
垃圾回收
在 Javascript 中,局部变量会随着函数的执行完毕而被销毁,除非还有指向他们的引用。当闭包本身也被垃圾回收之后,这些闭包中的私有状态随后也会被垃圾回收。通常我们可以通过切断闭包的引用来达到这一目的。比如给它的值设为null,就清空了。
避免全局变量
1.为了避免将所有的工厂函数都放在全局作用域下,最简单的方法就是将他们挂在 app 全局变量下
2.可以通过传参的方式访问工厂函数
loader 对象
function Loader(){
let modules = Object.create(null);// 这里不用{}新建空对象而用Object.create(null),是因为Object.create(null)返回的是一个没有原型没有继承关系的空对象
let started = false;
function getNamespaceModule(modulesText){
// 一个闭包
let parent = modules;
if(modulesText){
let parts = modulesText.split('.');
for(let i=0; i<parts.length; i++){
let part = parts[i];
if (typeof parent[part] === "undefined") {
parent[part] = Object.create(null);
}
parent = parent[part];
}
}
return parent;
}
//
function addFunction(namespace, fn){
if(typeof(fn) !== "function") {
throw "Only functions can be added";
}
let module = getNamespaceModule(namespace);
let fnName = fn.name;
module[fnName] = fn;
}
//
function addNamespace(namespace){
return function(fn){
addFunction(namespace, fn)
}
}
//
function factory(){
if(typeof(arguments[0]) === "string"){
return addNamespace(arguments[0]);
} else {
return addFunction(null, arguments[0]);
}
}
function start(startApplication){
if(started){
throw "App can be started only once";
}
startApplication(Object.freeze(modules));
started = true;
}
return Object.freeze({
factory,
start
});
};
let app = Loader();
复制代码
其实以上代码,我看的不是很懂。。。。。。感觉似乎执行不下去的。。。调用了 Loader()后, Loader内部的getNamespaceModule()没有调用的地方,,,所以感觉没法走下去。。。有人理解这里面吗?
Object.create(null) 和 {}的区别:
总结
闭包是一个可以访问外部作用域中变量的内部函数。
这些被引用的变量直到闭包被销毁时才会被销毁。
闭包使得 timer 定时器,事件处理,AJAX 请求等异步任务更加容易。
可以通过闭包来达到封装性。
例子
以下也是一个闭包,函数返回函数的例子:
原文链接:
blog.leapoahead.com/2015/09/15/…
如有侵权,请联系作者删除