let命令
基本用法
- let声明的变量只在其所在的代码块(块级作用域)内有效
{
var a = 10
let b = 20
}
console.log(a)
console.log(b)
//for循环的计数器就很适合使用let命令
for (let i = 0
console.log(i)
- for循环中的var:变量i是var声明的,此代码中的i是全局变量,当for循环执行时,会将新的值赋值给全局变量i,当for循环结束后,将10赋值给了全局变量i,此时全局变量i变为了10,所以arr数组中无论调用哪一个函数,输出的i都是10
var arr = []
for (var i = 0
arr[i] = function() {
console.log(i)
}
}
arr[5]()
- for循环中的let:变量i是let声明的,当前的i只在本轮循环中的块级作用域中有效,所以每一次循环的其实都是一个新的变量i;
- 既然每一轮循环的变量i都是重新声明的,那怎么知道上一轮循环的值从而计算出本轮循环的值?:这是因为JavaScript引擎内部会记住上一轮循环中的值,初始化本轮循环中的变量i时,会在上一轮循环的基础上进行计算
var arr = []
for (let i = 0
arr[i] = function() {
console.log(i)
}
}
arr[5]()
arr[6]()
- for循环中使用let声明变量还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域
for (let i = 0
let i = 'hello'
console.log(i)
}
不存在变量提升
- 使用var声明变量时,会发生变量的声明提升,即变量可以在声明之前使用,值为undefined
console.log(foo)
var foo = 1
- 使用let命令声明变量时,没有变量的声明提升,let声明的变量一定要在声明后使用,否则会拨错;
console.log(bar)
let bar = 2
暂时性死区
- ES6明确规定,如果区块中存在let和const命令,则这个区块对这些命令声明的变量从一开始就形成封闭作用域,只要在声明之前使用这些变量,就会报错
- 暂时性死区的本质就是,只要进入当前作用域,所有使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量
- 只要块级作用域内存在let命令,他所声明的变量就“绑定”这个区域,不再受外部影响
//首先声明了全局变量temp,但是块级作用域内let又声明了一个局部变量temp,
//导致后者绑定这个块级作用域,所以在let声明的局部变量前,对temp赋值会报错
var temp = 123
if (true) {
temp = 'abc'
let temp
}
if (true) {
temp = 'acb';
console.log(temp);
let temp;
console.log(temp);
temp = 123;
console.log(temp);
}
function foo(n) {
let n = 123
console.log(n)
}
等同于==>
function foo(n) {
var n = 666
let n = 123
console.log(n)
}
foo(666)
function bar(x = y, y = 2) {
return [x, y];
}
bar();
var x = x
console.log(x)
let y = y
typeof
- “暂时性死区”也意味着typeof不再是一个百分之百安全的操作
- x变量使用let声明,在声明之前都属于x的“死区”,只要使用到该变量就会报错
typeof x;
let x;
- 一个根本没有被声明的变量,使用typeof反而不会报错
typeof isNotDefined;
不允许重复声明
function foo() {
var a = 1
let a = 10
}
function bar() {
let b = 1
let b = 10
}
function foo(arg) {
let arg;
}
函数内部等同于==>
var arg;
let arg;
function bar(arg) {
{
let arg;
}
}
块级作用域
ES6的块级作用域
function f1() {
let n = 5
if (true) {
let n = 10
}
console.log(n)
}
f1()
{{{{{let message = 'hello'}}}}}
{{{{
{let message = 'hello'}
console.log(message)
}}}}
{{{{
let message = 'world'
{let message = 'hello'}
}}}}
- 块级作用域的出现,实际上使得获得广泛应用的立即执行函数(IIFE)不再必要了
//IIFE写法
(function() {
var temp = ···
···
}())
//块级作用域写法
{
let temp = ···
···
}
块级作用域与函数声明
- ES5中规定,函数只能在顶层作用域和函数作用域之中声明,不能再块级作用域声明
- ES6中规定,允许在块级作用域之中声明函数
- 在浏览器的ES6环境中遵循以下三条规则:
- 允许在块级作用域内声明函数
- 函数声明类似于var,即会提升到全局作用域或函数作用域的头部
- 同时,函数声明还会提升到所在的块级作用域的头部
console.log(f);
{
console.log(f);
if (true) {
console.log(f);
function f() {
console.log('hello');
}
}
console.log(f);
f();
}
等同于==>
var f;
console.log(f);
{
console.log(f);
if (true) {
function f() {
console.log('hello');
}
console.log(f);
}
console.log(f);
f();
}
- ES6的块级作用域允许函数声明,在严格模式下没有使用大括号会报错
'use strict';
if (true)
function f() {console.log(666);}
f();
- 考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式的形式,而不是函数声明语句
//函数声明语句(尽量避免)
{
let a = 1
function f() {
return a
}
}
//函数表达式
{
let a = 1
let f = function() {
return a
}
}
const命令
基本用法
- const声明一个只读的常量;一旦声明,常量的值就不能改变
const PI = 3.14
PI = 3.1415
- const声明的常量不能改变值,这意味着,const一旦声明常量,就必须立即初始化,不能留到以后赋值
const MAX;
if (true) {
const PI = 3.14;
}
console.log(PI);
不存在变量提升
console.log(MAX)
const MAX = 999
暂时性死
var MIN = 1
if (true) {
MIN = -1
const MIN = 0
}
不可重复声明
var message = 'hello'
let age = 21
const message = 'bye'
const age = 12
const本质
- const实际上保证的并不是变量的值不得变动,而是变量指向的那个内存地址不得改动
- 对于简单的数据(数值,字符串,布尔值)而言,值就保存在变量指向的内存地址中,因此等同于常量
- 对于复合类型的数据(主要是对象和数组)而言,变量指向的内存地址保存的只是一个指针,指针指向保存的值,const只能保证这个指针是固定的,不能保证指针指向的数据结构不变
const foo = {}
foo.age = 21
console.log(foo.age)
foo = {}
const arr = []
arr.push('hello')
console.log(arr[0])
console.log(arr.length)
arr = ['world']
冻结对象
- 冻结对象,应该使用Object.freeze()方法
const foo = Object.freeze({})
//常规模式时,下面一行不起作用
//严格模式时,改行会报错
foo.age = 21
function freezeAllObject(obj) {
Object.freeze(obj);
Object.keys(obj).forEach((key, index) => {
if (typeof obj[key] === 'object') {
freezeAllObject(obj[key]);
}
});
}