《JavaScript高级程序设计》总结(六)—— 作用域和变量提升

551 阅读5分钟

函数变量的作用域

就是变量声明的区域,就是变量和函数的可访问范围

全局变量

在全局作用域中声明的变量叫做全局变量,全局作用域的变量可以在js中任何地方调用 ,变量没有在函数内声明或者声明的时候没有带var就是全局变量,拥有全局作用域。特殊:var a = b = c = 0; b与c是全局变量。

局部变量

如,在函数内部声明的变量只能在函数内部访问

块级作用域

在javascript中,函数里面定义的变量,可以在函数里面被访问,但在函数外无法访问。而由花括号封闭的代码块都有自己的作用域,因而支持根据条件来定义变量,变量在执行完毕后会被销毁,注意在es6之前的版本,并没有块级作用域,只有函数作用域和全局作用域,for循环的循环体中是函数作用域,for循环内部定义的变量在整个所在的函数内部是可以访问的。

举个栗子

    for(var i =0;i<10;i++){
            //console.log(i)
        }  
        console.log(i) //这里i打印为10,i的作用域是全局作用域

我们来举个复杂一点的函数来分析下其中变量的作用域

        var t = 9; 
        function f1() { 
            var t2 = 10; 
            console.log(t);
            console.log(t3)
            function f2() { 
                var t3 = 200; 
                console.log(t2); 
                return t2 * t2;
            }
            return  f2()
            
        }
        f1();
  • 1.变量t的作用域为全局作用域,任何地方都能访问到它
  • 2.函数f1的作用域为全局作用域,任何地方都能访问到它
  • 3.在函数f1的内部,变量t2的作用域为函数作用域f1,在函数f1中的任何地方都可以访问到它
  • 4.函数f2的作用域为函数作用域f1,在函数f1中的任何地方都可以访问到它
  • 5.在函数f2中,变量t3的作用域为函数作用域f2,在函数f2中的任何地方都可以访问到它

这样就形成了一个作用域链f2.Scope ===> f1.Scope ===> global.Scope

函数在执行的时候会沿着最末端的作用域依次向上查找变量,因此,子域可以访问父域的变量,而父域不可以访问子域的

上述代码的执行结果为

        var t = 9; 
        function f1() { 
            var t2 = 10; 
            console.log(t);//9
            console.log(t3)//父级不可以访问子级的变量。报错 not defined
            function f2() { 
                var t3 = 200; 
                console.log(t2); //10
                return t2 * t2; 
            }
            return  f2()
            
        }
        f1();

变量提升

如果一个声明的变量在函数体内,那么它的作用域就是函数内部。如果是在全局环境下声明的,那么它的作用域就是全局的。通过var声明的变量是无法用delete删除的。

函数内部的声明的变量会被提升到函数的头部。函数在解析执行的时候,先进行变量声明处理,然后再运行函数内部的代码。

变量和赋值语句一起书写,在js引擎解析时,会将其拆成声明和赋值2部分,声明置顶,赋值保留在原来位置

function a(){
    var b = 1
}

上述代码就相当于

function a(){
    var b 
    b = 1
}

变量重复声明不会出错,后面的会覆盖前面的。

var a = 1
var a = 2 
console.log(a)//被覆盖,a的值为2

当函数名和变量名相同时, 函数 > 变量,也就是先函数提升,后变量提升

var a = 1
function a(){
    
}
console.log(a)//a打印1

既然是先函数提升后变量提升那为什么会打印变量的值呢,因为,重复声明是会被后面的覆盖的

上述代码相当于


var a = function(){
    
}

var a = 1
console.log(a)//a打印1

我们来看几个题目在巩固一下作用域和变量提升

1.

if (!("a" in window)) { 
var a = 1; 
} 
console.log(a);

答案

if (!("a" in window)) { 
var a = 1; 
} 
console.log(a);//undefined

分析

1."a" in window是判断在全局作用域中存不存在a,若存在则为true,不存在为fasle
2.在es5中函数是没有块级作用域的,因此在if中定义的变量的作用域为包括此if语句的作用域,即变量a的作用域为全局作用域
3.在js执行过程中会先进行变量提升,因此在全局中先声明一个变量a
4.if()的判断为false,因此不会a不会赋值
5打印a为undefined


var a
if (!("a" in window)) { // true
 a = 1; 
} 
console.log(a);//undefined

2.

var a = 18;
function d() {
  console.log(a);
  var a = { age: 19};
  console.log(a);
}
d();  //  输出?
console.log(a);

答案

var a = 18;
function d() {
  console.log(a);//undefiend
  var a = { age: 19};
  console.log(a); //{ age: 19};
}
d();  //  输出?
console.log(a);//18

分析

在访问变量时会先在访问的作用域进行查找
1.函数d在执行的时候,遇到var a = {age:19}时进行了变量提升,只声明没有赋值,因此第一个打印语句打印undefined
2.全局中的a在全局中查找,打印18


//相当于
var a 
a = 18;
function d() {
  var a 
  console.log(a);//undefiend
  a = { age: 19};
  console.log(a); //{ age: 19};
}
d();  //  输出?
console.log(a);//18


小练习

练习1

console.log(a);
var a = 20;
console.log(a);
function a() {
}

练习2

f();
console.log(a);
console.log(b);
console.log(c);
function f() {
    var a = b = c = 9;
    console.log(a);
    console.log(b);
    console.log(c);
}

练习3

f();
function f() {
for(var k = 0; k <10; k++) {
	console.log(k);
}
console.log(k);
}

答案

练习1

  console.log(a); //打印函数a 
        var a = 20;
        console.log(a); //20
        function a() {
        }

练习2

f();
console.log(a);//Uncaught ReferenceError: a is not defined
console.log(b);//如果代码可以执行到这里(忽略上述行的报错),打印9
console.log(c);//如果代码可以执行到这里(忽略上述行的报错),打印9
function f() {
    var a = b = c = 9;
    console.log(a);//9
    console.log(b);//9
    console.log(c);//9
}