您的位置:首页 > Web前端 > JavaScript

JavaScript中ES6语法Let,Const变量定义解析

2020-08-25 20:41 791 查看

一、变量提升(变量预解析)

这是我们首先要了解的,也是必须要清楚的一个概念,那到底什么是变量提升呢?

JavaScript 代码是由浏览器中的 JavaScript 解析器来执行的。JavaScript 解析器在运行 JavaScript 代码的 时候分为两步:预解析和代码执行
l 预解析:在当前作用域下, JS 代码执行之前,浏览器会默认把带有 var 和 function 声明的变量在内存中 进行提前声明或者定义。
l 代码执行: 从上到下执行JS语句。

JS代码运行的时候确实是一行一行执行的,但是在实行前会有一个预解析的过程,相当于把所有的代码先全部扫描一遍,把带有 var 和 function 声明的变量先存在内存中,也可以说提到整体的代码前面去。
学习预解析能够让我们知道为什么在变量声明之前 访问变量的值是 undefined,为什么在函数声明之前就可以调用函数。

我们来看一段简单的代码

console.log(num); // 结果是多少?
var num = 10; // ?

答案很简单是undefined
为什么?

1变量提升

变量的声明会被提升到当前作用域的最上面,变量的赋值不会提升。

所以我们来看一下这段代码的解析过程

其实相当于
Var num;
Log(num);
num = 10;

2函数提升

函数的声明会被提升到当前作用域的最上面,但是不会调用函数。

fn();
function fn() {
console.log('123');
}

答案:123

3相关例题

接着我们看几个相关案例题

案例一

结果是几?

var num = 10;
function fn(){
console.log(num);
var num = 20;
console.log(num);
}
fn();

undefined
20

案例二

结果是几?

var a = 18;
f1();
function f1() {
var b = 9;
console.log(a);
console.log(b);
var a = '123';
}

undefined
9

案例三

结果是几?

f1();
console.log(c);
console.log(b);
console.log(a);
function f1() {
var a = b = c = 9;
console.log(a);
console.log(b);
console.log(c);
}

9 9 9 9 9 报错

这里提一下:var a = b = c = 9; 这句话的本质上是这样的 Var a=9,b=9,c=9 注意 只有a是用了var的 ,所以a是局部变量,b c是全局变量

如果要集体申明应该是var a=9,b=9,c=9。但是其实现在实际开发中var用的很少。

二、Let

ES6中新增的用于声明变量的关键字。
特点:
  A、不会进行预解析,结果会报错
  B、与forEach()中的变量类似,每次执行都会定义一个互相之间不影响的新变量
  C、不能重复定义变量名称,一个变量名称只能定义一次
  D、定义的变量如果在中,则只能在中被执行调用,其他位置都不可以。所以在不同中,定义的变量名称是可以重复之用的
  E、在循环中最好只用let定义

  • let声明的变量只在所处于的块级有效

注意:使用let关键字声明的变量才具有块级作用域,使用var声明的变量不具备块级作用域特性。

if (true) {
let a = 10;
}
console.log(a) // a is not defined
  • 不存在变量提升
console.log(a); // a is not defined 注意这个地方是会报错的,而不是undefined
let a = 20;
  • 暂时性死区
    Let会使当前的作用域形成块级作用域,也就是说当if里面使用了let后, 括号里面的tmp和外面申明的tmp完全没有关系了,这也叫暂时性死区
var tmp = 123;
if (true) {
tmp = 'abc'; // tmp is not defined
let tmp;
}

let经典面试题

var arr = [];
for (var i = 0; i < 2; i++) {
arr[i] = function () {
console.log(i);
}
}
arr[0]();
arr[1]();

答案:2 2

此题的关键点在于变量i是全局的,函数执行时输出的都是全局作用域下的i值。 打印i是会向上查找i的申明,但是只查到了循环体中的全局申明,所以i是全局变量 当下面数组调用函数时,for循环早就遍历完了,此时的i已经是2了

let经典面试题

let arr = [];
for (let i = 0; i < 2; i++) {
arr[i] = function () {
console.log(i);
}
}
arr[0]();
arr[1]();

此题的关键点在于每次循环都会产生一个块级作用域,每个块级作用域中的变量都是不同的, 函数执行时输出的是自己上一级(循环产生的块级作用域)作用域下的i值

每个块级作用域都是相互独立的,不受相互影响的

三、Const

作用:声明常量,常量就是值(内存地址)不能变化的量。

特点:
A、在js中,const定义的变量成为常量,不能被重复赋值,数据已经定义,不能修改
  B、const也是定义在中,不能在外调用
  C、const定义的是对象、数组、函数、引用数据类型。其中只要引用数据类型的地址没变化,就可以改变引用数据类型中的单元存储的数据
  D、const不会进行预解析,结果会报错

  • 具有块级作用域
if (true) {
const a = 10;
}
console.log(a) // a is not defined
  • 声明常量时必须赋值
const PI; // Missing initializer in const declaration
  • 常量赋值后,值不能修改。

声明了一个常量,代表这个识别名称的参照(reference)是唯读的(read-only),并不代表这个参照赋到的值是不可改变的(immutable),"const"只是针对存取层级的限制(access limitations),并不是不可改变性(immutability)。

上面这段在讲什么呢?实际上就是简单数据类型例如字符、数值常量赋值后不能更改,但是复杂数据类型,例如数组、对象,情况有所出入。
当对整个数组或者对象进行重新赋值时,是不被允许的,而修改里面的属性时是可以的,例如案例代码所示。
本质原理就是常量值对应的内存地址并不能变化,重新赋值等于修改内存地址,而修改属性则没有关系。

const PI = 3.14;
PI = 100; // Assignment to constant variable.
const ary = [100, 200];
ary[0] = 'a';
ary[1] = 'b';
console.log(ary); // ['a', 'b'];
ary = ['a', 'b']; // Assignment to constant variable.

额外

1声明常量时建议使用const还是let

这个问题网上的争议很多,有人说使用const有人说使用let,我仅我个人的理解来说我一下我的认识。

我喜欢还是用const (这是一个非常个人化主观化的观点,你说你喜欢用let当然可以)

  • const可以用在对象、数组与函数上,常量一声明时就要赋值,犯错的机会会减少很多。

  • JS引擎也可以作优化。虽然JS语言上并没有感受差异,但实际上引擎里有区分出常量与变量,而最重要的是因为JS语言是弱(动态)类型的脚本语言,常量一声明就赋值,代表它立即决定好是什么类型与值,自然效能比变量好得多。也可以理解说因为const声明的常量是固定的,内存不需要实时监测值的变化,所以比其他变量的内存占有率更少

2函数方法中的const

我不知道有没有人和我一样,有一段时间一直有个困惑,就是在函数中,或者方法中,用const申明变量,不是说const是不能再被赋值的吗?函数会调用很多次,为什么不报错?
其实后来一想很简单,因为函数每次执行完之后就会销毁了。。。。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: