ECMAScript6--01

Babel转码器

Babel是一个广泛使用的ES6转码器,可以将ES6代码转换为ES5代码,从而在浏览器或其他环境中执行。

1
2
3
4
5
6
7
//转码前
input.map(item => item+1);

//转码后
input.map(function(item){
return item+1;
});

上面的原始代码使用了箭头函数,这个特性还没有得到广泛支持,Babel将其转换为普通函数,就能在现有的js环境执行里面。
Babel自带一个babel-node命令。提供支持ES6的REPL环境。它支持node的PERL环境的所有功能,而且可以直接运行ES6代码。

1
2
3
4
$ babel-node
>
>console.log([1,2,3].map(x => x*x))
> [1,4,9]

babel-node命令也可以直接运行ES6脚本。假定将上面的代码放入脚本文件es6.js。

1
2
$ babel-node es6.js
[1,4,9]

-o参数将转换后的代码,从标准输出导入文件

1
2
3
$ babel es6.js -o es5.js
//或者
$ babel es6.js --out-fifle es5.js

-d参数用于转换整个目录

1
$ babel -d build-dir source-dir

注意,-d参数后面跟的是输出目录。
如果希望生成source map文件,则要加上-s参数

1
$ babel -d build-dir source-dir -s

浏览器环境

Babel也可以用于浏览器。但是从Babel6.0开始,不再直接提供浏览器版本,而是要用工具构建出来。

node环境

需要注意的是,Babel默认不会转换iterator、generator、set、maps、proxy、reflect、symbol、promise等全局对象,以及一些定义在全局对象上的方法(比如object.assign)。如果用到了这些功能就需要安装babel-polyfill模块。

1
$ npm install babel-polyfill --save

然后在所有脚本头部都加上一行:

1
2
3
require('babel-polyfill');
//or
import 'babel-polyfill';

在线转换

Babel提供一个REPL在线编译器,可以在线将ES6代码转换为ES5代码。转换后的代码,可以直接作为ES5代码插入网页运行

Traceur转码器

该转码器润许将ES6代码直接插入网页。首先,必须在网页头部加载Traceur库文件。

1
2
3
4
5
6
7
8
 <!-- 加载Traceur转码器 -->
<script src="http://google.github.io/traceur-compiler/bin/traceur.js"></script>
<!-- 将Traceur转码器 用于网页 -->
<script src="http://google.github.io/traceur-compiler/bin/bootstrap.js"></script>
<!-- 打开实验选项,否则有些不成功 -->
<script>
traceur.options.experimental = true;
</script>

接下来可以将ES6代码放上上面这些代码的下方就可以了。

1
2
3
<script type="module">
//代码区
</script>

注意,script的标签属性值是module。
也可以外部引入

1
2
<script type="module" src="es6.js">
</script>

let和const命令

let

基本用法

ES6新增了let命令,用来生命变量,它的用法类似于var,但是所生命的变量只在let命令所在的代码块内有效

1
2
3
4
5
6
7
{
let a = 10
var b = 1
}

a//a is not defined
b//1

for循环的计数器,就很适合用let命令。

1
2
for(let i = 0;i < 10;i++)
console.log(i);//i is not defined

计数器i只在循环体内有效,只在本轮循环有效,所以每一次循环的i其实都是一个新的变量。如果是var声明,在全局范围内都是i有效的,所以每一次循环新的i都会覆盖旧值。

不存在变量提升

let不想var那样,会有“变量提升”现象。所以变量一定要在声明后使用。

1
2
console.log(a);
let a = 1;

上述会输出一个错误,因为上述代码在声明a之前就使用这个变量结果就会抛出一个错误。这也意味着typeof不再是一个百分之百安全的操作

1
2
typeof x //出错
let x

暂时性死区

只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再收外部的影响。

1
2
3
4
5
var t = 123;
if(true){
t = "abc";//出错
let t;
}

上面代码中,存在全局变量t,但是块级作用域内又声明了一个局部变量t,导致后者绑定这个块级作用域,所以在let声明变量前,对t赋值会报错。

es6明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域,凡是在声明之前使用这些命令就会报错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

ES6规定暂时性死区和不存在变量提升主要是为了减少运行时错误,防止在变量声明前就使用这个变量。
~~~
总之,暂时性死区的本质就是,只要已进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的哪一行代码出现,才可以获取和使用该变量。
~~~
#### 不允许重复声明
let不允许在相同作用域内,重复声明同一个变量
~~~
//报错
function(){
let a = 10;
var a = 1;
}

//报错
function(){
let a = 10;
let a = 1;
}
~~~
因此```不能在函数内部重新声明参数
1
2
3
4
5
6
7
8
9
function func(arg){
let arg;//报错
}

function func(arg){
{
let arg;//报错
}
}

块级作用域

为什么需要块级作用域

ES5只有全局作用域和函数作用域,没有块级作用域。
1、内层变量可能会覆盖外层变量
2、用来计数的循环变量泄露为全局变量

ES6的块级作用域

let为JS新增了块级作用域。ES6允许块级作用域的任意嵌套。外层作用域无法获取内层作用域的变量。内层作用域可以定义外层作用域的同名变量。
块级作用域使得获得广泛应用的立即执行匿名函数不再必要了。

ES5的函数提升:
不管会不会进入if代码块,函数声明都会提升到当前作用域的顶部得到执行。
ES6的块级作用域:
不管会不会进入if代码块,其内部声明的函数都不会影响到作用域的外部。

const命令

const也是用来声明变量,但是声明的是常量,一旦声明,常量的值就不能改变。相当于read only
const的作用域和let作用域相同:只在声明所在的会计作用域内有效。
const声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。也跟let一样不能重复声明。

1
对于复合类型的变量,变量名不指向数据,而是指向数据所在的地址。const命令只是在保证变量名指向的地址不变,并不保证该地址的数据不变。
1
2
3
4
5
~~~
const foo = {};
foo.prop = 123;
foo.prop //123
foo = {}//read only

上面代码中,常量foo存储的是一个地址,这个地址指向一个对象。不可变的只是这个地址,即不能把foo指向另一个地址,但对象本身是可变的,所以依然可以为其添加一个新属性。
另一个例子:

1
2
3
4
const a = [];
a.push('123');//可执行
a.length = 0;//可执行
a = ["aa"]//报错

上面代码中,常量a是一个数组,这个数组本身是可写的,但是如果将另一个数组赋值给a,就会报错。如果真的项把对象冻结,可以使用Obejct.freeze方法。

1
2
const foo = Obejct.freeze({});
foo.prop = 123;//不起作用

除了将对象本身冻结,对象的属性也应该冻结。下面是一个将对象完全冻结的函数:

1
2
3
4
5
6
7
8
var constantize = (obj) => {
Obejct.freeze(obj);
Obejct.keys(obj).forEach( (key,value) => {
if(typeof obj[key] === 'object'){
constatize(obj[key]);
}
});
};

ES6一共又6种声明变量的方法。

全局对象的属性

全局对象是最顶层的对象,在浏览器环境值得是window对象,在Node.js指的是global对象。ES5中,全局对象的属性和全局变量是等价的。

1
2
3
4
5
window.a = 1;
a//1

a=2;
window.a//1

ES6中,var和function命令所声明的全局变量,依旧是全局对象的属性,另一方面,let命令、const命令、class命令声明的全局变量不属于全局对象的属性。

1
2
let 吧= 1
window.b//undefined

全局变量b由let声明,所以它不是全局对象的属性,返回undefined。

-------------本文结束感谢您的阅读-------------