• 函数进阶内容 旧时的 "var"


    旧时的 "var"

     
    本文用于帮助理解旧脚本

    本文所讲的内容对于帮助理解旧脚本很有用。

    但这不是我们编写新代码的方式。

    在本教程最开始那部分的 变量 这章中,我们提到了变量声明的三种方式:

    1. let
    2. const
    3. var

    var 声明与 let 相似。大部分情况下,我们可以用 let 代替 var 或者 var 代替 let,都能达到预期的效果:

     
    var message = "Hi";
    alert(message); // Hi

    但实际上 var 却是一头非常不同的,源自远古时代的怪兽。在现代脚本中一般不再使用它,但它仍然潜伏在旧脚本中。

    如果你不打算接触这样的脚本,你甚至可以跳过本章或推迟阅读本章。

    另一方面,了解将旧脚本从 var 迁移到 let 时的区别,以避免奇怪的错误,是很重要的。

    “var” 没有块级作用域

    用 var 声明的变量,不是函数作用域就是全局作用域。它们在代码块外也是可见的(译注:也就是说,var 声明的变量只有函数作用域和全局作用域,没有块级作用域)。

    举个例子:

     
    if (true) {
      var test = true; // 使用 "var" 而不是 "let"
    }
    
    alert(test); // true,变量在 if 结束后仍存在

    由于 var 会忽略代码块,因此我们有了一个全局变量 test

    如果我们在第二行使用 let test 而不是 var test,那么该变量将仅在 if 内部可见:

     
    if (true) {
      let test = true; // 使用 "let"
    }
    
    alert(test); // Error: test is not defined

    对于循环也是这样的,var 声明的变量没有块级作用域也没有循环局部作用域:

    for (var i = 0; i < 10; i++) {
      var one = 1;
      // ...
    }
    
    alert(i);   // 10,"i" 在循环结束后仍可见,它是一个全局变量
    alert(one); // 1,"one" 在循环结束后仍可见,它是一个全局变量

    如果一个代码块位于函数内部,那么 var 声明的变量的作用域将为函数作用域:

     
    function sayHi() {
      if (true) {
        var phrase = "Hello";
      }
    
      alert(phrase); // 能正常工作
    }
    
    sayHi();
    alert(phrase); // Error: phrase is not defined

    可以看到,var 穿透了 iffor 和其它代码块。这是因为在早期的 JavaScript 中,块没有词法环境,而 var 就是这个时期的代表之一。

    “var” 允许重新声明

    如果我们用 let 在同一作用域下将同一个变量声明两次,则会出现错误:

     
    let user;
    let user; // SyntaxError: 'user' has already been declared

    使用 var,我们可以重复声明一个变量,不管多少次都行。如果我们对一个已经声明的变量使用 var,这条新的声明语句会被忽略:

     
    var user = "Pete";
    
    var user = "John"; // 这个 "var" 无效(因为变量已经声明过了)
    // ……不会触发错误
    
    alert(user); // John

    “var” 声明的变量,可以在其声明语句前被使用

    当函数开始的时候,就会处理 var 声明(脚本启动对应全局变量)。

    换言之,var 声明的变量会在函数开头被定义,与它在代码中定义的位置无关(这里不考虑定义在嵌套函数中的情况)。

    那么看一下这段代码:

     
    function sayHi() {
      phrase = "Hello";
    
      alert(phrase);
    
      var phrase;
    }
    sayHi();

    ……从技术上讲,它与下面这种情况是一样的(var phrase 被上移至函数开头):

     
    function sayHi() {
      var phrase;
    
      phrase = "Hello";
    
      alert(phrase);
    }
    sayHi();

    ……甚至与这种情况也一样(记住,代码块是会被忽略的):

     
    function sayHi() {
      phrase = "Hello"; // (*)
    
      if (false) {
        var phrase;
      }
    
      alert(phrase);
    }
    sayHi();

    人们将这种行为称为“提升”(英文为 “hoisting” 或 “raising”),因为所有的 var 都被“提升”到了函数的顶部。

    所以,在上面的例子中,if (false) 分支永远都不会执行,但没关系,它里面的 var 在函数刚开始时就被处理了,所以在执行 (*) 那行代码时,变量是存在的。

    声明会被提升,但是赋值不会。

    我们最好用例子来说明:

     
    function sayHi() {
      alert(phrase);
    
      var phrase = "Hello";
    }
    
    sayHi();

    var phrase = "Hello" 这行代码包含两个行为:

    1. 使用 var 声明变量
    2. 使用 = 给变量赋值。

    声明在函数刚开始执行的时候(“提升”)就被处理了,但是赋值操作始终是在它出现的地方才起作用。所以这段代码实际上是这样工作的:

     
    function sayHi() {
      var phrase; // 在函数刚开始时进行变量声明
    
      alert(phrase); // undefined
    
      phrase = "Hello"; // ……赋值 — 当程序执行到这一行时。
    }
    
    sayHi();

    因为所有的 var 声明都是在函数开头处理的,我们可以在任何地方引用它们。但是在它们被赋值之前都是 undefined。

    上面两个例子中,alert 运行都不会报错,因为变量 phrase 是存在的。但是它还没有被赋值,所以显示 undefiend

    总结

    var 与 let/const 有两个主要的区别:

    1. var 声明的变量没有块级作用域,它们仅在当前函数内可见,或者全局可见(如果变量是在函数外声明的)。
    2. var 变量声明在函数开头就会被处理(脚本启动对应全局变量)。

    涉及全局对象时,还有一个非常小的差异,我们将在下一章中介绍。

    这些差异使 var 在大多数情况下都比 let 更糟糕。块级作用域是这么好的一个东西。这就是 let 在几年前就被写入到标准中的原因,并且现在(与 const 一起)已经成为了声明变量的主要方式。

  • 相关阅读:
    ubuntu下python的错误
    Zookeeper(二) zookeeper集群搭建 与使用
    Zookeeper(一) zookeeper基础使用
    MapReduce(五) mapreduce的shuffle机制 与 Yarn
    MapReduce(四) 典型编程场景(二)
    Mysql(一) 基本操作
    MapReduce(三) 典型场景(一)
    MapReduce(二)常用三大组件
    MapReduce(一) mapreduce基础入门
    Hive(六)hive执行过程实例分析与hive优化策略
  • 原文地址:https://www.cnblogs.com/perfectdata/p/15587634.html
Copyright © 2020-2023  润新知