阿西河

所有教程

公众号
🌙
阿西河前端的公众号

我的收藏

    最近访问  (文章)

      教程列表

      抓包专区
      测试专区

      JS var 声明变量

      var 功能

      var 声明语句声明一个变量,并可选地将其初始化为一个值。

      var 用法

      var x = 1;
      
      if (x === 1) {
        var x = 2;
      
        console.log(x);
        // expected output: 2
      }
      
      console.log(x);
      // expected output: 2
      

      ECMAScript 的变量是松散类型的,所谓松散类型就是可以用来保存任何类型的数据。

      换句话说,每个变量仅仅是一个用于保存值的占位符而已。定义变量时要使用 var 操作符(注意 var 是一个关键字),后跟变量名(即一个标识符),如下所示:

      var message;
      

      这行代码定义了一个名为 message 的变量,该变量可以用来保存任何值(像这样未经过初始化的 变量,会保存一个特殊的值—— undefined)。

      ECMAScript 也支持直接初始化变量,因此在定义变量的同时就可以设置变量的值,如下所示:

      var message = "hi";
      

      在此,变量 message 中保存了一个字符串值 “hi” 。

      像这样初始化变量并不会把它标记为字符串类型;

      初始化的过程就是给变量赋一个值那么简单。

      因此,可以在修改变量值的同时修改值的类型,如下所示:

      var message = "hi";
      message = 100; //  有效,但不推荐
      

      在这个例子中,变量 message 一开始保存了一个字符串值 “hi” ,然后该值又被一个数字值 100 取 代。虽然我们不建议修改变量所保存值的类型,但这种操作在 ECMAScript 中完全有效。

      有一点必须注意,即用 var 操作符定义的变量将成为定义该变量的作用域中的局部变量。

      也就是说,如果在函数中使用 var 定义一个变量,那么这个变量在函数退出后就会被销毁,例如:

      function test(){
      var message = "hi"; // 局部变量
      }
      test();
      alert(message); // 错误!
      

      这里,变量 message 是在函数中使用 var 定义的。

      当函数被调用时,就会创建该变量并为其赋值。

      而在此之后,这个变量又会立即被销毁,因此例子中的下一行代码就会导致错误。

      语法

      var varname1 [= value1] [, varname2 [= value2] ... [, varnameN [= valueN]]];
      

      varnameN

      变量名。变量名可以定义为任何合法标识符。

      valueN

      变量的初始化值。该值可以是任何合法的表达式。默认值为 undefined。

      可以使用一条语句定义多个变量,只要像下面这样把每个变量(初始化或不初始化均可)用逗号分隔开即可:

      var message = "hi",
          found = false,
          age = 29;
      

      这个例子定义并初始化了 3 个变量。同样由于 ECMAScript 是松散类型的,

      因而使用不同类型初始化变量的操作可以放在一条语句中来完成。

      虽然代码里的换行和变量缩进不是必需的,但这样做可以提高可读性。

      在严格模式下,不能定义名为 eval 或 arguments 的变量,否则会导致语法错误。

      描述

      变量声明,无论发生在何处,都在执行任何代码之前进行处理。

      var 声明的变量的作用域是它当前的执行上下文,它可以是嵌套的函数,也可以是声明在任何函数外的变量。

      如果你重新声明一个 JavaScript 变量,它将不会丢失其值。

      将赋值给未声明变量的值在执行赋值时将其隐式地创建为全局变量(它将成为全局对象的属性)。

      声明和未声明变量之间的差异是:

      1. 声明变量的作用域限制在其声明位置的上下文中,而非声明变量总是全局的。

        function x() {
        y = 1;   // 在严格模式(strict mode)下会抛出 ReferenceError 异常
        var z = 2;
        }
        
        x();
        
        console.log(y); // 打印 "1"
        console.log(z); // 抛出 ReferenceError: z 未在 x 外部声明
        
      2. 声明变量在任何代码执行前创建,而非声明变量只有在执行赋值操作的时候才会被创建。

        console.log(a);                // 抛出ReferenceError。
        console.log('still going...'); // 永不执行。
        var a;
        console.log(a);                // 打印"undefined"或""(不同浏览器实现不同)。
        console.log('still going...'); // 打印"still going..."。
        
      3. 声明变量是它所在上下文环境的不可配置属性,非声明变量是可配置的(如非声明变量可以被删除)。

        var a = 1;
        b = 2;
        
        delete this.a; // 在严格模式(strict mode)下抛出TypeError,其他情况下执行失败并无任何提示。
        delete this.b;
        
        console.log(a, b); // 抛出ReferenceError。
        // 'b'属性已经被删除。
        

      由于这三个差异,未能声明变量将很可能导致意想不到的结果。

      因此,建议始终声明变量,无论它们是在函数还是全局作用域内。

      而且,在 ECMAScript 5 严格模式下,分配给未声明的变量会引发错误。

      变量提升

      由于变量声明(以及其他声明)总是在任意代码执行之前处理的,所以在代码中的任意位置声明变量总是等效于在代码开头声明。

      这意味着变量可以在声明之前使用,这个行为叫做“hoisting”。“hoisting”就像是把所有的变量声明移动到函数或者全局代码的开头位置。

      bla = 2
      var bla;
      // ...
      
      // 可以隐式地(implicitly)将以上代码理解为:
      
      var bla;
      bla = 2;
      

      因此,建议始终在作用域顶部声明变量(全局代码的顶部和函数代码的顶部),这可以清楚知道哪些变量是函数作用域(本地),哪些变量在作用域链上解决。

      重要的是,提升将影响变量声明,而不会影响其值的初始化。当到达赋值语句时,该值将确实被分配:

      function do_something() {
        console.log(bar); // undefined
        var bar = 111;
        console.log(bar); // 111
      }
      
      // is implicitly understood as:
      function do_something() {
        var bar;
        console.log(bar); // undefined
        bar = 111;
        console.log(bar); // 111
      }
      

      例子

      声明并初始化两个变量:

      var a = 0,
          b = 0;
      

      给两个变量赋值成字符串值:

      var a = "A";
      var b = a;
      
      // 等效于:
      
      var a, b = a = "A";
      

      留意其中的顺序:

      var x = y, y = 'A';
      console.log(x + y); // undefinedA
      

      在这里,x 和 y 在代码执行前就已经创建了,而赋值操作发生在创建之后。当"x = y"执行时,y 已经存在,所以不抛出 ReferenceError,并且它的值是’undefined'。

      所以 x 被赋予 undefined 值。然后,y 被赋予’A'。于是,在执行完第一行之后,x === undefined && y === ‘A’ 才出现了这样的结果。

      多个变量的初始化

      var x = 0;
      
      function f(){
        var x = y = 1; // x在函数内部声明,y不是!
      }
      f();
      
      console.log(x, y); // 0, 1
      // x 是全局变量。
      // y 是隐式声明的全局变量。
      

      隐式全局变量和外部函数作用域

      看起来像是隐式全局作用域的变量也有可能是其外部函数变量的引用。

      var x = 0;  // x是全局变量,并且赋值为0。
      
      console.log(typeof z); // undefined,因为z还不存在。
      
      function a() { // 当a被调用时,
        var y = 2;   // y被声明成函数a作用域的变量,然后赋值成2。
      
        console.log(x, y);   // 0 2
      
        function b() {       // 当b被调用时,
          x = 3;  // 全局变量x被赋值为3,不生成全局变量。
          y = 4;  // 已存在的外部函数的y变量被赋值为4,不生成新的全局变量。
          z = 5;  // 创建新的全局变量z,并且给z赋值为5。
        }         // (在严格模式下(strict mode)抛出ReferenceError)
      
        b();     // 调用b时创建了全局变量z。
        console.log(x, y, z);  // 3 4 5
      }
      
      a();                   // 调用a时同时调用了b。
      console.log(x, z);     // 3 5
      console.log(typeof y); // undefined,因为y是a函数的本地(local)变量。
      

      规范

      规范状态备注
      ECMAScript 1st Edition (ECMA-262)StandardInitial definition. Implemented in JavaScript 1.0
      ECMAScript 5.1 (ECMA-262) var statementStandard
      ECMAScript 2015 (6th Edition, ECMA-262) variable statementStandard
      ECMAScript Latest Draft (ECMA-262) variable statementDraft
      目录
      目录