JavaScript中的 var let const 有何区别

案例分析

// 案例1
console.log(name);
var name = 'xiaoming';
console.log(name);
// 案例2
console.log(name);
let name = 'xiaoming';
console.log(name);
// 案例3
console.log(name);
const name = 'xiaoming';
console.log(name);

每个案例 两个输出分别是什么?为什么?

案例1输出 undefined 和 xiaoming (第一个打印使用name时,name尚未初始化赋值,因此是undefined)

案例2和案例3均报错,因为let 和 const 没有 var 变量声明提升的优势,造成了未定义先使用的错误。

var let const 有何不同

以下是 三个关键字 在MDN 的 解释。

var :声明会提前

let :声明不会提前 限定作用域

const :声明不会提前,常量 声明时必须赋值 赋值后不可变更 不可重新声明 限定作用域

var

作用:使用var关键字来声明一个变量。在声明的同时,可以进行初始化,也可以不初始化,不初始化值为未定义undefined
重点:

Variable declarations, wherever they occur, are processed before any code is executed. The scope of a variable declared with var is its current execution context, which is either the enclosing function or, for variables declared outside any function, global. If you re-declare a JavaScript variable, it will not lose its value.
变量的声明 不管在哪里声明的,它们都早于任何代码之前执行 变量的声明操作.
使用var声明的变量其作用于在它当前的执行上下文,要么在一个封闭的函数内,或者在函数外声明的,全局的。
而且,如果你重新声明了这个变量,这个变量的值并不会丢失。

因为声明是在任何代码之前就会执行的,这就解释了 为什么 第一个案例 后声明先使用没有报错了。
当然还有更确切的解释,请接着看:

var 提升

Because variable declarations (and declarations in general) are processed before any code is executed, declaring a variable anywhere in the code is equivalent to declaring it at the top. This also means that a variable can appear to be used before it's declared. This behavior is called "hoisting", as it appears that the variable declaration is moved to the top of the function or global code.

因为声明变量是在代码执行前执行的,不管在哪里声明一个变量都等于把它声明在顶部。这就意味着一个变量可以出现在它被声明之前,这种行为叫做提升(hoisting),因为它看起来把变量声明移到了函数或者全局代码的顶部。

For that reason, it is recommended to always declare variables at the top of their scope (the top of global code and the top of function code) so it's clear which variables are function scoped (local) and which are resolved on the scope chain.
因此,推荐在变量作用域顶部(全部代码的顶部和函数代码的顶部)声明变量,这样就非常清楚 哪些变量是函数作用域 哪些变量是在作用域链上解决的。

非常重要的一点需要指出来,var 提升,会影响变量的声明,但是不影响它值的初始化,当执行到赋值表达式的时候,会去赋值。

let

作用:声明一个块作用域的局部变量,是否初始化也是可选的。
let 允许你声明一个限制在块,声明,表达式内使用的变量。它不像var关键字,var关键字定义了一个全局变量或者限定到整个函数中的变量而不管块的范围。

这里的块可能有很多含义,比如括在{}中的 从 {开始 到}结束 都可以称之为块,

范围规则
使用let声明的变量被限定在它们被定义的代码块内,在子代码块内也是一样的。在这里,let和var主要的区别在于var变量作用在整个函数,下面两个例子很好解释:

function varTest() {
  var x = 1;
  if (true) {
    var x = 2;  // same variable!
    console.log(x);  // 2
  }
  console.log(x);  // 2
}

function letTest() {
  let x = 1;
  if (true) {
    let x = 2;  // different variable
    console.log(x);  // 2
  }
  console.log(x);  // 1
}

注意:let 没有 var 提升的 优待

In ECMAScript 2015, let bindings are not subject to Variable Hoisting, which means that let declarations do not move to the top of the current execution context. Referencing the variable in the block before the initialization results in a ReferenceError (contrary to a variable declared with var, which will just have the undefined value). The variable is in a "temporal dead zone" from the start of the block until the initialization is processed.

let声明不会移动到当前执行上下文的顶部,如果在let定义前使用,会抛出错误。
这就解释了案例2中为什么会报错了

