Javascript中的局部变量、全局变量的详解


在看这篇文章的内容之前我们先来看下面的一道题目(这两天参加校招笔试,碰到了很多这种题目)

    var test = 'test';
    function f() {
        console.log(test); 
        var test = 'hello';
        console.log(test);
    }
    f();

执行函数fn的时候会输出什么呢?
天啦,这道题我会,js中的作用域链中说了,使用一个变量的时候会先向自己的作用域中查找,如果找不到,则会查找父级作用域

那么上面理所当然应该输出 test hello,如果这样想,那么就是大错特错了,这一切还是和js这门语言的特性有关.继续向后探索.

1. 作用域和变量

大多数的语言都有作用域这个概念,js中也有局部作用域和全局作用域.

首先来看下面的代码:

var a = 10;
console.log(a);   //10 

将a定义在全局作用域中,打印a的值为10

再看下面:

    var a = '我是函数外部的变量';
    function foo() {
        var a = '我是函数内部的变量'
    }
    console.log(a)  //我是函数外部的变量

此刻打印的结果是函数外面的变量a,为什么是这样呢,这就出现了局部作用域的概念.

从字面意思来理解全局作用域和局部作用域:

  1. 全局作用域:在整个文档范围内有效
  2. 局部作用域:只在某个范围内有效

在上面的代码中,在函数foo后面的大括号中包围的代码就属于一个块级作用域,它里面定义的变量a就是一个局部变量,只在这个function里面有定义。出了这个function,就如同没有定义过一样。

此外,js还可以不使用关键字直接定义变量,那么会发生什么情况,看下面:

    var a = '我是函数外部的变量';
    function foo() {
        b = '我是直接定义的变量';
        var c = '我是函数内部的变量';
    }
    foo()
    console.log(a)  //我是函数外部的变量
    console.log(b)  //我是直接定义的变量
    console.log(c)  //c is not defined

由此可以看出 c 在函数执行后是没有被定义的。说明在函数体内用 var 关键字声明的变量 c 是局部变量;
在函数体外使用 var 关键字定义的变量a和在函数体内未用任何关键字定义的变量b 是全局变量。

得出结论:

在函数体外使用var关键字定义的变量和在函数体内未用任何关键字声明的变量是全局变量,在函数体内使用var关键字声明的变量是局部变量。

2. 块级作用域

在c语言中,for循环,if语句等都有块级作用域,那么在js中是怎样的呢?

    var a = 'apple'
    if(true) {
        var a = 'banana'
    }
    console.log(a)  //banana

通过上面的代码我们可以看到,a的值发生了改变
if后面的大括号不是块级作用域

实际上不仅仅是if语句,我们常用的for循环,while语句都不会形成一个块级作用域.

    for(var i = 0 ; i < 3 ;i++) {
        break;
    }
    console.log(i);  //0
    k =5;
    while(k>1) {
        k--;
        var m = 10;
    }
    console.log(k);  //1
    console.log(m);  //10

所以不要被括号所迷惑了,在大括号中定义的变量不一定就是局部作用域,在js中,形成局部作用域的方式只有一种,那就是函数.如果一个变量,没有定义在任何的function中,那么它将在全部程序范围内都可以使用

3. 作用域链

    var a = 'apple'
    var b = 'orange'
    function f() {
        var a = 'banana'
        console.log(a);  //banana
        console.log(b);  //orange
    }
    f();
    console.log(a);  //apple

在上面的代码中,首先会输出banana,因为在输出的时候会查找作用域链当遇见一个变量时,JS引擎会从其所在的作用域依次向外层查找,查找会在找到第一个匹配的标识符的时候,所以b的结果就是函数全局作用域内的orange,最后打印a是在全局,所以结果是apple

当遇见一个变量时,JS引擎会从其所在的作用域依次向外层查找,查找会在找到第一个匹配的标识符的时候停止.

function outer(){
    var a = 3;		//a的作用域就是outer
    function inner(){
        var b = 5;		//b的作用域就是inner
        console.log(a);	//能够正常输出3,a在本层没有定义,就是找上层
        console.log(b);   //能够正常输出5
    }
    inner();
}

