zoukankan      html  css  js  c++  java
  • JavaScript中变量提升是语言设计缺陷

    首先纠正下,文章标题里的 “变量提升” 名词是随大流叫法,“变量提升” 改为 “标识符提升” 更准确。因为变量一般指使用 var 声明的标识符,JS 里使用 function 声明的标识符也存在提升(Hoisting)。

    JS 存在变量提升,这个的设计其实是低劣的,或者是语言实现时的一个副作用。它允许变量不声明就可以访问或声明在后使用在前。新手对于此则很迷惑,甚至许多使用JS多年老手也比较迷惑。但在 ES6 加入 let/const 后,变量Hoisting 就不存在了。

    一、 变量未声明,直接使用

    1
    2
    3
    4
    function test() {
        alert(notDefined);
    }
    test(); // ?

    报错是自然的

    二. 变量声明在末尾

    1
    2
    3
    4
    5
    function test() {
      alert(declaredButNotAssigned); // undefined
      var declaredButNotAssigned;
    }
    test();

    输出 undefined, 结果比上例有所改善,没有报错,代码可以运行,但变量值可能不是程序员所期望的。

    三、 变量声明在末尾,同时给变量赋值

    1
    2
    3
    4
    5
    function test() {
        alert(declaredAndAssigned); // undefined
        var declaredAndAssigned = 1;
    }
    test();

    结果和 二 相同, 很明显,并不会因为赋值了就输出 1。

    二、三 都发生了变量提升(Hoisting),简单定义

    变量提升: 在指定作用域里,从代码顺序上看是变量先使用后声明,但运行时变量的 “可访问性” 提升到当前作用域的顶部,其值为 undefined ,没有 “可用性”。

    通常为了让大家理解起来容易些,把 三 拆成如下

    1
    2
    3
    4
    5
    function test() {
        var declaredAndAssigned;
        alert(declaredAndAssigned); // undefined
        declaredAndAssigned = 1;
    }

    即把声明和赋值分为两句。

    这里强调 “代码顺序” 和 “运行顺序”,是因为多数时候我们写的代码都是顺序执行的,即 “代码顺序” 和 “运行顺序” 是一致的。这也符合人的大脑的思维过程。比如有过 C语言 经验的程序员

    1
    2
    3
    4
    5
    #include <stdio.h>
    int main() {
        int x = 1;
        printf("%d, ", x); // 1
    }

    两句代码,先声明整数型 x, 再输出。代码顺序和运行顺序是一致的,即正常运行。

    如果顺序反过来

    1
    2
    3
    4
    5
    #include <stdio.h>
    int main() {
        printf("%d, ", x); // error
        int x = 1;
    }

    此时,编译都不能通过了。但JS里可以反过来写,见二、三。

    因此,有类 C语言 经验的程序员,都很清楚变量需要 先声明后使用,不然会报错。而到了JS里,有 变量提升 现象,可以 先使用后声明,C 的经验用到 JS 里迷惑便出现了。

    四、 函数表达式也存在变量提升

    1
    2
    3
    4
    5
    function test() {
        alert(func); // undefined
        var func = function() {};
    }
    test();

    但如果想使用这个 func,则无可能

    1
    2
    3
    4
    5
    6
    function test() {
        alert(func); // undefined
        func(); // 报异常
        var func = function() {};
    }
    test();

    结果func 是 undefined,调用 func 则会报异常。 在上面的定义中提到了 可访问性 和 可用性 对应如下语句。

    可访问性:alert(func),输出 undefined,可以运行,可以访问 func。

    可用性:   func(), 报异常,不能正常调用 func,表示无可用性。

    二、三、四 都是使用 var 声明的变量,JS 里函数声明也会存在提升,只是这个 “变量” 比较特殊,它是一个 function 类型(可以作为函数、方法或构造器)。它的名字(标识符)也会提升到当前作用域的顶部。

    五、函数声明的名也会提升到当前作用域顶部

    1
    2
    3
    4
    5
    6
    7
    8
    function test() {
        alert(f1); // function
        f1(); // "called"
        function f1() {
            alert('called');
        }
    }
    test();

    我们看到,声明 f1 在代码最末,f1 使用在前,alert(f1) 和 f1() 都正常执行,表示 可访问性 和 可用性 都有了。

    前面说了,变量提升(Hoisting)没什么用,属于语言的低劣设计,好的习惯还是 “先声明后使用”。这个特性也会出现在不少大公司面试题里

    题1:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 写出以下代码的运行结果
    var a = 1;
    function fn() {
        if (!a) {
            var a = 4;
        }
        alert(a); // 4
    }
    fn();

    题2:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 写出以下代码的运行结果
    var a = 1;
    function fn() {
        a = 2;
        return;
        function a() {}
    }
    fn();
    alert(a); // ?

    但这一切随着 ES6 的 let/const 到来结束了,ES里除全局变量外,其它都使用 let/const,var 替换成 let 后变量提升就不复存在了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function test() {
        alert(declaredButNotAssigned1); // 报异常
        alert(declaredButNotAssigned2); // 报异常
        alert(func); // 报异常
      
        let declaredButNotAssigned1;
        let declaredButNotAssigned2 = true;
        let func = function() {};
    }
    test();

    这强制程序员养成好的习惯,变量需要“先声明再使用”,否则报错。

    以下摘自MDN的关于let不在发生变量提升的描述

    In ECMAScript 6, let does not hoist the variable to the top of the block. If you reference a variable in a block before the let declaration for that variable is encountered, this results in a ReferenceError, because the variable is in a "temporal dead zone" from the start of the block until the declaration is processed.

    用 let 声明变量后,typeof 却不再安全了

    1
    2
    3
    4
    if (condition) {
        alert(typeof num); // Error!
        let num = 100;
    }

    以前可以用 typeof == 'undefined',来判断是否引入了某lib,比如jQuery

    1
    2
    3
    4
    // 判断jQuery是否引入了
    if (typeof $ !== 'undefined') {
        // do something
    }...

    jQuery没有引入,$ 没有声明,这句也不会报错而影响到下面的代码执行,但如果是 let 声明的就会报错了。

  • 相关阅读:
    jQuery选择器
    asp.net 操作 excel 出现 class 组件错误 或 打开文件错误
    [转]Win7、Windows Server 2008下无法在Windows Service中打开一个已经存在的Excel 2007文件问题的解决方案
    Microsoft Excel 不能访问文件“ 文件名称或路径不存在。 • 文件正被其他程序使用。 • 您正要保存的工作簿与当前打开的工作簿同名。
    页面打印 css
    如何在excel数据透视表的顶部显示列总计
    asp中javascript或jquery如果在body中 且需要页面元素 则需要放在最后
    sqlserver游标使用
    excel 冻结多行
    Request.Form("cardno").Item(y) 的count总是为0
  • 原文地址:https://www.cnblogs.com/yu412/p/11441158.html
Copyright © 2011-2022 走看看