const

使用const创建一个常量,这个常量可以是全局的,也可是被声明的块内。

不像var变量,全局常量不会成为window对象的属性。

常量一旦声明必须初始化赋值,且后续不可重新声明,不可重新赋值。

const声明创建了一个值的只读引用,者并不意味着引用指向的对象是不可变的。只是这个变量标识符不能重新赋值罢了。

和 let 一样, const也没有 var提升的优势。所以 定义前就使用 也是会报错的。


延伸

首先,var是js的大大坑,为了填补var的坑,es6引入了let。

想要了解var,就要先掌握代码在处理过程是对变量的查找过程。

    console.log(name);
    var name = 'xiaoming';
    console.log(name);

从上面的代码,很直观的看出第一行输出name时,name这个变量并没有声明。如果变量没有声明,代码肯定执行会抛出异常。但上面代码并没有抛出异常,反而输出了一个‘undefined’。这是为什么?

事实上,上面的代码等价于

    var name;
    console.log(name);
    name = 'xiaoming';
    console.log(name);

通过等价代码能看出,最先声明了一个变量name,并没有赋值,没有赋值的变量默认值是'undefined'。

通过这样的等价转换是不是就明白了结果。

JS编译器在处理代码分为两个过程:编译期和执行期。

再来看源代码

    console.log(name);
    var name = 'xiaoming';
    console.log(name);

重点看第一行代码,在编译期中,第一行需要输出name,name在哪里呢?后面有一个var name,所以编译会把var name提前,紧接着是执行期,name只是声明提前了,但并没有赋值,所以输出了默认的undefined。

所以,是编译器在编译期做了一些手脚,把var变量的声明提前了。

从上面这件事我们可以引申到函数表达式定义的理解。

    fun();
    var fun = function() {
      console.log('hello, js');
    }
    fun();

以上代码在执行第一行fun()时会抛出一个错误。

    TypeError: fun is not a function
        at Object.<anonymous> (/Users/youngxu/Desktop/sample.js:1:63)
        at Module._compile (module.js:569:30)
        at Object.Module._extensions..js (module.js:580:10)
        at Module.load (module.js:503:32)
        at tryModuleLoad (module.js:466:12)
        at Function.Module._load (module.js:458:3)
        at Function.Module.runMain (module.js:605:10)
        at startup (bootstrap_node.js:158:16)
        at bootstrap_node.js:575:3

虽然fun声明被提前了,但fun的定义并没有提前,fun只是被默认赋值了一个undefined值。这又不是一个函数,此时执行fun()时就会抛出错误,编译器很明白发生了什么。为什么?因为编译器抛出的是TypeError错误呀,好好体会一些。

事实上,js对var在编译器的处理过程会带来很多问题,最好的处理原则就是先明确声明,再明确定义。所以,js在后面引入了let,let的变量并不会提前声明,没有声明的变量会明确抛出(ReferenceError: name is not defined)。所以建议以后在写js代码时,能用let就不要用var。

至于const,const是用来定义不能修改的常量的,只能有一次赋值,不能提前声明。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容

  • let 命令 块级作用域 const 命令 顶层对象的属性 global 对象 let 命令 基本用法 ES6 新...
    嘉奇呦_nice阅读 1,622评论 0 2
  • 本文属个人笔记,不做详解,仅供参考! let命令 基本用法 ES6 新增了let命令,用来声明变量。它的用法类似于...
    R_yan阅读 28,988评论 6 18
  • let 命令 块级作用域 const 命令 顶层对象的属性 global 对象 let 命令 基本用法 ES6 新...
    卞卞村长L阅读 586评论 0 0
  • let 和 const 命令 let 命令 块级作用域 const 命令 顶层对象的属性 gl...
    安小明阅读 978评论 0 0
  • 此时此刻的大同,外面在下着小雨。阴霾的天气总是给人许多的压抑,雨水滴滴答答的打在路上,不知道在什么时候,就给人一种...
    向暖而生阅读 292评论 4 2