hzzzzzzzq / Blog

这是我记录博客的地方,希望能多写一些技术博客,JS、ES、React、Vue等
14 stars 0 forks source link

ES 6 系列 - 2. let 和 const 命令 #3

Open hzzzzzzzq opened 2 years ago

hzzzzzzzq commented 2 years ago

Let 和 const 命令


let 命令

简单实用

ES6 中新增了 let 命令,用来声明变量。使用方式同 var 命令,但是 let 命令只在所在的代码块内有效。

{
  let a = 10;
  var b = 10
}
console.log(a); // 报错,a 为定义 ReferenceError: a is not defined
console.log(b); // 10

在上面的使用案例中,我们可以看出 let 命令只在代码块中生效,而在代码块以外使用则无效。

for循环中使用

我们来看看在 for 循环中使用 var 命令.

下面这段代码,打印出来的会是什么呢?可以跑一下代码,看一下控制台。

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

好了,你会发现,所有的打印都是 5,为什么呢?是因为 var 命令是在全局范围内有效,所以全局只有一个变量 i,每一次循环,变量 i 的值都会发生改变,所以当你打印是,打印的就是全局的 i ,也就是经过多次变化赋值的 5

上面代码中,我在外面打印了一下 i ,来证明 var 定义的是一个全局变量。

来,让我们将 var 改成 let,现在打印的结果又是什么呢?

for (let i = 0; i < 5; i++) {
  console.log(i);
}
console.log(i); // ReferenceError: i is not defined

当然,我想很多人都猜到了结果,就是 0 1 2 3 4 ,那么这里又是为什么呢?

let 变量只在所在的代码块内有效,在子代码块中依然有效。在上述循环中 i 只在本轮循环内有效,所以每一次循环的 i 其实都是一个新定义的变量,所以会输出当前 i 的值,每次循环对应输出。

当然,这里可能会出现一个问题,每轮的变量都是一个新变量,那它是怎么知道上一轮循环的值的?稍做解释:JS 引擎(v8)内部会记住上一轮循环的值,初始化本轮的变量时,就在上一轮循环的基础上进行计算。

上面我们说,let 命令在子代码块中也有效,可以从 for 循环上来看,() 内部其实就是一个父级作用域,而 {} 内部就是一个单独的子作用域。

不存在变量提升

变量提升,即变量可以在声明之前使用,值为 undefinedvar 命令存在,而 let 命令并没有变量提升,照样,我们来看看使用的案例。

console.log(a); // undefined
var a = 10;

console.log(b); // ReferenceError
let b = 10;

可以在控制台中跑一下这段代码,进行尝试。你会发现,a 变量进行了变量提升,而 b 变量没有发生,这表明了在声明之前使用,变量 b 是不存在的。

暂时性死区

代码块中,使用 let 命令声明变量之前,该变量都是不可用的

在外部有一个全局变量 x,但是在内部使用 let 声明了一个 x,于是内部的 x 绑定了这个代码块,不受部影响。

但是在内部,声明变量之前的这个区域就是暂时性死区,我们来从下面的代码来看。

var x = 100;
if (true) {
  // 暂时性死区 开始点
  x = 10; // 报错
  console.log(x); // 报错
  let x; // 暂时性死区结束点
  console.log(X); // undefined

  x = 20;
  console.log(x); // 20
}

暂时性死区的存在,也就意味着 typeof 的操作会变的不安全,在没有声明变量时,使用 typeof 并不会报错,返回一个 undefined,然而因为暂时性死区,会使得 typeof 报错 ReferenceError

if (true) {
  console.log(typeof x); // undefined
  console.log(typeof y); // ReferenceError
  let y;
}

函数默认值中也存在暂时性死区,而且会不容易发现。

function add(x = y, y = 1) {
    return x + y;
}
add(); // ReferenceError

在上面的代码中,就是如此,给函数赋默认值时,使用后面的值赋值给前面的 x,而这时候 y 还没有定义,所以会报错。

不允许重复声明

var 变量是允许进行重复声明的,但是 let 变量是不允许的。

来,上代码。

var a = 10;
var a = 20;
console.log(a); // 20

var b = 10;
let b = 20;
console.log(b); // 20

let c = 10;
var c = 20;
// 报错

let d = 10;
let d = 20;
// 报错

const 命令

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

基本用法

const PI = 3.14159
console.log(PI); // 3.14159

PI = 3.14 // TypeError: Assignment to constant variable

const 声明时,必须立即进行初始化,不能进行赋值。

cosnt PI; // 报错 SyntaxError

const 只声明,不赋值就会报错。

const 作用域与 let 相同:只在声明所在的块级作用域内有效。

{
  const PI = 3.14159
}
console.log(PI); //Uncaught ReferenceError

const 命令也不存在变量提升,但是存在暂时性死区,只能在声明后使用。

let 一样,不可进行重复声明。

本质

首先,我们来看一个对象的例子:

const obj = {}
obj.prop = 1;
console.log(obj.prop) // 1

obj = {} // TypeError

由上面可以看出,常量 obj 储存的是一个地址,这个地址指向一个对象。不可变的只是这个地址,即不能把 obj 指向另一个地址,但对象本身是可变的,所以依然可以添加新属性。

再来看一个数组的例子:

const arr = []
arr.push(1);
console.log(arr) // 1

arr = []

同对象,常量 arr 是一个数组,这个数组本身是可写的,但是如果将另一个数组赋值给 arr,就会报错。

由此可见