1. let要好好用

1. 基本用法

let命令用于声明变量,但是在所声明的变量具有块级作用域的特性,只在let命令所在的代码块中有效。

先看下面这段代码输出什么:

var a = [];
for (var i = 0; i < 10; i++){
a[i] = function () {
console.log(i);
};
}
a[8]();

程序的输出结果是10而不是8,因为i是全局变量,执行完for循环后,i的值变为10,a数组中存放的是console.log(i)这段代码,在调用 a[8]()i 的值 为10,故最终输出10.

若使用let上述代码的输出会变成什么呢?

var a = [];
for (let i = 0; i < 10; i++){
a[i] = function () {
console.log(i);
};
}
a[8]();

程序最终输出8,因为使用let声明变量 i 后,每一次循环的的 i 实际上都是一个新的变量,a[6]a[8] 对应的 i 并不是同一个 iJavaScript 引擎会记住当前的 i 值,在进行下一个 i 值执行 i++ 时会在当前的 i 的基础上进行计算。

for (let i =0; i < 3; i++){
let i = 'abc';
console.log(i);
}
//程序输出
'abc'
'abc'
'abc'

可以看到内部的 i 并没有覆盖掉外部for循环中的 i ,两个变量虽然同名但是两个完全不同的变量,两者互不影响,并不存在覆盖的情况。它们有各自单独的作用域。

2. let声明的变量不存在变量提升

var 命令声明的变量是有变量提升的,但是 let 命令声明的变量不存在变量提升

看下面的例子:

// var
consol.log(i); // 输出undefined
var i = 3; // let
console.log(i); // ReferenceError
let i = 3;

使用 var 命令时,在程序预解析阶段,js 解释器遇到 var 命令时会将后面变量进行提前声明,但是注意并不会赋值,而 let 命令并不会进行提前声明,故会报ReferenceError 错误。

3. TDZ(temporal dead zone)暂时性死区

ES6明确规定,如果在某个区块中存在 letconst 命令,那么这个区块中对用 letconst 命令声明的变量从一开始就形成封闭作用域,只要在声明之前使用这些变量,就会报错。

也就是说在这个区块中用这两个命令声明的变量的作用域链中只有当前作用域,只在当前作用域中起作用,在其它作用域中重名的变量和它们没有半毛钱的关系,并不会形成覆盖等作用,就像第一条基本用法中第三段代码,变量 i 并不会进行覆盖。

var tmp = 123;
if (true) {
tmp = 'abc';
let tmp;
console.log(tmp);
}

运行结果截图:

在使用 let 声明之前就执行tep = 'abc'; 使用了变量 tmp 导致出现引用错误。

再看一例:

var tmp = 123;
if (true) {
let = tmp;
console.log(tmp);
}

运行结果截图:

if 语句块内的 tmp 变量和外面的 tmp 变量并不是一个变量,它们两个没有半毛钱的关系,在 if 语句块内声明了 tmp 但是并没有赋值,故输出 undefined.

ES6规定暂时性死区和let,const不出现变量提升,主要是为了减少运行时的错误,在没有声明变量之前就使用它,一是不符合正常的思维逻辑,二是会引发一些意料之外的行为。

因此,平时在写代码时要养成先声明后使用的编程习惯!

4. 不允许重复声明

ES6规定:let 不允许在相同的作用域内重复声明同一个变量

下面的代码都是错误的:

// 报错
function () {
var i;
let i;
}
// 报错
function () {
let a = 1;
let a = 2;
}
// 报错
function f (arg) {
let arg;
}
// 不报错
function f (arg) {
{
let arg;
}
}

前面两种很好理解,第三种,我们知道在 js 中函数的形参实际上就是函数内部的一个局部变量,function f(arg) 就相当于 function f (var arg), 所以实际上和前面两种情况一样。

第四种由于在函数内部又新建了一个区块,故两个变量是存在于不同的区块中,故不会报错。

let 声明的变量只在当前的区块中有效,并且在当前区块中只能有一个这样的变量,不能出现同名的变量

2. 块级作用域

1. 为什么需要块级作用域

两段代码来说明原因:

例1(内层变量覆盖外层变量):

// 覆盖
var tmp = new Date(); function f () {
console.log(tmp);
if (false) {
var tmp = 'hello';
}
} f(); // 输出 undefined

上述代码相当于:

// 覆盖
var tmp = new Date(); function f () {
var tmp; // 变量提升
console.log(tmp);
if (false) {
var tmp = 'hello';
}
} f(); // 输出 undefined

在预解析阶段,由于变量提升,在函数 f 内部,变量 tmp 被提升到函数 f 作用域的头部,但是并未赋值,赋值是在执行阶段进行的。

例2(循环计数变量变为全局变量):

for (var i = 0; i < 10; i++) {
console.log(i);
}
console.log(i); // 输出10

在其它具有块级作用域的语言中,在 for 循环执行完毕后,变量 i 就被销毁了,但是在 js 中,变量 i 并没有被销毁,反而变成了全局变量。

2. ES6中实现了块级作用域

ES6中块级作用是利用 let 命令实现的,单纯写一对大括号,并不是块级作用域

