ECMAscript 与 JavaScript关系
ECMA-262是第一版规则,其关系是前者是后者的规格,后者是前者的实现(这里我理解是一种标准类似于协议、规范等)
Babel 转码器
babel可以将es6转化为es5,不用担心环境支持
例如:
// 转码前
input.map(item => item + 1);
// 转码后
input.map(function (item) {
return item + 1;
});
配置文件(.babelrc)
Babel 的配置文件是.babelrc,存放在项目的根目录下。使用 Babel 的第一步,就是配置这个文件。
该文件用来设置转码规则和插件,基本格式如下。
presets字段设定转码规则,官方提供以下的规则集,你可以根据需要安装。
最新转码规则
$ npm install --save-dev babel-preset-latest
react 转码规则
$ npm install --save-dev babel-preset-react
不同阶段语法提案的转码规则(共有4个阶段),选装一个
$ npm install --save-dev babel-preset-stage-0
$ npm install --save-dev babel-preset-stage-1
$ npm install --save-dev babel-preset-stage-2
$ npm install --save-dev babel-preset-stage-3
然后,将这些规则加入.babelrc。
{
"presets": [
"latest",
"react",
"stage-2"
],
"plugins": []
}
注意,以下所有 Babel工具和模块的使用,都必须先写好.babelrc
命令行转码器babel-cli
Babel提供了babbel-cli,用于命令行的转码
安装
$ npm install --global babel-cli
基本用法
转码结果输出到标准输出
$ babel example.js
转码结果写入一个文件
–out-file 或 -o 参数指定输出文件
$ babel example.js --out-file compiled.js
或者
$ babel example.js -o compiled.js
整个目录转码
–out-dir 或 -d 参数指定输出目录
$ babel src --out-dir lib
或者
$ babel src -d lib
-s 参数生成source map文件
$ babel src -d lib -s
let 和 const
let
和var类似用于声明变量,但是只在let命令的代码块中有用
{
let a = 10;
var b = 1;
}
a // ReferenceError: a is not defined.
b // 1
for循环的计数器更适合let
let不存在变量提升
var在声明前使用会出现undefined但是let为了避免这样操作,只能在声明后使用
// var 的情况
console.log(foo); // 输出undefined
var foo = 2;
// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;
暂时性死区
只要块级作用域内存在let命令,他所声明的变量就绑定这个区域,不再受外部的影响。
var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}
上面代码中存在一个全局变量tmp=123,但是快级作用域内let又声明一个tmp='abc’变量,所以在let申明变量前,对tmp赋值就会报错。
ES6明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。
let不允许重复声明
let在相同作用域中重复申明同一个变量
// 报错
function () {
let a = 10;
var a = 1;
}
// 报错
function () {
let a = 10;
let a = 1;
}
因此,不能在函数内部重新声明参数。
function func(arg) {
let arg; // 报错
}
function func(arg) {
{
let arg; // 不报错
}
}
块级作用域
在es5中只有全局作用域,函数作用域没有快级作用域,这带来很多不合理的场景。
第一:内层变量可能会嵌套外层变量
var tmp = new Date();
function f() {
console.log(tmp);
if (false) {
var tmp = 'hello world';
}
}
f(); // undefined
上面代码的原意是,if代码块的外部使用外层的tmp变量,内部使用内层的tmp变量。但是,函数f执行后,输出结果为undefined,原因在于变量提升,导致内层的tmp变量覆盖了外层的tmp变量。
第二种场景,用来计数的循环变量泄露为全局变量。
var s = 'hello';
for (var i = 0; i < s.length; i++) {
console.log(s[i]);
}
console.log(i); // 5
do 表达式
{
let t = f();
t = t * t + 1;
}
上面块级作用域将两个语句封装在一起。但是快级作用域,没有办法得到t的值,因为块级作用域不返回值,除非t是全局变量。
有一个提案,使得快级作用域可以变为表达式,也就是说是返回值,办法就是在作用域之前加上do,使他变为do表达式。
let x = do {
let t = f();
t * t + 1;
};
上面代码中,变量x会得到整个块级作用域的返回值。
const命令
基本
const声明一个只读常量,一旦声明常量的值就不能改变。
const PI = 3.1415;
PI // 3.1415
PI = 3;
// TypeError: Assignment to constant variable.
上面代码表明改变常量的值会报错
const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立刻初始化,不能留到最后。
const foo;
// SyntaxError: Missing initializer in const declaration
上面代码表示,对于const来说,只声明不赋值,就会报错。
const的作用域与let命令相同:只在声明所在的块级作用域内有效。
if (true) {
const MAX = 5;
}
MAX // Uncaught ReferenceError: MAX is not defined
const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。
声明方面const和let是一样的不可以重复。
本质
const是保证变量指向的那个内存地址不得改变,值保存在变量指向的那个内存地址,因此同等于常量对于复合类型数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指针,const只能保证这个指正是固定的,它的数据结构是否可变就不能控制了。
const foo = {};
// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123
// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: “foo” is read-only