准备工作
- 新建空工作文件夹
- 在该文件夹打开终端,并初始化项目,-y取默认配置,安装完毕出现package.json配置文件
自动生成 package.json
$ npm init -y
- 后续可能需安装大量安装包,切换成淘宝镜像进行下载更快捷
#查看当前镜像源
$ npm get registry
#切换淘宝镜像,直接更换地址法
$ npm config set registry http://registry.npm.taobao.org/
1.6 Babel转码器
- 配置文件 .babelrc
格式:
{
"presets": [],
"plugins": []
}
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
配置规则:
{
"presets": [
"latest",
"react",
"stage-2"
],
"plugins": []
}
注:后续babel相关工具和模块的使用,.babelrc配置好是前提
- 命令行转码babel-cli
安装命令:
#全局安装
$ npm install --global babel-cli
# 安装在项目中
$ npm install --save-dev 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
安装在项目中是改写package.json
{
"devDependencies": {
"babel-cli": "^6.0.0"
},
"scripts": {
"build": "babel src -d lib"
},
}
转码时执行
$ npm run build
- babel-node
babel-cli自带babel-node命令,提供REPL环境(可直接运行es6),执行"babel-node"直接进入REPL环境,或直接运行es6脚本——“babel-node es6.js”
babel-cli安装在项目中时,package.json可配置如下:
{
"scripts": {
"script-name": "babel-node es6.js"
}
}
"使用babel-node替代node,这样script.js本身就不用做任何转码处理" ——
- node?不是babel?
- babel-register
实时转码,适合开发环境 。当使用require加载 .js .jsx .es .es6 等文件时,该工具会将其自动转码,但不包括当前文件的转码
下载:
$ npm install --save-dev babel-register
使用:
require("babel-register");
require("./index.js");
- babel-core
提供转码API
安装命令:
$ npm install babel-core --save
使用:
var babel = require('babel-core');
// 字符串转码
babel.transform('code();', options); // => { code, map, ast }
// 文件转码(异步)
babel.transformFile('filename.js', options, function(err, result) {
result; // => { code, map, ast } });
// 文件转码(同步)
babel.transformFileSync('filename.js', options); // => { code, map, ast }
// Babel AST转码
babel.transformFromAst(ast, code, options); // => { code, map, ast }
举例:
var es6Code = 'let x = n => n + 1';
var es5Code = require('babel-core')
.transform(es6Code, {
presets: ['latest']
})
.code;
// '"use strict";\n\nvar x = function x(n) {\n return n + 1;\n};'
- babel-polyfill
babel默认转换新的js语法,不转换新的API及一些定义在全局对象上的方法,使用babel-polyfill可对这些API和方法进行转码
安装:
$ npm install --save babel-polyfill
使用:
import 'babel-polyfill';
// 或者
require('babel-polyfill');
注:Babel 默认不转码的 API 非常多,详细清单可以查看babel-plugin-transform-runtime模块的definitions.js文件。
1.7 Traceur转码器
Traceur 允许将 ES6 代码直接插入网页
使用:
<script src="https://google.github.io/traceur-compiler/bin/traceur.js"></script>
<script src="https://google.github.io/traceur-compiler/bin/BrowserSystem.js"></script>
<script src="https://google.github.io/traceur-compiler/src/bootstrap.js"></script>
<script type="module">
import './Greeter.js';
</script>
2.1 let
- 无变量提升,凡在let声明前使用,则报ReferenceError 错误 (var在声明前使用,值为undefined)
- 暂时性死区(TDZ) 在块级作用域中存在let命令,该变量就会绑定该区域,不受外部影响
var tmp=1;
if (true) {
// TDZ开始
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError
let tmp; // TDZ结束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
- 不允许重复声明
2.2 块级作用域
-
函数声明与块级作用域
ES5 规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域声明。但浏览器为兼容旧代码,不会报错
ES6 规定:(仅在浏览器的ES6环境中有效,其他环境不用遵守,即将块级作用域的函数声明当作let处理)a. 块级作用域中,函数声明语句的行为类似 let,在块级作用域之外不可引用 b. 允许在块级作用域内声明函数(包含在大括号 {} 中才成立) c. 函数声明会提升到所在作用域的头部,,类似 var
考虑到环境导致的行为差异太大,当需要在块级作用域中声明函数时,应采用函数表达式的形式,而不是函数声明语句
// 函数声明语句
{
let a = 'secret';
function f() {
return a;
}
}
// 函数表达式
{
let a = 'secret';
let f = function () {
return a;
};
}
-
const 本质
const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合型数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指针,const只能保证这个指针是固定的。当需要将对象及其属性都变为常量(不可修改),应使用 Object.freeze 方法
var constantize = (obj) => {
Object.freeze(obj);
Object.keys(obj).forEach( (key, i) => {
if ( typeof obj[key] === 'object' ) {
constantize( obj[key] );
}
});
};
2.4 顶层对象的获取
ES5的顶层对象在各种实现里面不统一:
顶层对象 | window | self | global |
---|---|---|---|
浏览器 | √ | √ | X |
Node | X | X | √ |
web Worker | X | √ | X |
为了取到顶层对象,现在一般使用 this 变量,但有局限性
- 全局环境中,this会返回顶层对象。但是,Node 模块和 ES6 模块中,this返回的是当前模块。
- 函数里面的this,如果函数不是作为对象的方法运行,而是单纯作为函数运行,this会指向顶层对象。但是,严格模式下,这时this会返回 undefined。
- 不管是严格模式,还是普通模式,new Function(‘return this’)(),总是会返回全局对象。但是,如果浏览器用了 CSP(Content Security Policy,内容安全政策),那么eval、new
Function这些方法都可能无法使用。
以下两种方式勉强可以使用获取顶层对象:
// 方法一
(typeof window !== 'undefined'
? window : (typeof process === 'object' &&
typeof require === 'function' &&
typeof global === 'object')
? global : this
);
// 方法二
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');
};
垫片库 system.global 模拟了一个提案,可以在所有环境拿到 global。
// CommonJS 的写法
var global = require('system.global')();
// ES6 模块的写法
import getGlobal from 'system.global';
const global = getGlobal();
3.1 解构赋值
- 数组的解构赋值,以元素的次序取值
- 对象的解构赋值,以元素的名称取值(可重新取名)
//注:
//p:匹配模式,
//name:真正赋值的变量
let {
p : name}=obj;
const node = {
loc: {
start: {
line: 1,
column: 5 } } };
let {
loc, loc: {
start }, loc: {
start: {
line }} } = node; line // 1 loc // Object {start: Object} start // Object {line: 1, column: 5}
对象的解构赋值可以设置默认值,但生效条件是当对象的属性值严格等于 undefined
var {
x = 3} = {
x: undefined}; x // 3
var {
x = 3} = {
x: null}; x // null
解构赋值的规则是,只要等号右边的值不是对象或者数组,就先将其转为对象。由于 undefined 和 null 无法转为对象,所以对它们解构赋值会报错
let {
prop: x } = undefined; // TypeError
let {
prop: y } = null; // TypeError
变量声明语句不得使用圆括号——会引发歧义导致报错
赋值语句的非模式部分可以使用括号
4.2 codePointAt()
JavaScript 内部,字符以 UTF-16 的格式储存。\u0000~\uFFFF 之间的字符只表示 2 个字节的字符,超出 0xFFFF 的字符需要用 4 个字节表示
codePointAt 方法是测试一个字符由 2 个字节还是由 4 个字节组成的简单方法。其返回的是十进制码点,如需十六进制的值可用 toString 方法转换
let s = '𠮷a';
//for...of 可以正确识别 32 位的 UTF-16 字符,即识别大于0xFFFF的码点
for (let ch of s) {
console.log(ch.codePointAt(0).toString(16));
}
// 20bb7 // 61
function is32Bit(c) {
return c.codePointAt(0) > 0xFFFF;
}
is32Bit("𠮷") // true
is32Bit("a") // false
与 codePointAt 相反的操作是:将码点返回对应的字符,可用 String.fromCodePoin 方法
注:fromCodePoint方法定义在String对象上,而codePointAt方法定义在字符串的实例对象上。
4.7 includes(), startsWith(), endsWith(),repeat(),padStart(),padEnd()
- includes():返回布尔值,表示是否找到了参数字符串。
- startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
- endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
第一个参数表示要查找的字符,第二个可选参数表示开始搜索的位置
let s = 'Hello world!';
s.startsWith('world', 6) // true
s.endsWith('Hello', 5) //
true s.includes('Hello', 6) // false
- repeat方法返回一个新字符串,表示将原字符串重复n次
- padStart()用于头部补全
- padEnd()用于尾部补全
//参数表示重复n次
'x'.repeat(3) // "xxx"
//第一个参数表示总字符串长度
//第二个可选参数表示需要补全的字符,无则补空
'x'.padStart(5, 'ab') // 'ababx'
'x'.padEnd(5, 'ab'