快速了解JavaScript作用域

W3 school这样解释的:
在 JavaScript 中,对象和函数也是变量。作用域决定了从代码不同部分对变量、对象和函数的可访问性。


局部作用域和全局作用域

JavaScript中,有两种作用域类型——局部作用域和全局作用域。

变量的可访问性由作用域决定,如果变量a在函数体中声明,那么它会成为该函数的局部变量,外部是不可访问的。

    var global = 321;
    function private() {
            var a = 123; //局部变量,外部不可见
            console.log(a);//123
            console.log(global);//321
        }
        private();
        console.log(a); //错误:Uncaught ReferenceError: a is not defined

上面的代码中,global是全局变量,函数体中也可以访问到它,但是a是声明在函数体中的变量,作用域局限于函数体中。当外面调用输出它时,就会报错,因为未定义。


JavaScript 变量的有效期

JavaScript 变量的有效期始于其被创建时。

局部变量会在函数完成时被删除。

全局变量会在您关闭页面是被删除。


预编译

在练习之前,我们先提一下JS中的预编译,等会我们将会用到

1、创建AO对象
就是隐式的创建一个AO(Activation Object)空对象。

2、查找形参和变量声明,将形参名和变量名作为AO对象的属性,值为undefined
注意此处是变量声明(必须带var),只是查找形参名和变量声明名不赋值。

3、将形参与实参统计
即修改AO对象中属性名为形参的值为传入的实参,如果没有形参此步略过。

4、查找函数声明,函数名作为AO对象的属性,函数体作为的值
注意:此处是函数声明,而非匿名函数和函数表达式 。

总结:预编译过程就是查找变量声明、形参和函数声明的过程,并不初始化赋值,在解释执行阶段才会进行初始化 。


练习

对于作用域的一些基础我们已经了解差不多了,来结合一下题目来测试一下自己的掌握度吧。

可以先自己试着分析,然后我会在后面把这些题目逐一进行分析。

//题目一:
    var a = "aa";
    function test() {
            console.log(a);
            var a = "bb";
            console.log(a);
        }
    test();
    alert(a);
//题目二:
    (function() {     
        var a = 5    
        function a() {}    
        console.log(a);

        function b() {}
        b = 6;
        console.log(b);
        var c = d = b    
    })();
    console.log(d);
    console.log(c);
//题目三:
    var foo = {
            n: 1
        };
    (function(foo) {
        console.log(foo.n);
        foo.n = 3;
        var foo = { 
            n: 2
        }
        console.log(foo.n);
    })(foo) 
    console.log(foo.n)

解析

//题目一:
    var a = "aa";
    function test() {
            console.log(a);
            var a = "bb";
            console.log(a);
        }
    test();
    alert(a);

在这里插入图片描述

预编译结束后然后逐行执行:
首先全局中的a = undefined  ——> “aa”;

调用test()时首先执行console.log(a),由于预编译期间声明了局部变量a,并没有赋值,此时a = undefined,控制台打印结果为 undefined 。(如果test函数中没有var a,执行时就会往上找,找到全局中的变量a进行输出。)

紧接着test中的局部变量a = undefined  ——> “bb”;

执行console.log(a),由于上面刚刚给变量a进行了赋值"bb",所以控制台输出 bb

最后alert(a) 这里就要区分开全局变量和局部变量的作用域,很明显这里执行结果会弹出结果: aa

//题目二:
    (function() {     
        var a = 5    
        function a() {}    
        console.log(a);

        function b() {}
        b = 6;
        console.log(b);
        var c = d = b;    
    })();
    console.log(d);
    console.log(c);

在这里插入图片描述

预编译结束后然后逐行执行:
a = 函数体  ——> 5;

执行console.log(a),控制台打印 5

b = 函数体  ——> 6;

执行console.log(a),控制台打印 6

执行c=d=b,即同时给c和d赋值等于6 。(注意:这里的d未用var关键字声明,自动成为全局对象)

立即执行函数执行后就被销毁了,但是d是全局对象,所以console.log(d)可以找到d并打印出 6 。c是的作用域在立即执行函数中,当立即执行函数执行完被销毁后,也就无法在全局中找到它,所以结果报错Uncaught ReferenceError: c is not defined

//题目三:
    var foo = {
            n: 1
        };
        (function(foo) {
            console.log(foo.n);//foo1
            foo.n = 3;
            var foo = { 
                n: 2
            }
            console.log(foo.n);
        })(foo) 
    console.log(foo.n)

在这里插入图片描述

执行console.log(foo.n)因为函数体中声明的局部变量foo没有找到属性n,但找到了传进来的实参foo,所以输出 1

后面foo.n = 3改变的是全局的foo对象中属性n的属性值(因为只有全局中的foo具有属性n。) 1 变成 3

foo = {n:2} 是将foo重新赋值了(且是在立即执行函数内部重新声明的,作用域是在立即执行函数内生效的)。

console.log(foo.n)输出 2 。

立即执行函数销毁后,局部作用域的 foo={n: 2} 不见了,输出的是全局中的foo,此时foo = {n: 3};故输出 3 。

属性n。) 1 变成 3

foo = {n:2} 是将foo重新赋值了(且是在立即执行函数内部重新声明的,作用域是在立即执行函数内生效的)。

console.log(foo.n)输出 2 。

立即执行函数销毁后,局部作用域的 foo={n: 2} 不见了,输出的是全局中的foo,此时foo = {n: 3};故输出 3 。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值