菜单

JavaScript中变量提高是语言设计缺欠【www.2138.com】

2020年3月1日 - www.2138.com
JavaScript中变量提高是语言设计缺欠【www.2138.com】

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

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

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

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

function test;}test(); // ?

 

二. 变量声明在末尾

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

function test() { alert(declaredButNotAssigned); // undefined var declaredButNotAssigned;}test();
function test() {
    alert(notDefined);
}
test(); // ?

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

报错是自然的

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

www.2138.com 1

function test() { alert; // undefined var declaredAndAssigned = 1;}test();

 

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

二. 变量声明在末尾

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

function test() {
    alert(declaredButNotAssigned); // undefined
    var declaredButNotAssigned;
}
test();

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

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

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

 

#include int main() { int x = 1; printf; // 1}

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

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

function test() {
    alert(declaredAndAssigned); // undefined
    var declaredAndAssigned = 1;
}
test();
#include int main; // error int x = 1;}

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

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

 

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

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

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

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

function test; // undefined var func = function;

JavaScript中变量提高是语言设计缺欠【www.2138.com】。 

www.2138.com,但如果想使用这个 func,则无可能

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

function test; // undefined func(); // 报异常 var func = function;
function test() {
    var declaredAndAssigned;
    alert(declaredAndAssigned); // undefined
    declaredAndAssigned = 1;
}

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

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

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

 

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

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

二、三、四 都是使用 var 声明的变量,JS 里函数声明也会存在提升,只是这个
“变量” 比较特殊,它是一个 function
类型。它的名字也会提升到当前作用域的顶部。

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

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

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

function test; // function f1(); // "called" function f1; }}test();

 

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

如果顺序反过来

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

#include <stdio.h>
int main() {
    printf("%d, ", x); // error
    int x = 1;
}
// 写出以下代码的运行结果var a = 1;function fn { var a = 2; } alert;

// 写出以下代码的运行结果var a = 1;function fn() { a = 2; return; function a; // ?

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

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

 

function test() { alert(declaredButNotAssigned1); // 报异常 alert(declaredButNotAssigned2); // 报异常 alert; // 报异常 let declaredButNotAssigned1; let declaredButNotAssigned2 = true; let func = function;

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

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

 

以下摘自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.

function test() {
    alert(func); // undefined
    var func = function() {};
}
test();

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

 

if  { alert; // Error! let num = 100;} 

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

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

function test() {
    alert(func); // undefined
    func(); // 报异常
    var func = function() {};
}
test();
// 判断jQuery是否引入了if (typeof $ !== 'undefined') { // do something}...

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

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

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

以上所述就是本文的全部内容了,希望大家能够喜欢。

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

 

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

 

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

function test() {
    alert(f1); // function
    f1(); // "called"
    function f1() {
        alert('called');
    }
}
test();

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

 

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

题1:

// 写出以下代码的运行结果
var a = 1;
function fn() {
    if (!a) {
        var a = 2;
    }
    alert(a); // ?
}
fn();

 

题2:

// 写出以下代码的运行结果
var a = 1;
function fn() {
    a = 2;
    return;
    function a() {}
}
fn();
alert(a); // ?

 

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

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 却不再安全了

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

 

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

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

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

 

相关:

Temporal_dead_zone_and_errors_with_let

why-typeof-is-no-longer-safe

JavaScript判断变量是否为undefined两种方式差异

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图