let与const
let与const
ES2015(ES6)
新增加了两个重要的JavaScript
关键字: let
和const
。
块级作用域
代码块内如果存在let
或者const
,代码块会对这些命令声明的变量从块的开始就形成一个封闭作用域。
{
let a = 1;
var b = 2;
function s(){return a;}
console.dir(s);
/*
...
[[Scopes]]: Scopes[2]
0: Block {a: 1}
1: Global ...
*/
}
// 此处不能使用 a ,a 是块级作用域
// 此处可以使用 b , b 在此处是全局作用域
[[Scopes]]
是保存函数作用域链的对象,是函数的内部属性无法直接访问,[[Scopes]]
中可以看到出现了一个Block
块级作用域,这使得let
特别适合在for
中使用,在ECMAScript 2015
引入let
关键字之前,只有函数作用域和全局作用域,函数作用域中又可以继续嵌套函数作用域,在for
并未具备局部作用域,于是有一个常见的闭包创建问题。
function counter(){
var arr = [];
for(var i = 0 ; i < 3 ; ++i){
arr[i] = function(){
return i;
}
}
return arr;
}
var coun = counter();
for(var i = 0 ; i < 3 ; ++i){
console.log(coun[i]()); // 3 3 3
}
可以看到运行输出是3 3 3
,而并不是期望的0 1 2
,原因是这三个闭包在循环中被创建的时候,共享了同一个词法作用域,这个作用域由于存在一个i
由var
声明,由于变量提升,具有函数作用域,当执行闭包函数的时候,由于循环早已执行完毕,i
已经被赋值为3
,所以打印为3 3 3
,可以使用let
关键字声明i
来创建块级作用域解决这个问题
function counter(){
var arr = [];
for(let i = 0 ; i < 3 ; ++i){
arr[i] = function(){
return i;
}
}
return arr;
}
var coun = counter();
for(var i = 0 ; i < 3 ; ++i){
console.log(coun[i]()); // 0 1 2
}
当然也可以使用匿名函数新建函数作用域来解决
function counter(){
var arr = [];
for(var i = 0 ; i < 3 ; ++i){
(function(i){
arr[i] = function(){
return i;
}
})(i);
}
return arr;
}
var coun = counter();
for(var i = 0 ; i < 3 ; ++i){
console.log(coun[i]()); // 0 1 2
}
一次声明
同一作用域内let
和const
只能声明一次,var
可以声明多次
let a = 1;
let a = 1; //Uncaught SyntaxError: Identifier 'a' has already been declared
const b = 1;
const b = 1; //Uncaught SyntaxError: Identifier 'b' has already been declared
暂时性死区
当使用let
与const
生成块级作用域时,代码块会对这些命令声明的变量从块的开始就形成一个封闭作用域,代码块内,在声明变量之前使用它会报错,称为暂时性死区。
{
console.log(a); // Uncaught ReferenceError: Cannot access 'a' before initialization
let a =1;
}
变量提升
let
与const
也存在变量提升,在ES6
的文档中出现了var/let hoisting
字样,也就是说官方文档说明let
与var
一样,都存在变量提升,但是与var
的变量提升有所不同
let 的「创建」过程被提升了,但是初始化没有提升。
var 的「创建」和「初始化」都被提升了。
function 的「创建」「初始化」和「赋值」都被提升了。
在stackoverflow
中比较有说服力的例子
x = "global";
// function scope:
(function() {
x; // not "global"
var/let/… x;
}());
// block scope (not for `var`s):
{
x; // not "global"
let/const/… x;
}
js
中无论哪种形式声明var
,let
,const
,function
,function*
,class
都会存在提升现象,不同的是,var
,function
,function*
的声明会在提升时进行初始化赋值为 undefined,因此访问这些变量的时候,不会报ReferenceError
异常,而使用let
,const
,class
声明的变量,被提升后不会被初始化,这些变量所处的状态被称为temporal dead zone
,此时如果访问这些变量会抛出ReferenceError
异常,看上去就像没被提升一样。
https://blog.csdn.net/jolab/article/details/82466362
https://www.jianshu.com/p/0f49c88cf169
https://stackoverflow.com/questions/31219420/are-variables-declared-with-let-or-const-not-hoisted-in-es6
window
在全局作用域中使用var
直接声明变量或方法等会挂载到window
对象上,let
与const
声明变量或方法等会保存在Script
作用域中
var a = 1;
let b = 2;
const c = 3;
console.log(window.a); // 1
console.log(window.b); // undefined
console.log(window.c); // undefined
let a = 1;
{
let b = 2;
function s(){return a + b;}
console.dir(s);
/*
...
[[Scopes]]: Scopes[3]
0: Block {b: 2}
1: Script {a: 1}
2: Global ...
*/
}
初始化
var
与let
在声明时可以不赋初值,const
必须赋初值
var a;
let b;
const c; //Uncaught SyntaxError: Missing initializer in const declaration
只读常量
const
用以声明一个只读常量,初始化后值不可再修改
const a = 1;
a = 2; // Uncaught TypeError: Assignment to constant variable.
const
其实保证的不是变量的值不变,而是保证变量指向的内存地址所保存的数据不允许改动。对于简单类型number
、string
、boolean
、Symbol
,值就保存在变量指向的那个内存地址,因此const
声明的简单类型变量等同于常量。而复杂类型object
,array
,function
,变量指向的内存地址其实是保存了一个指向实际数据的指针,所以const
只能保证指针是固定的,至于指针指向的数据结构变不变就无法控制了。
const a = {};
console.log(a); // {}
a.s = function(){}
console.log(a); // {s: ƒ}
相关
ES6新特性 https://github.com/WindrunnerMax/EveryDay/blob/master/JavaScript/ES6%E6%96%B0%E7%89%B9%E6%80%A7.md
Js变量提升 https://github.com/WindrunnerMax/EveryDay/blob/master/JavaScript/JS%E5%8F%98%E9%87%8F%E6%8F%90%E5%8D%87.md
- 看各路神仙如何大战MySQL insecure warning报警有感
- 由optimizer_switch所引起的诡异问题
- 【Oracle 12c Flex Cluster专题 】— Leaf Node的故障迁移
- MySQL中的统计信息相关参数介绍
- iOS学习——UITableViewCell两种重用方法的区别
- iOS学习——UIPickerView的实现年月选择器
- iOS学习——自动定位
- iOS学习——iOS原生实现二维码扫描
- iOS学习——iOS开发小知识点集合
- iOS学习——@class和#import的区别
- iOS学习——UIView的研究
- iOS学习——布局利器Masonry框架源码深度剖析
- iOS项目——自定义UITabBar与布局
- @FeignClient中的@RequestMapping也被Spring MVC加载的问题解决
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Jupyter 插件太好用了
- 华为提出十大数学挑战!解出一个就是年薪百万!
- 一道 Google 的面试题
- 生产实践 | 基于 Flink 的短视频生产消费监控
- 图数据库调研
- Swift 类构造器的使用
- 「网络IO套路」当时就靠它追到女友
- 起个简单枯燥的标题:找出连续差相同的数字
- 10分钟带你搞懂代理模式、静态代理、JDK+CGLIB动态代理
- 握草!某程序员竟然在深夜偷偷在代码里下毒!
- 自然资源部贡献的Landuse数据(2000、2010、2020)
- LoRa节点开发——SDK整体设计思路
- 01 . Nginx简介及部署
- 02 . Nginx平滑升级和虚拟主机
- LoRa节点开发——LoRaWAN节点入网代码详解