zoukankan      html  css  js  c++  java
  • javscript中变量的作用域和提升

    示例:

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

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

    初学者思路:

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

    为什么是10呢?这是因为变量提升的原因造成的。


    原因分析:

    声明与定义

    为了理解变量提升,我们先来看一个简单的情况:

    var a = 1;
    var a;  叫做 “声明变量”

    var a = 1;  叫做 “定义变量”

    声明:是指你声称某样东西的存在,比如一个变量或一个函数;但你没有说明这样东西到底是什么,仅仅是告诉解释器这样东西存在而已
    定义:是指你指明了某样东西的具体实现,比如一个变量的值是多少,一个函数的函数体是什么,确切的表达了这样东西的意义。

    总结一下:

    var a;            // 这是声明
    a = 1;            // 这是定义(赋值)
    var a = 1;        // 合二为一:声明变量的存在并赋值给它

    重点来了:当你以为你只做了一件事情的时候(var a = 1),实际上解释器把这件事情分解成了两个步骤,一个是声明(var a),另一个是定义(a = 1)。

    这和变量提升有何关系?

    回到最开始的那个令人困惑的例子,我告诉你解释器是如何分析你的代码的:

    var a;

    a = 1;

    function foo(){

      var a; //关键点

      if (!a) {

        a = 10;

              }

      alert (a);  //此时的a并非是函数体外的那个变量

    }

    如代码所示,在进入函数体后解释器声明了新的变量 a,而无论 if 语句的条件如何,都将为新的变量 a 赋值为 10。
     

    作用域:

    有人可能会问了:“为什么不是在 if 语句内声明变量 a?”

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

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

    如果我就是想要 alert(a) 出那个 1 怎么办?

    创建新的作用域

    alert(a) 在执行的时候,会去寻找变量 a 的位置,它从当前作用域开始向上(或者说向外)一直查找到顶层作用域为止,若是找不到就报 undefined。

    因为在 alert(a) 的同级作用域里,我们再次声明了本地变量 a,所以它报 10;所以我们可以把本地变量 a 的声明向下(或者说向内)移动,这样 alert(a) 就找不到它了。

    记住:JavaScript 只有函数作用域!

    var a = 1;
    function foo() {
      if (!a) {
        (function() {   // 这是上一篇说到过的 IIFE,它会创建一个新的函数作用域
          var a = 2;    // 并且该作用域在 foo() 的内部,所以 alert 访问不到
        }());        // 不过这个作用域可以访问上层作用域哦,这就叫:“闭包”
      };
      alert(a);
    };
    foo();
     
    你或许在无数的 JavaScript 书籍和文章里读到过:“请始终保持作用域内所有变量的声明放置在作用域的顶部”,因为这样可以避免 变量提升 特性给你带来的困扰,在当前作用域内有哪些变量可以访问。但是,变量声明的提升并非 提升的全部。在 js中,有四种方式可以让命名进入到作用域中(按优先级):

    1.语言定义的命名:比如 this 或者 arguments,它们在所有作用域内都有效且优先级最高,所以在任何地方你都不能把变量命名为 this 之类的,这样是没有意义的
    2.形式参数:函数定义时声明的形式参数会作为变量被 hoisting 至该函数的作用域内。所以形式参数是本地的,不是外部的或者全局的。当然你可以在执行函数的时候把外部变量传进来,但是传进来之后就是本地的了
    3.函数声明:函数体内部还可以声明函数,不过它们也都是本地的了
    4.变量声明:这个优先级其实还是最低的,不过它们也都是最常用的


    进一步理解声明和定义的区别:提升只提升了命名,没有提升定义

    函数声明与函数表达式的差别

    先看两个例子:

    function test(){

       foo();
      function foo(){
        alert ("会显示");
      }
    }
    test () ;
     
    function test(){
      foo();
      var foo = function () {
        alert("不会显示");
      }
    }
    test();

         在第一个例子里,函数 foo 是一个声明,既然是声明就会被提升(我特意包裹了一个外层作用域,因为全局作用域需要你的想象,不是那么直观,但是道理是一样的),所以在执行 foo() 之前,作用域就知道函数 foo 的存在了。这叫做函数声明(Function Declaration),函数声明会连通命名和函数体一起被提升至作用域顶部。

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

  • 相关阅读:
    禁止浏览器缓存input值
    网站性能分析插件
    获取没有key值的数据,用循环器
    //解决scrollview嵌套gridview不默认显示顶部
    解决ScrollView下嵌套ListView进页面不在顶部的问题
    Android:完美解决ScrollView嵌套GridView
    Android RadioButton代码去掉左边默认显示的圆圈
    Android设置虚线、圆角、渐变
    android在代码中四种设置控件(以及TextView的文字颜色)背景颜色的方法
    Android开发之Intent跳转到系统应用中的拨号界面、联系人界面、短信界面
  • 原文地址:https://www.cnblogs.com/zhaoxinmei-123/p/8909178.html
Copyright © 2011-2022 走看看