ES6 作用域

什么是作用域?

任何编程语言都有作用域的概念,简单来说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。

JavaScript的作用域是靠函数来形成的,也就是说一个函数的变量在函数外不可以访问。

全局作用域

任何地方都能访问到的对象拥有全局作用域。

  1. 函数外面定义的变量拥有全局作用域
1
2
3
4
5
6
7
8
9
var n = 2;
function fn(){
var a = 1;
return a;
}

console.log(fn()); // 1
console.log(n); // 2
console.log(a); //报错 a is not defined
  1. 未定义直接赋值的变量自动声明为拥有全局作用域
1
2
3
4
5
6
7
8
9
var n = 2;
function fn(){
a = 1;
return a;
}

console.log(fn()); // 1
console.log(n); // 2
console.log(a); // 1
  1. window对象的属性拥有全局作用

局部作用域

局部作用域一般只在固定的代码片段内可访问到,最常见的例如函数内部,所以在一些地方会把这种作用域成为函数作用域。

1
2
3
4
5
6
7
8
9
10
11
var n = 2;
function fn(){
var a = 1;
return a;
}

console.log(fn()); // 1
console.log(n); // 2
console.log(a); //报错 a is not defined

// a是函数内部声明并赋值,拥有局部作用域,只能带函数fn内部使用,在fn外部使用就会报错,这就是局部作用域的特性,外部无法访问。

作用域链

通俗的讲,当声明一个函数时,局部作用域一级一级向上包起来,就是作用域链。

  1. 当执行函数时,总是先从函数内部找寻局部变量。

  2. 如果内部找不到(函数的局部作用域没有),则会向创建函数的作用域(声明函数的作用域)寻找,一次向上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var a = 1;

function fn(){
var a = 10;
function fn1(){
var a = 20;
console.log(a); // 20
}
function fn2(){
console.log(a); // 10
}

fn1();
fn2();
}

fn();
console.log(a) // 1

闭包

提到作用域就不得不提到闭包,简单来讲,闭包外部函数能够读取内部函数的变量。

优点:闭包可以形成独立的空间,永久的保存局部变量。

缺点:保存中间值的状态缺点是容易造成内存泄漏,因为闭包中的局部变量永远不会被回收。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function f1() {
var n = 999;
nAdd = function () {
n += 1;
}
function f2() {
console.log(n);
}
return f2;
}
var result = f1();

result(); // 999
nAdd(); // 执行 n += 1
result(); // 1000

ES6的块级作用域

ES6引入了块级作用域,明确允许在块级作用域中声明函数,let和const命令都涉及块级作用域。块级作用域允许声明函数只在使用大括号的情况下成立,如果未使用大括号,会报错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// ES5 中作用域
var callbacks = []
for (var i = 0; i <= 2; i++) {
callbacks[i] = function() {
return i * 2
}
}

console.table([
callbacks[0](),
callbacks[1](),
callbacks[2](),
])

;((function() {
const foo = function() {
return 1
}
console.log("foo()===1", foo() === 1)
;((function() {
const foo = function() {
return 2
}
console.log("foo()===2", foo() === 2)
})())
})())

// ES6中块级作用域
const callbacks2 = []
for (let j = 0; j <= 2; j++) {
callbacks2[j] = function() {
return j * 2
}
}

console.table([
callbacks2[0](),
callbacks2[1](),
callbacks2[2](),
])

{
function foo() {
return 1
}

console.log("foo()===1", foo() === 1)
{
function foo() {
return 2
}

console.log("foo()===2", foo() === 2)
}
console.log("foo()===1", foo() === 1)
}