# 作用域

作用域指一个变量的作用的范围

在JS中一共有两种作用域:

  • 全局作用域
  • 函数作用域

# 1、全局作用域

直接编写在script标签中的JS代码,都在全局作用域

全局作用域在页面打开时创建,在页面关闭时销毁

在全局作用域中有一个全局对象window,它代表的是一个浏览器的窗口,由浏览器创建,可以直接使用

在全局作用域中:

  • 创建的变量都会作为window对象的属性保存
  • 创建的函数都会作为window对象的方法保存

全局作用域中的变量都是全局变量,在页面的任意的部分都可以访问的到

var a = 3;
console.log(window.a); //3
console.log(a); //3

b = 3;
console.log(b); //3
1
2
3
4
5
6

# 1.1、变量的声明提前

使用var关键字声明的变量,会在所有的代码执行之前被声明

但是如果声明变量时不适用var关键字,则变量不会被声明提前

// 1、变量的声明提前
console.log("a = " + a); // a = undefined
var a = "abc";
// ======相当于======
var a;
console.log("a = " + a); // a = undefined
a = "abc";

// 2、没有变量的声明提前,报错
console.log("b = " + b); // UncaughtReferenceError: b is not defined
b = "abc";
// ======相当于======
console.log("b = " + b); // UncaughtReferenceError: b is not defined
window.b = "abc";
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 1.2、函数的声明提前

使用函数声明形式创建的函数function

函数(){
	语句...
}
1
2
3

它会在所有的代码执行之前就被创建,所以我们可以在函数声明前来调用函数

fun1(); // fun1...
fun2(); // UncaughtTypeError: fun2 is not a function
// 函数声明,会被提前创建
function fun1(){
    console.log("fun1...");
}
// 函数表达式,不会被提前创建(变量会被提前声明,但函数不会被提前创建)
var fun2 = function(){
    console.log("fun2...");
}
1
2
3
4
5
6
7
8
9
10

# 2、函数作用域

调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁

每调用一次函数就会创建一个新的函数作用域,他们之间是互相独立的

  • 在函数作用域中可以访问到全局作用域的变量
  • 在全局作用域中无法访问到函数作用域的变量

当在函数作用域操作一个变量时,它会先在自身作用域中寻找,

  • 如果有就直接使用
  • 如果没有则向上一级作用域中寻找,直到找到全局作用域
  • 如果全局作用域中依然没有找到,则会报错

在函数中要访问全局变量可以使用window对象

var a = 10;
function fun2(){
    var a = 20;

    function fun3(){
        var a = 30;
        console.log("fun3 ==> a = " + a);  // fun3 ==> a = 30
    }

    fun3();

    console.log("fun2 ==>a = " + a); // fun2 ==>a = 20
    console.log("a = " + window.a); // a = 10
}
fun2(); 
console.log("a = " + a); // a = 10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

在函数作用域也有声明提前的特性,使用var关键字声明的变量,会在函数中所有的代码执行之前被声明

函数声明也会在函数中所有的代码执行之前执行

// 在函数作用域也有声明提前的特性,使用`var`关键字声明的变量,会在函数中所有的代码执行之前被声明
function func1(){
    console.log(a);
    var a = "func1";

    // 函数声明也会在函数中所有的代码执行之前执行
    func2(); // fun2...
    function func2(){
        console.log("fun2...");
    }
}
func1(); // undefined
1
2
3
4
5
6
7
8
9
10
11
12

在函数中,不适用var声明的变量都会成为全局变量

// 函数声明且调用
func3();
function func3() {
    a = 4;
}
console.log("a = " + window.a);  // a = 4
console.log("a = " + window["a"]);   // a = 4
console.log("a = " + a);    // a = 4
// 函数声明不调用
function func4() {
    b = 4;
}
console.log("b = " + window.b);  // b = 4
console.log("b = " + window["b"]);   // b = 4
console.log("b = " + b);    // UncaughtReferenceError: b is not defined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

定义形参就相当于在函数作用域中声明了变量

var e = 10;
function fun5(e){
    console.log(e);
}
fun5(); // undefined
fun5(55);  // 55
1
2
3
4
5
6

练习

// 说出以下代码的执行结果
var a = 123; 
function fun(){
    console.log(a);
}
fun(); 			// 123
// =====================
var a = 123; 
function fun(){
    console.log(a);
    var a = 456;
}
fun(); 			// undefined
console.log(a);  // 123
// =====================
var a = 123; 
function fun(){
    console.log(a);
    a = 456;
}
fun(); 			// 123
console.log(a);  // 456
// =====================
var a = 123; 
function fun(a){
    console.log(a);
    a = 456;
}
fun();			// undefined
console.log(a);  // 123
// =====================
var a = 123; 
function fun(a){
    console.log(a);
    a = 456;
}
fun(789);		// 789
console.log(a);  // 123
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

# 3、this

解析器在调用函数每次都会向函数内部传递进一个隐含的参数,这个隐含的参数就是this

this指向的是一个对象,这个对象我们称为函数执行的上下文对象

根据函数的调用方式的不同,this会指向不同的对象

  • 以函数的形式调用时,this永远都是window
  • 以方法的形式调用时,this就是调用方法的那个对象
// - 以函数的形式调用时,`this`永远都是`window`
function fun(){
    console.log(this.name);
}
var name = "ddd"; // ddd
fun();
// - 以方法的形式调用时,`this`就是调用方法的那个对象
var obj = {
    name: "孙悟空",
    sayName: fun
}
obj.sayName(); // 孙悟空
1
2
3
4
5
6
7
8
9
10
11
12