Babel转码器
Babel是一个广泛使用的ES6转码器,可以将ES6代码转换为ES5代码,从而在浏览器或其他环境中执行。
1 | //转码前 |
上面的原始代码使用了箭头函数,这个特性还没有得到广泛支持,Babel将其转换为普通函数,就能在现有的js环境执行里面。
Babel自带一个babel-node命令。提供支持ES6的REPL环境。它支持node的PERL环境的所有功能,而且可以直接运行ES6代码。
1 | $ babel-node |
babel-node命令也可以直接运行ES6脚本。假定将上面的代码放入脚本文件es6.js。
1 | $ babel-node es6.js |
-o参数将转换后的代码,从标准输出导入文件
1 | $ babel es6.js -o 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 | require('babel-polyfill'); |
在线转换
Babel提供一个REPL在线编译器,可以在线将ES6代码转换为ES5代码。转换后的代码,可以直接作为ES5代码插入网页运行
Traceur转码器
该转码器润许将ES6代码直接插入网页。首先,必须在网页头部加载Traceur库文件。
1 | <!-- 加载Traceur转码器 --> |
接下来可以将ES6代码放上上面这些代码的下方就可以了。
1 | <script type="module"> |
注意,script的标签属性值是module。
也可以外部引入
1 | <script type="module" src="es6.js"> |
let和const命令
let
基本用法
ES6新增了let命令,用来生命变量,它的用法类似于var,但是所生命的变量只在let命令所在的代码块内有效
1 | { |
for循环的计数器,就很适合用let命令。
1 | for(let i = 0;i < 10;i++) |
计数器i只在循环体内有效,只在本轮循环有效,所以每一次循环的i其实都是一个新的变量。如果是var声明,在全局范围内都是i有效的,所以每一次循环新的i都会覆盖旧值。
不存在变量提升
let不想var那样,会有“变量提升”现象。所以变量一定要在声明后使用。
1 | console.log(a); |
上述会输出一个错误,因为上述代码在声明a之前就使用这个变量结果就会抛出一个错误。这也意味着typeof不再是一个百分之百安全的操作
1 | typeof x //出错 |
暂时性死区
只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再收外部的影响。
1 | var t = 123; |
上面代码中,存在全局变量t,但是块级作用域内又声明了一个局部变量t,导致后者绑定这个块级作用域,所以在let声明变量前,对t赋值会报错。
es6明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域,凡是在声明之前使用这些命令就会报错。
1 |
|
1 | function func(arg){ |
块级作用域
为什么需要块级作用域
ES5只有全局作用域和函数作用域,没有块级作用域。
1、内层变量可能会覆盖外层变量
2、用来计数的循环变量泄露为全局变量
ES6的块级作用域
let为JS新增了块级作用域。ES6允许块级作用域的任意嵌套。外层作用域无法获取内层作用域的变量。内层作用域可以定义外层作用域的同名变量。
块级作用域使得获得广泛应用的立即执行匿名函数不再必要了。
ES5的函数提升:
不管会不会进入if代码块,函数声明都会提升到当前作用域的顶部得到执行。
ES6的块级作用域:
不管会不会进入if代码块,其内部声明的函数都不会影响到作用域的外部。
const命令
const也是用来声明变量,但是声明的是常量,一旦声明,常量的值就不能改变。相当于read only
。
const的作用域和let作用域相同:只在声明所在的会计作用域内有效。
const声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。也跟let一样不能重复声明。
1 | 对于复合类型的变量,变量名不指向数据,而是指向数据所在的地址。const命令只是在保证变量名指向的地址不变,并不保证该地址的数据不变。 |
1 | ~~~ |
上面代码中,常量foo存储的是一个地址,这个地址指向一个对象。不可变的只是这个地址,即不能把foo指向另一个地址,但对象本身是可变的,所以依然可以为其添加一个新属性。
另一个例子:
1 | const a = []; |
上面代码中,常量a是一个数组,这个数组本身是可写的,但是如果将另一个数组赋值给a,就会报错。如果真的项把对象冻结,可以使用Obejct.freeze
方法。
1 | const foo = Obejct.freeze({}); |
除了将对象本身冻结,对象的属性也应该冻结。下面是一个将对象完全冻结的函数:
1 | var constantize = (obj) => { |
ES6一共又6种声明变量的方法。
全局对象的属性
全局对象是最顶层的对象,在浏览器环境值得是window对象,在Node.js指的是global对象。ES5中,全局对象的属性和全局变量是等价的。
1 | window.a = 1; |
ES6中,var和function命令所声明的全局变量,依旧是全局对象的属性,另一方面,let命令、const命令、class命令声明的全局变量不属于全局对象的属性。
1 | let 吧= 1; |
全局变量b由let声明,所以它不是全局对象的属性,返回undefined。