以下内容使用的书籍为《es6标准入门-第2版》,阮一峰著,如有需要请购买正版
本文仅为个人读书笔记,如有不详之处请查阅原文
es5提供了var 和function两种变量声明方法,在es6中提供了额外的4中声明方法,分别为let,const,import,class,本章将讲述let和const两种
let
基本用法
let仅在所在的代码块内有效(即被{}包裹的区间内)
{
let a=1;
var b=1;
}
a //ReferenceError: a is not defined
b //1
for循环的计数器就很适合let
var a=[];
for(let i=0;i<10;i++){
a[i]=function(){
console.log(i)
}
}
a[6](); //6
let仅在本轮循环中有效,相当于每次循环都是一个新的变量
不存在变量提升
typeof x;
let x=1;
这将抛出一个ReferenceError异常,因为let不存在变量提升,所以变量一定要在声明之后使用。
暂时性死区(TDZ)
只要区块作用域中存在let命令,那么它声明的变量就跟这个区块绑定了,不再受外部影响
var tmp="test it";
if(true){
tmp="abc"; //ReferenceError
let tmp;
}
如果区块中存在let和const命令,则这个区块对这些声明变量从一开始就形成封闭作用域,只要在声明之前使用这个变量就会报错。使用let命令声明变量之前,该变量都不可以使用,这在语法上被称为“暂时性死区”(temporal dead zone,简称TDZ),所有在let生命变量之前使用该变量的区域都称为该变量的死区
有些死区比较隐蔽
function bar(x=y,y=1){
return [x,y]
}
bar();
这里会抛出ReferenceError,而不是返回[undefind,1]
不允许重复声明
let不允许在相同作用域内重复声明
function bar(){
let a=1;
let a=10;
}
//Identifier 'a' has already been declared
同样,不能在函数内部重复声明参数
function bar(a){
let a=1;
// Identifier 'a' has already been declared
}
function bar2(a){
{
let a=1;
// 不会报错
}
}
块级作用域
为什么要有块级作用域
避免变量提升导致的内部变量覆盖外部变量
var tmp=new Date();
function f(){
console.log(tmp);
if(true){
var tmp=12
}
}
f()//undefined
避免计数器的循环变量泄露为全局变量
for(var i=0;i<10;i++){
}
console.log(i);//10
ES6的块级作用域
let为ES6增加了块级作用域
ES6允许块级作用域任意嵌套
{{{{{{{let insane="hello word"}}}}}}}
外层作用域无法读取内层作用域的变量
{{{{{{
{let insane="hello word"}
console.log(insane)//insane is not defined
}}}}}}
内层作用域可以定义外层作用域的同名变量
{{{{{{
let insane="outside"
{
let insane="inside"
console.log(insane)//inside
}
console.log(insane)//outside
}}}}}}
函数本身的作用域在其所在的区块级作用域之内
function f(){
console.log('I am outside')
}
(function(){
if(false){
function f(){
console.log('I am inside')
}
}
f();
})();
//es5显示inside,es6显示outside
const
基本用法
const用来声明常量,一旦声明,其值就不能改变
const Pi=3.1415926
console.log(Pi)//3.1415926
Pi=3;//TypeError: Assignment to constant variable
const一旦声明就必须赋值
const a;//SyntaxError: Missing initializer in const declaration
const的作用域和let一样,只在块级作用域有效
const不存在变量提升,同样存在死区
const也不能重复声明
以上示例参照let
对象和数组
const可以声明一个数组或者对象类型的变量,并且其值是可写的,但是不能重新赋值
const a=[];
a.push(1);
console.log(a);//[1]
a=[];//TypeError: Assignment to constant variable.
const a={}
a.b=3
console.log(a);//{b:3}
a={}//TypeError: Assignment to constant variable.
var b={c:1}
const a=b
a.c=2;
console.log(a);//{c:2}
console.log(b);//{c:2}
a={}//TypeError: Assignment to constant variable.
如果希望a的所有属性都不可写,可以使用Object的freeze方法,将对象冻结
const foo=Object.freeze({});
foo.prop=1;
console.log(foo);//{}
上面的代码将foo指向一个冻结对象,所以新增属性不起作用
除了对象本身冻结,对象的属性也应该冻结,下面是一个将对象彻底冻结的函数
比部分用了其它的es6内容,可在看完后面的部分后,再回来看
var constantize=(obj)=>{
Object.freeze(obj);
Object.keys(obj).forEach((key,value)=>{
if( typeof obj[key] === 'object'){
constantize(obj[key]);
}
})
}
跨模块常量
const只对当前块级作用域有效,以下可以设置跨模块常量
//constants.js
export const A=1;
export const B=2;
export const C=3;
//test1.js
import * as constants from './constants';
console.log(constants.A)//1
console.log(constants.B)//2
//test2.js
import {A,B} as constants from './constants';
console.log(A//1
console.log(B)//2
全局对象的属性
浏览器中的全局对象为window,node中的全局对象为global
在ES5中,全局对象的属性和全局变量是等价的
ES6中var和function声明的全局变量依旧是全局对象的属性,而let const class声明的全局变量不属于全局对象的属性
var a=1;
//如果是node则下面为global.a
console.log(window.a)//1
let b=1;
console.log(window.b)//undefined
window.b=2;//Identifier 'b' has already been declared
console.log(b)//1
console.log(window.b)//2