JS基础:变量作用域

作用域、变量提升的知识点,面试时会经常遇到

作用域的分类

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

  • 全局作用域:作用范围整个 script 标签内部,页面打开时创建,关闭时销毁
  • 函数作用域:作用范围函数内部,随着函数调用结束而销毁

全局变量

  • 定义在函数外的变量,都是全局变量
  • 在函数内部,不使用 var 声明的变量 也是是全局变量
  • 全局变量有全局作用域,可以在页面任意位置访问,包括函数中
  • 全局变量在页面关闭后销毁

局部变量

  • 在函数内部,使用 var 声明的变量是局部变量
  • 在函数内部,不使用 var 声明的变量 是全局变量
  • 函数的形参也是属于局部变量,只能在函数内部访问
  • 局部变量,只能在函数内部访问
  • 局部变量在函数开始执行时创建,函数执行完后局部变量会自动销毁

作用域生命周期

  • 全局作用域在页面关闭后销毁
  • 调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁
  • 每调用一次函数就会创建一个新的函数作用域,他们之间是互相独立的

作用域的范围

全局变量可以在任意位置访问,局部变量只能在函数内部访问

    var a = 'aaa';  // 全局变量

    function foo() {
        var b = 'bbb';  // 局部变量
        c = 'ccc';      // 全局变量
        console.log(a); // 打印结果:aaa  说明全局变量可在函数中访问
    }

    foo();
    console.log(b); // 报错:Uncaught ReferenceError: b is not defined 说明局部变量只能在函数内访问

函数内访问全局变量

在函数作用域操作一个变量时,会先在函数内寻找,如果有就直接使用(就近原则),如果没有则向上一级作用域中寻找,直到找到全局作用域;如果全局作用域中依然没有找到,则会报错 ReferenceError

在函数中要访问全局变量可以使用window对象(比如说,全局作用域和函数作用域都定义了变量a,如果想访问全局变量,可以使用window.a

    var a = 'aaa';  // 创建全局变量a
    var b = 'bbb';

    function foo() {
        var a = 111;  // 创建局部变量a
        console.log(a); // 打印结果:111  就近原则 优先使用局部变量a
        console.log(b); // 打印结果:bbb  就近原则 局部没有使用全局
        console.log(window.a);  // // 打印结果:aaa  强制使用全局变量a
    }

    foo();

作用域链:内部函数访问外部函数的变量,采用的是链式查找的方式来决定取哪个值,这种结构称之为作用域链。查找时,采用的是就近原则

var num = 10;

function fn() {
    // 外部函数
    var num = 20;

    function fun() {
        // 内部函数
        console.log(num);
    }
    fun();
}
fn();	// 20

window对象

在全局作用域中有一个全局对象window,window对象可以在页面任意位置直接访问

  • 创建的全局变量都会作为window对象的属性保存,所有的全局变量都是window对象的属性
  • 创建的函数都会作为window对象的方法保存
    var a = 'aaa';  // 相当于 var a;  window.a = 'aaa';

    function foo() {
        a = 111;  // 修改变量a的值 就近原则 这里修改全局变量a 相当于 window.a = 111;
        b = 222;  // 创建全局变量b 相当于 window.b = 222;
        var c = 'ccc';  // 创建局部变量
        c = 333;  // 修改变量c的值 就近原则 这里修改局部变量c 和window无关
        console.log(a); // 打印结果:111  相当于 window.a
        console.log(window.a);  // // 打印结果:111  强制使用全局变量a 因为被修改了所以是111
    }



    // 创建的函数都会作为window对象的方法保存
    window.foo();
    console.log(b);     // 函数内隐式声明的全局变量b  b必须在调用函数后才会被声明
    //console.log(c);     // c is not defined  c局部变量已经随着函数运行结束销毁了
    window.alert('哈哈');

变量提升

JS的代码是从上到下执行的,但下面代码并没有报错

    console.log(a);   // undefined  并没有报错
    var a = 1;  

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

    /*
    console.log(a);   // undefined  并没有报错
    var a = 1;
    */
    
    // 上方代码相当于
    var a;
    console.log(a);
    a = 1;

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

    console.log(window.a);  // undefined  对象的属性如果不存在会报undefined
    console.log(a);   // ReferenceError: a is not defined
    a = 1;  // 这里也相当于 window.a  只不过没有var不会被声明提前

举例4

foo();

function foo() {
    if (false) {
        var i = 123;
    }
    console.log(i);
}

打印结果:undefined。注意,打印结果并没有报错,而是 undefined。这个例子,再次说明了:变量 i 在函数执行前,就被提前声明了,只是尚未被赋值。

例4中, if(false)里面的代码虽然不会被执行,但是整个代码有解析的环节,解析的时候就已经把 变量 i 给提前声明了。

总结

既然JS中存在变量提升的现象,那么,在实战开发中,为了避免出错,建议先声明一个变量,然后再使用这个变量。

函数的声明提前

函数声明:

  • 使用函数声明的形式创建的函数function foo(){},会在所有的代码执行之前就被创建完成,不仅仅是声明提前
  • 所以,使用函数声明的形式创建的函数,可以先调用函数,再定义
    /*
    fun1();  // 虽然 函数 fn1 的定义是在后面,但是因为被提前定义了, 所以此处可以调用函数

    function fun1() {
        console.log('我是函数 fun1');
    }
    */

    // 以上代码相当于
    function fun1() {
        console.log('我是函数 fun1');
    }

    console.log(fun1);  // 输出函数体
    fun1(); // 我是函数 fun1

函数表达式

  • 使用函数表达式创建的函数var foo = function(){},不会被提前创建
  • 如果函数表达式有使用var,会提前声明变量
    /*
    console.log(fun1);  // undefined
    fun1();             // TypeError: fun1 is not a function

    var fun1 = function () {
        console.log('我是函数 fun1');
    }
    */

    // 以上代码相当于
    var fun1;
    console.log(fun1);  // undefined
    fun1();             // TypeError: fun1 is not a function

    fun1 = function () {
        console.log('我是函数 fun1');
    }

JS没有块级作用域(ES6之前)

在其他编程语言中(如 Java、C#等),存在块级作用域,由{}包括起来。比如在 Java 语言中,if 语句里创建的变量,只能在if语句内部使用:

if(true){
    int num = 123;
    system.out.print(num); // 123
}
system.out.print(num); // 报错

但是,在 JS 中没有块级作用域(ES6之前)。举例如下:

if(true){
var num = 123;
    console.log(123); //123
}

console.log(123); //123(可以正常打印)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值