Table of Contents generated with DocToc (opens new window)
# 词法作用域
全局变量会自动成为全局对象(比如浏览器中的window对象)的属性,因此可以间接地通过全局对象属性的引用来对其进行访问
var a = 10;
a // 10
window.a //10
2
3
无论函数在哪里被调用,也无论它如何呗调用,它的词法作用域都只由函数被声明时所处的位置决定.
# 关于 const 和 let 声明的变量不在 window 上
在ES5中,顶层对象的属性和全局变量是等价的,var 命令和 function 命令声明的全局变量,自然也是顶层对象。
var a = 12;
function f(){};
console.log(window.a); // 12
console.log(window.f); // f(){}
2
3
4
5
但ES6规定,var 命令和 function 命令声明的全局变量,依旧是顶层对象的属性,但 let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。 class、let定义的即便是全局对象,但不是顶层对象的属性,在window自然获取不到,只能去定义变量的块级作用域里获取
# 闭包
阮一峰-学习JavaScript闭包 (opens new window)
什么是闭包?
函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起构成闭包(closure)。也就是说,闭包可以让你从内部函数访问外部函数作用域。在 JavaScript 中,每当函数被创建,就会在函数生成时生成闭包。
简单来说,闭包是由函数和其他与其相关的引用环境组成的包裹体,使得函数能够在其定义的词法作用域外访问变量。
闭包的特性:
- 函数嵌套:在一个函数内部定义了另一个函数
- 内部函数引用外部函数的变量:内部函数可以访问外部函数的变量,即使外部函数已经执行完毕。
- 外部函数返回内部函数:通常外部函数会返回内部函数,使得外部作用域的变量仍然可以被内部函数访问。
function getDate() {
let date = 129;
function getToday() {
console.log("today is" + date);
}
return getToday();
}
getDate();
// getToday就是闭包
2
3
4
5
6
7
8
9
10
Chrome debug 工具中可以查看闭包
使用闭包的注意点

# 闭包为什么会造成内存泄漏
先看一段代码
function printNumber() {
let num = 0;
debugger;
return function () {
for (let i = 0; i < 10; i++) {
num++;
}
debugger;
console.log(num);
};
}
var printer = printNumber();
debugger;
printer();
debugger;
printer();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
执行结果是10、20
这里产生闭包的原因是printNumber返回了一个函数printer,printer内引用了num,而在两次printer之间闭包(即printer)还存在,num被保存了;
最后当整个程序执行完毕后,gc回收机制执行,闭包被回收,内存被释放。
# 关于JavaScript的GC回收机制
JavaScript 引擎基础 GC 方案是(simple GC):mark and sweep(标记清除),即:
- 遍历所有可访问的对象。
- 回收已不可访问的对象。
浏览器的 Javascript 具有自动垃圾回收机制(GC:Garbage Collecation),也就是说,执行环境会负责管理代码执行过程中使用的内存。其原理是:垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。但是这个过程不是实时的,因为其开销比较大并且GC时停止响应其他操作,所以垃圾回收器会按照固定的时间间隔周期性的执行。
# 闭包的用途
1.数据缓存
看以下一个题目
var data = [];
for (var i = 0; i < 3; i++) {
data[i] = function () {
console.log(i);
};
}
data[0]();
data[1]();
data[2]();
2
3
4
5
6
7
8
9
10
11
答案是都是3
很明显,闭包缓存了状态
# Vue的闭包
当你在浏览器中调试 Vue 组件时,可能会看到类似下面的闭包特征:
// Vue 组件的例子
function VueComponent() {
let message = 'Hello, Vue!';
function updateMessage(newMessage) {
message = newMessage;
}
return {
message,
updateMessage
};
}
const instance = VueComponent();
console.log(instance.message); // 'Hello, Vue!'
instance.updateMessage('New message');
console.log(instance.message); // 'New message'
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18