详细解析let和const命令

目录
  • let命令
    • 基本用法
    • 特性
    • 暂时性死区
    • 不允许重复声明
  • const
    • 特性
  • 补充——块级作用域

let命令

基本用法

我们都知道let命令是用来声明变量的,类似于var,但是通过let命令声明的变量只在所在代码块内有效。

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

如上代码所示,使用的是let声明的i,那么这个变量仅在块级作用域内有效,即只在每轮循环内有效,所以每次循环的i其实都是一个新变量,所以最后输出了5。

如果使用了var去申明i,那么这个i是在全局范围内都有效的,函数数组里的i都会指向同一个i,导出最后输出的是最后一轮循环的i的值。

那就有了一个疑问,使用let声明的i如果每一轮都是一个新变量,那怎么知道上一轮循环的值然后计算出本轮的值?

这是因为JavaScript引擎内部会记住上一轮循环的值,初始化当次循环的i时,会在上一轮循环的基础上计算。

for循环还有一个特别的地方,就是设置循环变量的括号内是一个父作用域,而循环体内是一个单独的子作用域。

特性

不存在变量提升:

不存在变量提升我们应该都知道,在var命令中会发生“变量提升”,就是变量可以在声明之前使用,而值是undefined。这种是比较奇怪的现象,因为他都还没声明怎么就可以使用呢?

所以。let命令改变了这种语法,通过let声明的变量一定要在变量声明后才能用,不然就会报错。

console.log(b) // undefined
console.log(a) // 报错ReferenceError
let a = 2;
var b = 1;

暂时性死区

只要块级作用域内存在Let命令,那么这个let声明的变量就绑定了这个区域,不会受到外部的影响。

var num = 1;
if(true) {
    num = 2; // 报错,ReferenceError
    let num;
}

如上面代码所示,if代码块内想要修改的是if外部的num,但是if内部通过let声明了一个num但为赋值,所以导致了通过let声明的num绑定了这个块级作用域(即if内部这个块级作用域),所以不会受到外部的num影响,所以对num赋值会报错。

ES6明确规定了,如果在区块内存在let和const命令,那么这个区块对这些命令声明的变量从一开始就形成封闭的作用域,只要在声明之前就使用这些变量,就会报做。也称暂时性死区(temporal dead zone,简称TDZ)

当然,这也造成了typeof变得不安全了,因为如果一个变量没有声明就使用typeof不会报错,会返回一个undefined,但是现在不成立了,因为会报错ReferenceError。

function bar(x = y, y = 2) {
    return[x,y]
}
bar() // 报错

如上代码所示,调用bar函数会报错,因为参数x的默认值等于另一个参数y,但是x在声明的时候y还没有声明,属于死区

总之,只要进入当前作用域,所要使用的变量就已经存在,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

其实let和const是存在变量提升的,但是由于暂时性死区的关系,变量已经存在了,但是不可获取,要等到声明变量的时候才可以获取和使用。

不允许重复声明

let不允许在校内共同作用域内重复声明同一个变量,这个就不用说了大家都知道。

let a = 1;
let a = 2; // 报错

const

我们都知道const声明一个只读的常量。修改这个值就会报错。但是实际上保证的不是变量的值不能改动,而是变量指向的那个内存地址不能改动。

对于简单数据类型来说,值就是保存在变量指向的内存地址中,因此等同于常量。

而对于复合类型的数据,变量指向的内存地址保存的是一个指针,const只保证指针是固定,不保证内部的结构是不是可变的。

    // 给对象添加一个属性
    const foo = {}
    foo.name = 123
    console.log(foo.name) // 123
    // 将foo指向另一个对象,就会报错
    foo = {} // 报错。

如果想要将对象冻结,可以使用Object.freeze

const foo = Object.freeze({});

特性

  • 只在声明所在的块级作用域内有效。
  • const声明的常量不会提升,同样存在暂时性死区,只能在声明后使用。
  • 不可重复声明。

补充——块级作用域

函数能不能在块级作用域中声明呢?

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

ES6中引入了块级作用域,明确允许在块级作用域之中声明函数。而且。函数声明的语句的行为类似于let,在块级作用域之外不可引用。

function f() {
    console.log('I am outside!')
}
(function() {
    if(false) {
        function f() {
            console.log('I am inside')
        }
    }
}
f();
}())

上面的代码,在es5中运行时会得到I am inside!,因为if内部的f函数会被提升到函数头部。

但是在ES6环境则会报错,为什么呢?

因为游览器的实现有自己的方式:

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

所以上述代码实际在ES6的游览器中会生成这样的代码:

  function f() {
    console.log("I am outside!");
  }
  (function () {
    var f = undefined;
    if (false) {
      function f() {
        console.log("I am inside");
      }
    }
  })();

因此,应该避免在块级作用域内声明函数,如果确定需要写成函数表达式的形式,而不是函数声明语句。

