zoukankan      html  css  js  c++  java
  • 神奇的函数作用域

    前言

    今天发现了两个关于函数作用域的神奇例子,这里和大家分享分享:

    第一个例子

        var a = 1;
     
        function foo() {
          if (!a) {
            var a = 2;
          }
          alert(a);
        };
     
        foo();

    上面这段代码在运行时会产生什么结果?

     我们来分析一下:

            1.创建了全局变量 a,定义其值为 1
            2.创建了函数 foo
            3.在 foo 的函数体内,if 语句将不会执行,因为 !a 会将变量 a 转变成布尔的假值,也就是 false
            4.跳过条件分支,alert 变量 a,最终的结果应该是输出 1 

    看起来无懈可击的分析,但是实际上,结果错误。答案竟然是 2!为什么?

    什么叫申明?

            是指你声称某样东西的存在,比如一个变量或一个函数;但你没有说明这样东西到底是什么,仅仅是告诉解释器这样东西存在而已;

    什么叫定义?

            是指你指明了某样东西的具体实现,比如一个变量的值是多少,一个函数的函数体是什么,确切的表达了这样东西的意义。

    所以上面的代码实际上可以写成这样:

        var a;
        a = 1;
     
        function foo() {
          var a;    // 关键在这里
          if (!a) {
            a = 2;
          }
          alert(a);   // 此时的 a 并非函数体外的那个全局变量
        }
        foo();

    然后又有人会问,不是有个if吗?if不成立哪就不会为a赋值为2。

    因为 JavaScript 没有块级作用域(Block Scoping),只有函数作用域(Function Scoping),所以说不是看见一对花括号 {} 就代表产生了新的作用域,和 C 不一样!

    当解析器读到 if 语句的时候,它发现此处有一个变量声明和赋值,于是解析器会将其声明提升至当前作用域的顶部(这是默认行为,并且无法更改),这个行为就叫做 Hoisting。

    怎样能够alert出那个a=1?

        let a;
        a = 1;
     
        function foo() {
          let a;    // 关键在这里
          if (!a) {
            a = 2;
          }
          alert(a);   // 此时的 a 并非函数体外的那个全局变量
        }
        foo();

    es6的语法,javascript是有块级作用域的。

    还可以通过闭包的方式实现:

      var a = 1;
         
        function foo() {
          if (!a) {
            (function() {    
              var a = 2;    
            }());        
          };
          alert(a);
        };
         
        foo();

    第二个例子

     var a = 1;        
        function test() {
            foo();
                     
            var foo = function() {
                alert(a);
            }
        }
                     
        test();

    这个运行的结果是什么?初略一看,alert(1),但是实际上报错。

      Uncaught TypeError: foo is not a function。

    为什么会这样?

      提升的仅仅是变量名 foo,至于它的定义依然停留在原处。因此在执行 foo() 之前,作用域只知道 foo 的命名,不知道它到底是什么,所以执行会报错(通常会是:undefined is not a function)。这叫做函数表达式(Function Expression),函数表达式只有命名会被提升,定义的函数体则不会。

     var a = 1;        
        function test() {
            var foo;
            foo();             // 这个时候函数foo,只声明,未赋值。
            foo = function() {
                alert(a);
            }
        }
                     
        test();

    怎么改?   

     var a = 1;        
        function test() {
            var foo = function() {
                alert(a);
            }
            foo();
        }
                     
        test();        // 1

    这个例子也展示了函数声明与函数表达式的差别,函数申明会放到作用域的顶部,函数表达式则不会。

    最后引用很多书中的一句话:“请始终保持作用域内所有变量的声明放置在作用域的顶部”,相信现在的你对这句话应该有一个认识了。

  • 相关阅读:
    python 装饰器
    git
    JS原生方法实现jQuery的ready()
    js获取css属性方法
    列表页调出点击量
    数组操作
    判断IE版本
    判断IE浏览器用IE条件表达式
    [jQuery] Cannot read property ‘msie’ of undefined错误的解决方法
    复选框字段数组拆分后循环选项值,if判断根据选项值,前端输出html
  • 原文地址:https://www.cnblogs.com/blogcxz/p/7341978.html
Copyright © 2011-2022 走看看