// 没有用let命令
{
var tmp =2
}
console.log(tmp); // 输出2

上面的代码并没有利用 let 命令,tmp 是一个全局变量。

// 使用let命令
{
let tmp =2
}
console.log(tmp); // undefined

运行结果:

tmp是一个局部变量,在外部无法访问内部的变量,故输出undefined。

块级作用域的出现使得之前广泛使用的立即执行匿名函数(IIFE)的写法变得不再必要

3. 块级作用域与函数声明

ES5规定:函数只能在顶层作用域和函数作用域中声明,不能在块级作用域中声明

ES6规定:允许在块级作用域中声明函数,在块级作用域中,函数声明语句的行为类似于 let 在块级作用域之外不可引用

ES6环境浏览器行为方式:

  • 允许在块级作用域内声明函数
  • 函数声明类似于 var ,即会提升到全局作用域或函数作用域的头部
  • 同时,函数声明还会提升到所在的块级作用域的头部

可以看到ES6规定和浏览器的实现是不一致的,这是为了降低对旧代码产生的影响。

下面举例来说明一下:

例:

function f () {
console.log("I am outside");
}
(function () {
if (false) {
function f() {
console.log("I am inside");
}
}
f();
}());
// 输出结果:
// "I am inside"

ES5环境下,以上代码相当于:

// ES5环境
function f () {
console.log("I am outside");
}
(function () {
function f() {
console.log("I am inside"); // 函数f声明提前,注意在ES5下函数体也提前了
}
if (false) {
}
f();
}());
// 输出结果:
// "I am inside"

在IE浏览器下调试时,IE10 及以下浏览器版本应该都是 ES5 环境,因为运行上述代码时结果与理论上一致,而在 edge 版本上运行时,却发生引用错误和 ES6 环境理论上一致。

ES6环境下,以上代码相当于:

// ES6环境
function f () {
console.log("I am outside");
}
(function () {
var f = undefined; // 在ES6环境下同样也是提前了,但是函数体并没有提前
if (false) {
function f() {
console.log("I am inside"); // 函数f声明提前,注意在ES5下函数体也提前了
}
}
f();
}());

运行结果:

chrome,IE edge ,Firefox下和上述结果是一致的,应该是 ES6 环境。

无论支不支持在块级作用域中声明函数,这种行为是不好的,即使真的需要,也应该在块级作用域中写成函数表达式的形式

4. do表达式(仅仅是提案)

do 表达式解决的是在块级作用域不能返回值的问题

do 表达式的用法如下:

let x = do {
let t = 3;
t+1;
};

如果浏览器能实现的话, x 的值应该为块级作用域的返回值 t+1 为4.

3. const(不要忘记立即初始化哦)

1. 基本用法

const 声明的是一个只读的常量,一旦声明,常量的值就不能改变

const PI = 3.1415929;
pi = 2; // 报错,不能够给常量再赋值

const 一旦声明常量,就必须立即初始化,否则也会报错

const PI; // SyntaxError:Misssing initializer in const declaration

const 的作用域与 let 命令相同,也是只在声明所在的块级作用域中生效

if (true) {
const MAX = 5;
}
console.log(MAX); // Uncaught ReferenceError MAX is not defined

const 命令声明的常量不会提升,同样存在暂时性死区

if (true) {
console.log(MAX); // ReferenceError,必须在变量使用之前声明它
const MAX = 5;
}

**使用const 声明的常量与let 一样不可进行重复声明 **

var a = 1;
const a = 1; // 报错,在同一个块级作用域内不能声明两个同名的变量(常量)

2. const的本质

const 实际上保证的并不是变量的值不得改动,而是变量指向的那个内存地址不得改动,对于简单数据类型而言,值就保存在变量指向的内存地址中,内存地址不改变,值自然也就不会改变。但是对于复合类型的数据类型而言,变量指向的内存地址中保存的是一个指针,这个指针指向的内存空间中保存的是这个复合数据类型的值。我们对象是可变的数据类型,故地址不变,对象的内容实际上是可以改变的。

举例说明:

const obj = {};
// 为对象添加一个属性
obj.prop = 1; // 添加成功
// 令obj指向另一个对象
obj = {}; // 报错

obj 指向另一个对象时,由于地址发生了改变,会改变 obj 的值,故会报错。

如果真的想将对象冻结,可以使用 Object.freeze 方法

const foo = Object.freeze({});
// 常规模式下,下面一行不起作用
// 严格模式下,下面一行会报错
foo.prop = 123;

常量 foo 指向了一个冻结的对象,所以添加新属性时不起作用,严格模式下还会报错。

4. ES6中声明变量的六种方法

  • var
  • function
  • let
  • const
  • import
  • class

5. ES6对顶层对象属性的改变

1. ES5中顶层对象的属性

顶层对象在浏览器环境中指的是 window 对象,在 Node 环境中指的是 global 对象。在 ES5 中顶层对象的属性与全局变量是等价的。

window.a = 2;
console.log(a) // 输出2