到此这篇关于详细解析let和const命令的文章就介绍到这了,更多相关let和const内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • JavaScript中ES6规范中let和const的用法和区别

    ES6-- let 和 const 命令 引言 本文主要讲解ES6中变量的相关操作,变量的命名, 讲解 var. let . const 三者的区别 正文 ES6中的 let 和 const 都是用来声明变量的, 他们与 var 有所区别 let 命令 我们都知道在for循环中,我们命名的变量 i 一般都只是为了在这个循环中使用,才临时命名的, 我们希望循环结束后,这个变量就消失, 但是却相反,用 var 命名的变量,在 for 循环结束后并不会销毁,而会存在于全局中. for(var i=0;

  • javascript 变量声明 var,let,const 的区别

    目录 作用域(Scope)是什么 var 声明 提升(Hoisting) let 声明 const 声明 作用域(Scope)是什么 作用域是程序的执行环境,它包含在当前位置可访问的变量和函数.在 ES5 语法中,有全局作用域和局部作用域,ES6 则新增了块级作用域. 全局作用域是最外层的作用域,在函数外面定义的变量属于全局作用域,可以被任何其他子作用域访问.在浏览器中,window 对象就是全局作用域.在编写前端代码过程中,其中有一条优化规则就是少使用全局变量,因为全局变量容易导致程序BUG,

  • javascript的var与let,const之间的区别详解

    目录 作为全局变量时 变量提升 暂时性死区 块级作用域 重复声明 修改声明的变量(常量与变量声明) 总结 说到JavaScript中声明变量的几种方法也就是var.let.const了,let和const是es6中新增的命令.那么它们之间有什么区别呢? 我们先整体说一下三者的区别,在详细介绍,var.let.const的区别主要从以下几点分析: 作为全局变量时的不同 变量提升 暂时性死区 块级作用域 重复声明 修改声明的变量 作为全局变量时 在ES5中,顶层对象的属性和全局变量是等价的,用var

  • 详解javascript中var与ES6规范中let、const区别与用法

    随着ES6规范的到来,Js中定义变量的方法已经由单一的 var 方式发展到了 var.let.const 三种之多.var 众所周知,可那俩新来的货到底有啥新特性呢?到底该啥时候用呢?下面就是小编总结出的关于javascript中var与ES6规范中let.const区别详解 我们先来絮叨絮叨 var 方式定义变量有啥 bug ? Js没有块级作用域 请看这样一条规则:在JS函数中的var声明,其作用域是函数体的全部. for(var i=0;i<10;i++){ var a = 'a'; }

  • JavaScript ES6语法中let,const ,var 的区别

    一.变量声明的方式let / const let / const 共同点 1.都是块级作用域2.在同一个作用域下,变量名不允许重复3.他们声明的全局变量并没有挂在 window对象上4.都没有预编译 let / const 不同点 1.let 声明的变量值可以改变2.const 声明的变量值不能改变,必须声明后立即赋值 (如:const a = 3.14;)3.const 存引用数据类型时,内容可以发生改变(地址不能改变) 优先考虑使用const , 如果变量会发生改变,就使用let , 最后使

  • JS变量中有var定义和无var定义的区别以及es6中let命令和const命令

    之前我们在写js代码的时候都知道可以用var定义全局变量和局部变量,也可以省略var,而且在非严格模式下不会报错,但是并不知道两者的区别... var x = 1; y = 4; console.log(x);//1 console.log(y);//4 console.log(window.x);//1 console.log(window.y);//4 简单测试下可以知道定义的x和y都被挂载在window对象上,变为window下的属性,这并不能说明什么... delete x; delet

  • JavaScript中const、var和let区别浅析

    在JavaScript中有三种声明变量的方式:var.let.const.下文给大家介绍js中三种定义变量的方式const, var, let的区别. 1.const定义的变量不可以修改,而且必须初始化. const b = 2;//正确 // const b;//错误,必须初始化 console.log('函数外const定义b:' + b);//有输出值 // b = 5; // console.log('函数外修改const定义b:' + b);//无法输出 2.var定义的变量可以修改,

  • 5分钟快速掌握JS中var、let和const的异同

    前言 首先,一个常见的问题是,ECMAScript 和 JavaScript 到底是什么关系? ECMAScript是一个国际通过的标准化脚本语言.JavaScript由ECMAScript和DOM.BOM三者组成.可以简单理解为:ECMAScript是JavaScript的语言规范,JavaScript是ECMAScript的实现和扩展. 2011 年,ECMAScript 5.1 版发布.之前我们大部分人用的也就是ES5 2015 年 6 月,ECMAScript 6 正式通过,成为国际标准

  • 详细解析let和const命令

    目录 let命令 基本用法 特性 暂时性死区 不允许重复声明 const 特性 补充——块级作用域 let命令 基本用法 我们都知道let命令是用来声明变量的,类似于var,但是通过let命令声明的变量只在所在代码块内有效. let a = []; for (let i = 0;i < 10; i++ ) { a[i] = function () { console.log(i); } } a[5](); // 5 如上代码所示,使用的是let声明的i,那么这个变量仅在块级作用域内有效,即只在每

  • linux下修改文件权限chmod命令详细解析

    使用 Linux 的chmod命令控制谁可以访问读写或运行目标文件. 在 Linux 中,谁可以对文件或目录做什么是通过一系列权限来控制的. 权限可以控制对文件或目录执行的操作(读写或执行). 我们可以使用-l(长格式)选项来ls列出文件和目录的文件权限. ls -l 在每一行中,第一个字符标识列出条目类型.如果它是破折号 ( -),则它是一个文件.如果是字母d ,则是目录. 接下来的九个字符代表三组权限的设置. 前三个字符显示拥有文件的用户的权限(用户权限). 中间三个字符显示用户组成员的权限

  • MySQL索引详细解析

    目录 1. MySQL 索引的最左前缀原则 2. 前缀索引 3. 索引下推(ICP——Index Condition Pushdown) 4. 查看 MySQL 语句是否用到索引 5. 为什么官方建议用自增长主键作为索引 6. 如何创建索引 7. 创建索引注意事项 8. 使用索引一定可以提高查询性能嘛 9. 索引失效 1. MySQL 索引的最左前缀原则 左前缀原则是联合索引在使用时要遵循的原则,查询索引可以使用联合索引的一部分,但是必须从最左侧开始.在创建联合索引时,要根据业务需求,where

  • Mysql中复制详细解析

    1.mysql复制概念 指将主数据库的DDL和DML操作通过二进制日志传到复制服务器上,然后在复制服务器上将这些日志文件重新执行,从而使复制服务器和主服务器的数据保持同步.复制过程中一个服务器充当主服务器(master),而一个或多个其它服务器充当从服务器(slaves).主服务器将更新重新写入二进制日志文件,并维护文件的一个索引以跟踪日志循环.这些日志可以记录发送到从服务器的更新.当一个从服务器连接主服务器时,它通知主服务器.从服务器在日志中读取的最后一次成功更新的位置.从服务器接受从那时起发

  • MySQL DELETE语法使用详细解析

    以下的文章主要描述的是MySQL DELETE语法的详细解析,首先我们是从单表语法与多表语法的示例开始的,假如你对MySQL DELETE语法的相关内容十分感兴趣的话,你就可以浏览以下的文章对其有个更好的了解. 单表语法: 复制代码 代码如下: DELETE [LOW_PRIORITY] [QUICK] [IGNORE] FROM tbl_name   [WHERE where_definition]   [ORDER BY ...]   [LIMIT row_count] 多表语法: 复制代码

  • ES6入门教程之let和const命令详解

    前言 在javascript中,我们都知道使用var来声明变量.javascript是函数级作用域,函数内可以访问函数外的变量,函数外不能访问函数内的变量. 函数级作用域会导致一些问题就是某些代码块内的变量会在全局范围内有效,这我们是非常熟悉的: for (var i = 0; i < 10; i++) { console.log(i); // 0,1,2...,9 } console.log(i); //10 if(true){ var s = 20; } console.log(s); //

  • 对MySQL配置参数 my.ini/my.cnf的详细解析

    以下的文章主要描述的是对MySQL配置参数 my.ini/my.cnf的详细解析,我们主要是以实例演示的方式来对MySQL配置参数 my.ini/my.cnf的实际操作步骤进行说明,以下就是相关内容的具体描述. 1.获取当前配置参数 要优化MySQL配置参数,首先要了解当前的配置参数以及运行情况.使用下列命令可以获得目前服务器使用的配置参数: 复制代码 代码如下: mysqld –verbose –help mysqladmin variables extended-status –u root

  • Docker网络原理及自定义网络详细解析

    Docker在宿主机上虚拟了一个网桥,当创建并启动容器的时候,每一个容器默认都会被分配一个跟网桥网段一致的ip,网桥作为容器的网关,网桥与每一个容器联通,容器间通过网桥可以通信.由于网桥是虚拟出来的,外网无法进行寻址,也就是默认外网无法访问容器,需要在创建启动容器时把宿主机的端口与容器端口进行映射,通过宿主机IP端口访问容器.这是Docker默认的网络,它有一个弊端是只能通过IP让容器互相访问,如果想使用容器名称或容器ID互相访问需要在创建启动容器时候用link的方式修改hosts文件实现.一般

  • C++内存管理详细解析

    目录 一.C++内存管理 1. new/delete表达式 2.new/delete重载 3.类内自定义allocator(per-class allocator) 二.多线程内存分配器 1.malloc/free 2.brk和mmap 三.补充知识 1.内存泄漏 2.malloc/free和new/delete的比较 3.RAII规则 一.C++内存管理 C++中有四种内存分配.释放方式: 最高级的是std::allocator,对应的释放方式是std::deallocate,可以自由设计来搭

随机推荐