带你彻底搞清Js变量的声明及其作用域

搞前端的小码农们应该都知道,Js不同于其他语言的块级作用域,它使用的是函数作用。那么到底什么是块级作用域,什么又是函数作用域呢:

  • 块级作用域:以一对{}为界限形成一个代码块,代码块内声明的变量对于代码块外不可见。
  • 函数作用域:以function(){}为界限,函数内声明的变量对函数外不可见。

怎么样,到这是不是觉得“楼主你在侮辱我的智商吧,这么简单的问题还用你说”,稍安勿躁,就这么一个看似简单的作用域问题,里面可是有很多小坑的呢,下面让我们一一来举例说明

1.在JavaScript中没有用var声明的变量都是全局变量

    t = 10;//全局变量
    function foo() {
        t = 3;//全局变量,和上面的t是同一个变量
        console.log(t);
    }
    console.log(t);
    foo();
    console.log(t);
复制代码

这段代码的输出结果是

 10

 3

 3
复制代码

这个函数内部的t因为并不是用var声明的变量,所以它虽然在函数内但它的作用域并不是这个函数而是全局,那么此时他和外部的t是在同一个作用域内,那么当函数执行时后面的全局变量t的值就会覆盖前面的全局变量t的值,导致全局作用域内的t的值更改为3.

好,那么我们现在稍微的来修改一下这个代码

    var t = 10;
    function foo() {
        t = 3;
        console.log(t);
    }
    console.log(t);
    foo();
    console.log(t);
复制代码

看出我修改了什么地方吗,对,把函数外的t用var声明了一下,那么你觉得修改后的代码和上面的输出会一样吗?答案是一样的。虽然我们用var声明了这个变量,但由于它是在函数外,所以它的作用域还是全局,因此并没有丝毫影响.

再让我们修改一下

    var t = 10;
    function foo() {
        var t = 3;//用var声明的,非全局变量,作用域为此函数内
        console.log(t);
    }
    console.log(t);
    foo();
    console.log(t);
复制代码

这一次,会发现控制台输出的值变为了

10

3

10
复制代码

函数内的t用var声明形成了函数作用域,所以即使是函数执行完毕最后一个console.log(t)输出的还是作用于全局的那个变量t。

2.变量声明的提升

继续修改上面的代码

    var t = 10;
    function foo() {
        console.log(t);
        var t = 3;
    }
    console.log(t);
    foo();
    console.log(t);
复制代码

控制台的输出为:

10

undefined

10
复制代码

咦,中间输出的怎么会是undefined?这就涉及到变量声明的提升的问题了,在foo函数内 var t = 3;虽然在console.log(t);之后,但是var声明的变量会提升到当前作用域的最前面,注意这里仅仅是声明的提升,而值却是不提升的。

3.作用域链

作用域链:由于js的变量都是对象的属性,而该对象可能又是其它对象的属性,而所有的对象都是window对象的属性,所以这些对象的关系可以看作是一条链,链头就是变量所处的对象,链尾就是window对象

光说不练假把式,上代码

    var t = 10;
    function foo() {
        console.log(t);
         t = 3;
    }
    console.log(t);
    foo();
    console.log(t);
复制代码

控制台输出为

10

10

3
复制代码

foo中的t不是用var声明的,所以不存在变量提升的问题,故此时在当前函数作用域内找不到声明在console.log(t)的t,那么这时候怎么办呢,继续往上层作用域找啊,发现上一层的全局作用域中声明了var t = 10,找到啦,所以第二个输出的是10。这就是传说中的作用域链啦,对于console.log(t)来说,他所处的作用域链就是foo-->window

4.Js没有块级作用域

其实写这个标题的时候我是有点方的,因为自从es6的let出现以后,Js也有块级作用域了。不过没关系,过于let我们一会再说。

先上代码

if(true){
       var m=5;
       console.log(m);
   }
   console.log(m);
复制代码

控制台输出为

5

5
复制代码
 function foo (){
       var m=5;
       console.log(m);
   }
   foo();
   console.log(m);
复制代码

控制台输出为

5

Uncaught ReferenceError: m is not defined
复制代码

怎么样,是不是很清晰很明了。Js中作用域以以function(){}为界限,不以{}为界限。

5.Es6中let形成的块级作用域

ES6 新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。也就是是说如果在以{}为界限的代码块中用let声明了一个变量,那么这个变量的作用域就是这个代码块,对于代码块外是不可见的。

if(true){
       let m=5;
       console.log(m);
   }
   console.log(m);
复制代码

控制台输出:

5

Uncaught ReferenceError: m is not defined
复制代码

let的出现解决了变量污染的问题,可以代替闭包解决一些问题,后面我也会专门写一篇关于闭包的文章。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值