顶层对象的属性与全局变量相关带来的问题:

  • 无法在编译时就提示变量未声明的错误,只有在运行时才能知道(因为全局变量有可能是顶层对象的属性创造的,而属性的创造是动态的)
  • 程序员不知不觉就会创建全局变量(比如打字出错)
  • 顶层对象的属性是到处都可以读写的,非常不利于模块化编程

2. ES6中顶层对象的属性

1. 为了保持兼容性,varfunction 命令声明的全局变量依然是顶层对象的属性

2. let,const,class 命令声明的全局变量不属于顶层对象的属性

var a = 1;
console.log(window.a); // 1
let b = 2;
console.log(window.b); // undefined

let 声明的变量 b 并不是顶层对象的属性,故会输出 undefined

ES6 中 let 和 const 总结的更多相关文章

  1. es6 中的let,const

    在es6中,let的作用和var差不多,都是用来声明变量的,但是他们之间的区别在于作用域不同,大家都知道在js中没有块级作用域,例如: for(var i=0;i<10;i++){ consol ...

  2. ES6中var/let/const的区别

    let的含义及let与var的区别: let 声明的变量只在它所在的代码块有效: 如下: for (let i = 0; i < 10; i++) { console.log(i); } con ...

  3. ES6 中 let and const

    let 和 const 命令 let 命令 基本用法 ES6 新增了let命令,用来声明变量.它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效. { let a = 10; v ...

  4. ES6中let与const命令详解

    阮一峰ES6入门 let 作用域 let命令用来声明变量,但声明的变量只在let命令所在的代码块内有效. { let a = 10; var b = 1; } a // ReferenceError: ...

  5. ES6中let、const和var的区别

    一.let 1.基本用法 ES6 新增了let命令,用来声明变量. let 的用法类似于 var,但所声明的变量只在 let 命令所在的代码块内有效(一个“{}”相当于一个代码块) { let a = ...

  6. 浅谈ES6——ES6中let、const、var三者的区别

    在了解let.const.var的区别之前,先了解一些什么是es6 Es6 全称ECMAscript 是JavaScript语言的一个标准,其实Es6本质就是JavaScript的一个版本,为什么叫E ...

  7. ES6中let和const详解

    let和var一样也是用来定义变量,不同之处在于let是块级作用域,只在所定义的块级作用域中生效,一个花括号便是一个块级作用域 {var a="我是var定义的";let b=&q ...

  8. ES6中的关键字 - const

    const 关键字 1.声明后的值不可以修改: const name = "小康哥"; name = "小康"; // 报错,const为constant的缩写 ...

  9. Es6中let与const的区别:(神奇的块级作用域)

    所谓的块级作用域:形成一个暂时性的死区:{    } 一.共同点: a:都是用来声明变量: b:都能形成一个块级作用域: c:都只能在声明变量的块级作用域里面有效: 二.不同点: 1.let: a:在 ...

随机推荐

  1. 005_硬件基础电路_PCB安规设计规范

    包含两个文件:讲解pcb绘制过程中的安规要求 002_2_PCB安规设计规范(原创-绝对经典全面-玩转高压PCB设计)总结 002_3_电气间隙和爬电距离规定 链接:https://pan.baidu ...

  2. java后台防止XSS的脚本攻击

    import java.util.regex.Pattern; //具体过滤关键字符public class XSSUtil { private static Pattern[] patterns = ...

  3. Mongodb 分片 手动维护chunk

    去年的笔记 For instance, if a chunk represents a single shard key value, then MongoDB cannot split the ch ...

  4. 使用webuploader实现断点续传

    核心原理: 该项目核心就是文件分块上传.前后端要高度配合,需要双方约定好一些数据,才能完成大文件分块,我们在项目中要重点解决的以下问题. * 如何分片: * 如何合成一个文件: * 中断了从哪个分片开 ...

  5. 2019ICPC徐州自我反省及未来打算

    徐州站结束了有好几天了,然而为了热爱的网络课(qdu-zpj网络课你值得信赖),一直没时间写个博客,今天又来说点心里话 今年的ICPC,就这样都打完了,可惜最终也是没能拿金,不过拿到了块银,也算保底吧 ...

  6. angular2-cli 安装

    1.如果你之前安装失败过,最好在安装angular-cli之前先卸载干净,用以下两句: npm uninstall -g angular-cli npm cache clean  2.设置淘宝镜像,国 ...

  7. NCNN使用总结

    目录 NCNN简介 NCNN注意事项 NCNN使用心得 小技巧 小想法 NCNN简介 ncnn 是一个为手机端极致优化的高性能神经网络前向计算框架.ncnn 从设计之初深刻考虑手机端的部署和使用.无第 ...

  8. manjaro 滚动更新后无法开机,Failed to start load kernel modules,nvidia驱动导致

    今天滚动后无法开机,启动时显示Faild to start load kernel modules,卡在后面无法进入登录界面 systemctl status systemd-modules-load ...

  9. 卸载ros

    #卸载 ros sudo apt-get autoremove ros-*

  10. meshing-缺角正方体

    原视频下载地址:https://yunpan.cn/cqLyi92H5Akh5  访问密码 1d48