Javascript的作用域

在传统的面向对象程序设计中,作用域分为公用作用域和私有作用域。
公用作用域中的对象属性可以从对象外部访问,即开发者创建对象的实例后,就可使用它的公用属性。
而私有作用域中的属性只能在对象内部访问,即对于外部世界来说,这些属性并不存在。这意味着如果类定义了私有属性和方法,则它的子类也不能访问这些属性和方法。

受保护作用域也是用于定义私有的属性和方法,只是这些属性和方法还能被其子类访问。
ECMAScript中只存在公用作用域。

关于Javascript的作用域要注意的几点:

  • Javascript是使用静态作用域(词法作用域)的语言,他的作用域在函数创建的时候便已经确定。
  • JavaScript中不存在块级作用域,但是在ES6引入了letconst关键字,定义的变量有块级作用域。
  • JavaScript中每个函数作为一个作用域,外部无法访问内部作用域中的变量。
  • JavaScript的作用域链
  • 声明提升

JavaScript函数作用域

1
2
3
4
5
6
7
8
9
function Test(){
var name = 'Pooky';
}
Test();
console.log(name);
// 报错:Uncaught ReferenceError: name is not defined

这里函数中的name变量,外部无法访问。

作用域链

由于JavaScript中的每个函数作为一个作用域,如果出现函数嵌套函数,则就会出现作用域链。

1
2
3
4
5
6
7
8
9
10
11
12
13
sex = 'man';
function Test(){
var name = "Pooky";
function Test2(){
var age = '27';
console.log(sex);
}
return Test2();
}
var act = Test();
act();

Javascript是使用静态作用域的语言,他的作用域在函数创建的时候便已经确定。
这里的函数Test2在函数Test中,这就出现了作用域链,由于作用域链函数创建时就已经创建,

执行时只需按照作用域链去寻找。

上述代码,作用域链:

全局作用域 -> Test函数作用域 -> Test2函数作用域

当执行act()时,其寻找顺序为根据作用域链从内到外的优先级寻找,如果内层没有就逐步向上找,直到没找到抛出异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var name = "Pooky";
function Test2(){
console.log(name);
}
function Test(){
var name = "xiaoming";
return Test2;
}
var act = Test();
act();
// 输出结果: Pooky

上述代码,创建了两条作用域链:

全局作用域 -> Test2函数作用域
全局作用域 -> Test函数作用域

当执行act()时,act实际执行的是Test2函数,而Test2函数的作用域链已经存在:全局作用域 -> Test2函数作用域,所以,执行时会根据已经存在的作用域链去寻找,

所以这里的输出结果是: Pooky。

声明提升

JavaScript有两个阶段:编译阶段和执行阶段。声明操作在编译阶段进行,赋值操作在执行阶段。

JavaScript编译阶段会找到所有的声明,并创建作用域链。

JavaScript中不管是变量声明,还是函数声明,都会被提升,提升就是把变量声明和函数声明从他们代码中出现的位置被移动到执行环境的顶部。

提升操作会优先进行函数的声明,函数会首先被提升然后才是变量。

ES6中letconst 声明的变量不会被提升。

变量声明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function foo() {
console.log(a); //undefined
var a = 10;
console.log(a); //10
}
foo();
\\相当于
function foo() {
var a
console.log(a); //undefined
a = 10;
console.log(a); //10
}
foo();

定义函数有两种方式:函数声明和函数表达式,注意,函数声明提升会在编译阶段把声明和函数体整体都提前到执行环境顶部,但是函数表达式相当于变量声明,

声明操作会被提升到执行环境顶部,并赋值undefined。但是赋值操作会在执行阶段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//函数声明
foo();
function foo() {
console.log("hello"); //hello,这里执行成功是因为foo的声明被提到了执行环境顶部
}
//函数表达式
foo(); // TypeError: baz is not a function
let foo = function() {
console.log("hello");
}
//相当于
let foo;
foo();
foo = function() {
console.log("hello");
}
Donate comment here