Babel
Babel 是一个广泛使用的 ES6 转码器,可以将 ES6 代码转为 ES5 代码,从而在现有环境执行。
Babel 的配置文件是.babelrc,存放在项目的根目录下。使用 Babel 的第一步,就是配置这个文件。
{
"presets": [
"@babel/env",
"@babel/preset-react"
],
"plugins": []
}
// presets字段设定转码规则
Babel 提供命令行工具@babel/cli,用于命令行转码。
安装:npm install --save-dev @babel/cli
使用
# 转码结果输出到标准输出
$ npx babel example.js
# 转码结果写入一个文件
# --out-file 或 -o 参数指定输出文件
$ npx babel example.js --out-file compiled.js
# 或者
$ npx babel example.js -o compiled.js
# 整个目录转码
# --out-dir 或 -d 参数指定输出目录
$ npx babel src --out-dir lib
# 或者
$ npx babel src -d lib
# -s 参数生成source map文件
$ npx babel src -d lib -s
@babel/register
@babel/register模块改写require命令,为它加上一个钩子。此后,每当使用require加载.js、.jsx、.es和.es6后缀名的文件,就会先用 Babel 进行转码。
安装:npm install --save-dev @babel/register
使用:
// index.js
require('@babel/register');
require('./es6.js');
@babel/register只会对require命令加载的文件转码,而不会对当前文件转码。另外,由于它是实时转码,所以只适合在开发环境使用。
@babel/core
如果某些代码需要调用 Babel 的 API 进行转码,就要使用@babel/core模块。
var es6Code = 'let x = n => n + 1';
var es5Code = require('@babel/core')
.transform(es6Code, {
presets: ['@babel/env']
})
.code;
console.log(es5Code);
// transform方法的第一个参数是一个字符串,表示需要被转换的 ES6 代码,第二个参数是转换的配置对象。
@babel/polyfill
Babel 默认只转换新的 JavaScript 句法(syntax),而不转换新的 API,比如Iterator、Generator、Set、Map、Proxy、Reflect、Symbol、Promise等全局对象,以及一些定义在全局对象上的方法(比如Object.assign、新增的Array.from方法)都不会转码。。如果想让这些方法运行,必须使用babel-polyfill,为当前环境提供一个垫片。
安装:npm install --save-dev @babel/polyfill
使用:
// 在脚本头部,加入如下一行代码
import '@babel/polyfill';
// 或者
require('@babel/polyfill');
let 和 const命令
let
1、let声明的变量命令所在的代码块内有效。
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 10
// 变量i是var命令声明的,在全局范围内都有效,所以全局只有一个变量i
// 每一次循环,变量i的值都会发生改变,而循环内被赋给数组a的函数内部的console.log(i),里面的i指向的就是全局的i。
// 把var换成let 输出6
// 因为当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6
你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。
2、不存在变量提升
// var 的情况
console.log(foo); // 输出undefined
var foo = 2;
// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;
3、暂时性死区(TDZ)
只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。
if (true) {
// TDZ开始
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError
let tmp; // TDZ结束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
// 在let命令声明变量tmp之前,都属于变量tmp的“死区”。
// 如果块级作用域内存在用let声明的变量 这let声明执行之前 访问该变量都会出错
// 在let之前的typeof也就没那么安全了
typeof x; // ReferenceError
let x;
// 没有let语句 则正常返回undefined
4、不允许重复声明
let不允许在相同作用域内,重复声明同一个变量。因此,不能在函数内部重新声明参数
function func(arg) {
let arg;
}
func() // 报错
function func(arg) {
{
let arg;
}
}
func() // 不报错
5、为什么需要块级作用域
没有块级作用域会导致某些场景不合理
1、第一种场景,内层变量可能会覆盖外层变量。
2、第二种场景,用来计数的循环变量泄露为全局变量。
const
const声明一个只读的常量。一旦声明,就必须立即初始化,不能留到以后赋值,也不可重复声明。
const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。
const foo = {};
// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123
// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only
global对象
- 浏览器里面,顶层对象是window,但 Node 和 Web Worker 没有window。
- 浏览器和 Web Worker 里面,self也指向顶层对象,但是 Node 没有self。
- Node 里面,顶层对象是global,但其他环境都不支持。
var getGlobal = function () {
if (typeof self !== 'undefined') { return self; }
if (typeof window !== 'undefined') { return window; }
if (typeof global !== 'undefined') { return global; }
throw new Error('unable to locate global object');
};
网友评论