JaneChelle | Blog JaneChelle | Blog

执着于理想,纯粹于当下

目录
ES6入门-var、let、const和块级作用域
/  

ES6入门-var、let、const和块级作用域

  今天又开始学习ES6,光看会混淆,于是决定做笔记可以疏通一下自己的思路,又便于以后的巩固,与大家共勉😉😉。

一、var、let和const的区别与详解

  ES5 只有两种声明变量的方法:var命令和function命令。ES6 除了添加let和const命令,还有另外两种声明变量的方法:import命令和class命令。所以,ES6 一共有 6 种声明变量的方法。
  今天我只学习了var、let和const,所以只记录这三个。

  1. var、let声明变量,const声明常量
  2. var有变量提升,且当var是全局变量时,属于顶层对象属性;let、const不存在变量提升,不可重复声明变量,声明的变量只在它所在的代码块有效,不属于顶层对象属性

举例:
let vs var:

(1) let变量只在他所在的代码块内有有效

{
 let a = 10;
 var b = 1;
}
a // ReferenceError: a is not defined.
b //1

(2)for循环的计数器,就很合适使用let命令。

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

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

解释:
  在第一个中,变量i是var变量声明的,i在全局范围内都有效,也就是说,全局只有一个变量i,只不过i是动态变化的,每循环一次,i的值就变一次。所有数组a的成员里面的i,指向的都是同一个i,导致运行时输出的是最后一轮的i的值,也就是 10。
  在第二个中,变量i是let声明的,当前的i只在本轮循环有效,也就是说,每次循环的i都是新的变量。Javascript内部引擎会记住上一轮i的值,初始化本轮i的值,就在上轮的基础上进行计算。

(3)循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域

for (let i = 0; i < 3; i++) {
  console.log(i);
}
// 0 1 2
for (let i = 0; i < 3; i++) {
  let i = 'abc';
  console.log(i);
}
//?

  大家觉得打印出来的结果会是什么?0,1,2 还是 三个abc
  答案是,输出了三次 abc.这表明函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域

(4)不存在变量提升
  var命令会发生“变量提升”现象,即变量可以在声明之前使用,值为undefined
let命令改变了语法行为,它所声明的变量一定要在声明后使用,否则报错。

// var 的情况
console.log(foo); // 输出undefined
var foo = 2;

// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;

(5)暂时性死区

var tmp = 123;

if (true) {
  tmp = 'abc'; // ReferenceError
  let tmp;
}

解释:
  虽然var声明了全局变量 temp,但是在代码块内又声明了let型的局部变量temp,let型的局部变量temp绑定了这个代码块,导致在let声明变量前对temp赋值会报错。

(6)不允许重复声明

// 报错
function func() {
  let a = 10;
  var a = 1;
}

// 报错
function func() {
  let a = 10;
  let a = 1;
}
----------------------------------------
function func(arg) {
  let arg;
}
func() // 报错

function func(arg) {
  {
    let arg;
  }
}
func() // 不报错

const:
(1)const声明一个只读的常量。一旦声明,常量的值就不能改变。

const PI = 3.1415;
PI // 3.1415

PI = 3;
// TypeError: Assignment to constant variable.

(2)const的作用域与let命令相同:只在声明所在的块级作用域内有效。
(3)const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用
本质:

const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。

const a = [];
a.push('Hello'); // 可执行
a.length = 0;    // 可执行
a = ['Dave'];    // 报错

  声明了数组常量a,这个数组本身是可写的,但是如果将另一个数组赋值给a就会报错。

二、顶层对象

(1)顶层对象,在浏览器环境指的是window对象,在 Node 指的是global对象。ES5 之中,顶层对象的属性与全局变量是等价的。
(2)为了保持兼容性,ES6中,var命令和function命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。也就是说,从 ES6 开始,全局变量将逐步与顶层对象的属性脱钩。
(3)在语言标准的层面,引入globalThis作为顶层对象。也就是说,任何环境下,globalThis都是存在的,都可以从它拿到顶层对象,指向全局环境下的this。

三、块级作用域

(1)允许块级作用域的任意嵌套

{{{{
  {let insane = 'Hello World'}
  console.log(insane); // 报错
}}}};

解释:
  上面代码使用了一个五层的块级作用域,每一层都是一个单独的作用域。第四层作用域无法读取第五层作用域的内部变量
(2)内层作用域可以定义外层作用域的同名变量

{{{{
  let insane = 'Hello World';
  {let insane = 'Hello World'}
}}}};

(3)块级作用域的出现,实际上使得获得广泛应用的匿名立即执行函数表达式(匿名 IIFE)不再必要了

// IIFE 写法
(function () {
  var tmp = ...;
  ...
}());

// 块级作用域写法
{
  let tmp = ...;
  ...
}

(4)块级作用域与函数声明
1.ES5 规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域声明
2.ES6 规定,块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用
3.ES6 的块级作用域必须有大括号,如果没有大括号,JavaScript 引擎就认为不存在块级作用域  


标题:ES6入门-var、let、const和块级作用域
作者:JaneChelle
地址:https://xiao.algerfan.cn/articles/2019/08/21/1566347372067.html