outer();
console.log(a);		//报错,因为a的作用域outer

作用域链:一个变量在使用的时候,就会在当前层去寻找它是否被定义,如果找不到,就找上一层function,直到找到全局变量,如果全局也没有,就报错。

var a = 1;		//全局变量
var b = 2;		//全局变量
function outer(){
    var a = 3;		//遮蔽了外层的a,a局部变量
    function inner(){
        var b = 4;  //遮蔽了外层的b,b局部变量
        console.log(a);   //① 输出3,a现在在当前层找不到定义的,所以就上一层寻找
        console.log(b);   //② 输出4
    }
    inner();		//调用函数
    console.log(a);	//③ 输出3
    console.log(b); //④ 输出2 b现在在当前层找不到定义的,所以就上一层寻找
}
outer();		//执行函数,控制权交给了outer
console.log(a);	// ⑤ 输出1
console.log(b); // ⑥ 输出2

多层嵌套,如果有同名的变量,那么就会发生“遮蔽效应”:

var a = 2;		//全局变量
function fn(){
	console.log(a) 		//undefined
    var a = 3;         //就把外层的a给遮蔽了,这函数内部看不见外层的a了。
    console.log(a);	//输出3,变量在当前作用域寻找,找到了a的定义值为3
}
fn();
console.log(a);  //输出2,变量在当前作用域寻找,找到了a的定义值为1

这个就回到了我们最初提出的问题,为什么会出现这种情况??

4. 变量提升

就像上面的一段代码,上面为什么会输出undefined呢???按照作用域查找规则,如果自己的作用域内没有这个变量,就会向上查找,那么按道理应该是2

这便是JavaScript的变量提升机制起了”作用“。下面介绍一下变量提升:
在函数体内变量声明总会被解释器”提升“到函数体的顶部,向上面的代码,js在执行的时候会处理成下面的样子,只不顾是我们看不到而已.

    var a = 2;		
    function fn(){
        var a;
        console.log(a) 		
        a = 3;      
        console.log(a);	
    }
    fn();
    console.log(a);  

由此可见,变量提升只提升声明部分,不提升赋值部分。

我们再来看看下面的代码,在函数里面定义函数aa,并且在声明函数之前就调用函数,那会发生什么呢?

    var a = 5;
    function f() {
        console.log(a);  //undefined
        var a = 10;
        console.log(aa());  //10
        function aa() {
            return a;
        }
    }
    f();

在函数声明之前就调用函数,不仅没有报错,而且还输出了正确的结果,刚才我们就提到了函数里定义的变量会进行变量提升,其实函数里定义的函数也会进行提升,而且提升的优先级还高于变量,上面的代码在执行时会被处理为如下:

    function f() {
        function aa() {
            return a;
        }
        var a;
        console.log(a);  //undefined
        a = 10;
        console.log(aa());  //10
    }

5. 一个例子

    var b = 'boy';
    console.log(b);
    function f() {
        console.log(a);
        console.log(c);
        if(a === 'apple'){
            a = 'Alice'
        }else {
            a = 'Ada'
        }
        console.log(a);
        var a = 'Andy';
        middle();
        function middle() {
            console.log(c++);
            var c = 100;
            console.log(++c);
            small();
            function small() {
                console.log(a);
            }
        }
        var c = a = 88;
        function bottom() {
            console.log(this.b);
            b = 'body'
            console.log(b);
        }
        bottom()
    }
    f();
    console.log(b);


 //    boy
 //  undefined
 //  undefined
 //  Ada
 //  NaN
 // 101
 // Andy
 // boy
 // body
 // body

第一次打印b.是全局的b,输出boy
打印ac的时候,二者都还为定义,所以是undefined
判断a不等于apple,所以a赋值为Ada
打印a时,a的值为Ada
又将a的值改为Andy
执行函数middle,打印c++,但此刻c的值是undefined,c++的值为NaN.
然后c赋值为100,++c的值为101
打印a,aAndy;
执行函数bottom,打印this.b,此刻this指向window,所以值为boy;
b的值改为body,打印b,为body;
此刻值得注意的是,声明变量b的时候没有使用任何关键字,所以b变为全局变量,最后打印b的值为body

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值