知识点

var 声明变量:
1、存在变量提升,实际上var无论在哪里声明,都会被当做当前的作用域顶部声明变量。
2、可以重复声明,后声明的变量会覆盖前声明的变量。
let 声明变量:
1、不存在变量提升。
2、禁止重复声明。
3、块级作用域,只在当前作用域块有用。
4、临时死区,而且不能在声明之前访问它。
const声明常量:
1、const 声明的是常量,其值一旦确定后不可以修改
2、const 声明常量时候必须要进行赋值
3、const 不存在变量提升,一旦执行快外就会立即销毁。
4、const 只能在当前代码块级有效,
5、const 不能重复声明相同常量。
6、const声明不允许修改绑定,但允许修改值,也就是说用const创建对象后,可以修改该对象的属性值。

一、声明JavaScript的变量有哪些?

每种编程语言都有变量,声明变量的方法各不同,在JavaScript里面,最经典的var声明一个变量,当ECMAScript6出现后,新增了2个声明变量的方法:let和const,那何时创建变量,用什么声明变量方法会更好呢?

二、先谈谈var声明及变量提示(hoisting)机制

var声明一个变量时候,只需要 var name; 或者声明赋值var name = "Bob";

实际上var无论在哪里声明,都会被当做当前的作用域顶部声明变量。

(1)什么是变量提示机制?
// var 的变量提升机制
function getValue(condition) {
if (condition) {
var values = 'Bob';
return values; } else {
console.log(values); // 这里访问到values 是undefined,原因下面解释:
return null; }
} // 原因解释:为什么上面的代码else还能访问values的值,虽然是undefined
// 无论变量values都会被创建,在编译过程中,JavaScript引擎会将上面的getValue函数修改成这样:
function getValue(condition) { // 重点看这里,变量values的声明被提升到函数顶部
var values; if (condition) {
values = 'Bob';
return values; } else {
console.log(values); // 所以这里访问到是声明过的但未赋值的values,所以是undefined。
return null; }
}

三、块级声明的出现

块级声明用于声明在指定的块的作用域之外无法访问的变量

  • 函数内部
  • 块级中(字符{ }之间的区域)

四、let声明

let声明变量和var声明变量,但let有自己的四个特征:

  • 块级作用域,限制在当前的块级作用域中,外面作用域无法访问。
  • 不存在变量提升。
  • 临时死区,而且不能在声明之前访问它。
  • 禁止重复声明相同的变量,否则报错。

我们可以把刚才聊到的getValue函数修改一下:

// let 块级作用域 && 不存在变量提升
function getValue(condition) {
if (condition) { // 使用let声明变量
let values = 'Bob';
return values; } else {
console.log(values); // 这里报错: ReferenceError: values is not defined..
// 原因就是用let声明的变量,是不存在变量提升的,
// 而且values变量只能在if{ 这个作用块里面有效 } 外面是访问不到的
// 同时,在外面访问不仅会访问不到,而且会报错 return null; }
} // let 禁止重复声明相同变量
function getValue() {
var values = "Bob";
let values = {name: 'Bob'}; // 使用let声明变量禁止重复声明已经有的变量名
// 否则报错:SyntaxError: Identifier 'values' has already been declared
}

五、const声明

  • const 声明的是常量,其值一旦确定后不可以修改。
  • const 声明常量时候必须要进行赋值。
  • const 不存在变量提升,一旦执行快外就会立即销毁。
  • const 只能在当前代码块级有效,
  • const 不能重复声明相同常量。
  • const声明不允许修改绑定,但允许修改值,也就是说用const创建对象后,可以修改该对象的属性值。
function getValue() {
// 声明一个常量
const USER_NAME = "梁凤波"; // 禁止重复声明相同常量,否则报错:TypeError: Assignment to constant variable.
// const USER_NAME = "Bob"; // 记住:const声明不允许修改绑定,但允许修改值,
// 也就是说用const创建对象后,可以修改该对象的属性值
const STUDYENT = {
name: '梁凤波'
}; console.log(`STUDYENT.name = ${STUDYENT.name}`); // STUDYENT.name = 梁凤波 STUDYENT.name = 'Bob';
console.log(`STUDYENT.name = ${STUDYENT.name}`); // STUDYENT.name = Bob
}

拓展:循环中的块级作用域绑定

访问for循环后的结果
// 在for循环内用var 声明,在外面访问到的是for循环后的结果
for (var i = 0; i < 10; i++) {
}
console.log(`i = ${i}`); // i = 10 // 在for循环内用let 声明,在外面 访问不到,块级作用域问题
for (let i = 0; i < 10; i++) {
}
console.log(`i = ${i}`); // ReferenceError: i is not defined
循环中的var声明
// 经过for循环后,在外面访问i,是直接访问到了结果i = 10

let funcs = [];
for (var i = 0; i < 10; i++) {
funcs.push(function () {
console.log(i);
})
} funcs.forEach(func => {
func() // 分别输出10次10
});

原因:循环里每次迭代同时共享着变量i,循环内部创建的函数全保留相同变量的引用,循环结束时候i的值变为10,所以每次调用console.log(i)时候回输出数字10

为了解决这个问题,可以在循环中使用立即调用函数表达式(IIFE),以强制生成计数器变量的副本:

使用var达到理想状态
// 如果要理想效果,在外面分别输出 0 ~ 9,
// 可以使用闭包暴露出去
let funcs = [];
for (var i = 0; i < 10; i++) {
funcs.push((function (val) {
return function () {
console.log(val);
}
}(i)))
} funcs.forEach(func => {
func()
});
循环中的let声明
let funcs = [];
for (let i = 0; i < 10; i++) {
funcs.push(function () {
console.log(i);
})
} funcs.forEach(func => {
func() // 分别输出 0 ~ 9
});

let 声明模仿上述示例IIFE所做的一切简化循环过程,每次迭代循环都会创建一个新变量,并以之前迭代中同名变量的值将其初始化。

循环中的const声明
let funcs = [];
let obj = {
a: true,
b: true,
c: true
} for (const key in obj) {
funcs.push(function () {
console.log(key);
})
} funcs.forEach(func => {
func() // 分别输出 a, b, c Authorization
});

let和const声明循环,const循环是不能改变key的值,const 循环应该使用for-in,for-of,其他和let示例一样,因为每次迭代不会像var循环例子一样修改已有的绑定,而是会创建一个新绑定。

全局块级作用域绑定

var RegExp = "Bob";

// 即使是全局对象RegExp定义在window,也不能幸免被var声明覆盖
console.log(RegExp); // Bob
console.log(window.RegExp); // Bob let RegExp = "Bob"; // 用let或const声明不能覆盖全局变量,而只能屏蔽它
console.log(RegExp); // Bob
console.log(window.RegExp); // undefined
console.log(window.RegExp === RegExp); // false const ncz = 'Hi!'
console.log('ncz' in window); // false

最后聊一聊块级绑定的最佳实践

默认使用const,只在确实需求改变变量的值使用let,这样就可以在某种程度上实现代码的不可变,从而防止默写错误产生。

深入理解ES6 - var-let-const的更多相关文章

  1. ES6 var,const , let三者区别

    每天学一点,知识涨一张 var 默认是会变量提升的,变量可以修改: let 定义变量,变量可以修: const 定义必须有常量值,const的值一但写上不可更改:let 与const相同之处:1> ...

  2. 理解es6中的const与“不变”

    const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动. 效果 对于简单类型的数据(数值.字符串.布尔值),值就保存在变量指向的那个内存地址,因此等同于常量. 对于复合类型 ...

  3. ES6之let(理解闭包)和const命令

    ES6之let(理解闭包)和const命令 最近做项目的过程中,使用到了ES6,因为之前很少接触,所以使用起来还不够熟悉.因此购买了阮一峰老师的ES6标准入门,在此感谢阮一峰老师的著作. 我们知道,E ...

  4. ES6中的var let const应如何选择

    javascript世界里面的每个人都在说有关ECMAScript 6 (ES6,也称作ES 2015)的话题,对象的巨大变化 ( 类 , super() , 等), 函数 (默认参数等), 以及模块 ...

  5. ES6和ES5变量声明的区别(var let const)

    // es5的语法与es6的语法区别 // var let const console.log(name);//undefine,不会报错,因为变量声明会提到作用域的最前面 var name=&quo ...

  6. es6(var,let,const,set,map,Array.from())

    1.变量声明--var,const,let 1.1 var - (全局作用域,局部作用域)会有变量提升 //第一个小例子 <script> var num = 123; function ...

  7. let、var、const声明的区别

    前言 看了方应杭老师的一篇解释let的文章,对JavaScript中的声明有了深刻的理解,这里也就有了总结一下JavaScript中各种声明之间区别的这篇文章. JavaScript中变量声明机制 首 ...

  8. javascript精雕细琢(一):var let const function声明的区别

    目录 引言 一.var 二.let 三.const 四.function 五.总结 引言        在学习javascript的过程中,变量是无时无刻不在使用的.那么相对应的,变量声明方法也如是. ...

  9. var和const和let的区别

    简述: 1.前端的变量申明,可以用到var,ES6的const(衡量)/let(变量) 2.在ES5用的都是var,到ES6之后,也就是2015年开始出现const/let. var 不会报错,有声明 ...

  10. let、var、const区别(表格比较)

    let.var.const区别(表格比较): 区别项 let var const 作用域 块级作用域 全局作用域或函数作用域 块级作用域 是否有变量提升 无 有 无 是否可重复声明 不可 可以 不可 ...

随机推荐

  1. delphi之IOCP学习(一)

    困扰已久的网络通信(IOCP:完成端口),今天终于揭开她的神秘面纱了,之前百度N久还是未能理解IOCP,网络上好多博文都没有贴出源码,初学者很难正在理解IOCP并自己写出通信例子 ,经过努力,今天自己 ...

  2. message contains no documents code:13066 mongdb数据库报的错误

    message contains no documents  code:13066stackoverflow上面的回答是: What version of the C# driver are you ...

  3. C++中的模板编程

    一,函数模板 1.函数模板的概念 C++中提供了函数模板,所谓函数模板,实际上是建立一个通用函数,其函数的返回值类型和函数的参数类型不具体指定,用一个虚拟的类型来表示.这个通用函数就被称为函数的模板. ...

  4. 概率论经典问题 —— 三个事件 A、B、C 独立 ≠ 三个事件两两独立

    三个事件 A.B.C 相互独立?三个事件两两独立? A:第一次正面朝上: B:第二次正面朝上: C:第一次和第二次结果不同: P(AB)=P(A)P(B): P(AC)=1/4=P(A)P(C)(不是 ...

  5. 让你编写的控件库在 XAML 中有一个统一的漂亮的命名空间(xmlns)和命名空间前缀

    原文 让你编写的控件库在 XAML 中有一个统一的漂亮的命名空间(xmlns)和命名空间前缀 在 WPF XAML 中使用自己定义的控件时,想必大家都能在 XAML 中编写出这个控件的命名空间了.然而 ...

  6. Erlang实现进程池

    开发工作中,经常会碰到进程池或者线程池,或者其它的资源池.在这里,用erlang实现一个简单的进程池. erlang进程是非常轻量级的,这个进程池的主要目的是用一种通用的方式去管理和限制系统中运行的资 ...

  7. 一个Java工程师的入门级Linux命令集

    0.前言    网上介绍linux的命令的文章一大堆,但是大部分都是流于命令介绍,把命令的所有参数都介绍一遍,但是其实在真正的工作中,很多参数都不会用到.本文总结了我自己常用的一些命令,这些命令都比较 ...

  8. MIPS之路在何方?

    目前市场上还有谁想要MIPS?MIPS接下来将何去何从?如果有一家公司希望能好好地经营MIPS,应该用什么策略呢?   MIPS仍然有营收来源.它还拥有ARM所没有的多执行绪技术.有人说,只要想到半导 ...

  9. c#扩展方法简单

    扩展方法 怎样知道的. 这还得从项目的本身说起.该项目是一套的微软底层架构上搭建起来的. 全部的框架以及控件的封装,数据的传递方法都是总体的框架封装好的. 对经常使用的dropwodnlist控件的数 ...

  10. (转)移动端自定义返回上一页的方法:history

    在实际的应用中,我们常常需要实现在移动app和浏览器中点击返回.后退.上一页等按钮实现自己的关闭页面.调整到指定页面或执行一些其它操作的需求. 那在代码中怎样监听当点击微信.支付宝.百度糯米.百度钱包 ...