mirror of
https://github.com/ruanyf/es6tutorial.git
synced 2025-05-24 18:32:22 +00:00
add ES7 features
This commit is contained in:
parent
8ac0c18bd7
commit
97f6f6d98e
@ -317,9 +317,9 @@ var a2 = [for (i of a1) i * 2];
|
|||||||
a2 // [2, 4, 6, 8]
|
a2 // [2, 4, 6, 8]
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码表示,通过for...of结构,数组a2直接在a1的基础上生成。
|
上面代码表示,通过`for...of`结构,数组`a2`直接在`a1`的基础上生成。
|
||||||
|
|
||||||
注意,数组推导中,for...of结构总是写在最前面,返回的表达式写在最后面。
|
注意,数组推导中,`for...of`结构总是写在最前面,返回的表达式写在最后面。
|
||||||
|
|
||||||
for...of后面还可以附加if语句,用来设定循环的限制条件。
|
for...of后面还可以附加if语句,用来设定循环的限制条件。
|
||||||
|
|
||||||
@ -386,8 +386,20 @@ var a3 = ["x3", "y3"];
|
|||||||
|
|
||||||
数组推导需要注意的地方是,新数组会立即在内存中生成。这时,如果原数组是一个很大的数组,将会非常耗费内存。
|
数组推导需要注意的地方是,新数组会立即在内存中生成。这时,如果原数组是一个很大的数组,将会非常耗费内存。
|
||||||
|
|
||||||
|
推导的用法不限于数组,还可以直接使用。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var results = (
|
||||||
|
for (c of customers)
|
||||||
|
if (c.city == "Seattle")
|
||||||
|
{ name: c.name, age: c.age }
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
## Array.observe(),Array.unobserve()
|
## Array.observe(),Array.unobserve()
|
||||||
|
|
||||||
这两个方法用于监听(取消监听)数组的变化,指定回调函数。
|
这两个方法用于监听(取消监听)数组的变化,指定回调函数。
|
||||||
|
|
||||||
它们的用法与Object.observe和Object.unobserve方法完全一致,也属于ES7的一部分,请参阅《对象的扩展》一章。唯一的区别是,对象可监听的变化一共有六种,而数组只有四种:add、update、delete、splice(数组的length属性发生变化)。
|
它们的用法与`Object.observe`和`Object.unobserve`方法完全一致,也属于ES7的一部分,请参阅《对象的扩展》一章。
|
||||||
|
|
||||||
|
唯一的区别是,对象可监听的变化一共有六种,而数组只有四种:`add`、`update`、`delete`、`splice`(数组的`length`属性发生变化)。
|
||||||
|
@ -37,9 +37,9 @@ class Point {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码定义了一个“类”,可以看到里面有一个constructor方法,这就是构造方法,而this关键字则代表实例对象。也就是说,ES5的构造函数Point,对应ES6的Point类的构造方法。
|
上面代码定义了一个“类”,可以看到里面有一个`constructor`方法,这就是构造方法,而`this`关键字则代表实例对象。也就是说,ES5的构造函数Point,对应ES6的Point类的构造方法。
|
||||||
|
|
||||||
Point类除了构造方法,还定义了一个toString方法。注意,定义“类”的方法的时候,前面不需要加上function这个保留字,直接把函数定义放进去了就可以了。另外,方法之间不需要逗号分隔,加了会报错。
|
Point类除了构造方法,还定义了一个`toString`方法。注意,定义“类”的方法的时候,前面不需要加上`function`这个保留字,直接把函数定义放进去了就可以了。另外,方法之间不需要逗号分隔,加了会报错。
|
||||||
|
|
||||||
ES6的类,完全可以看作构造函数的另一种写法。
|
ES6的类,完全可以看作构造函数的另一种写法。
|
||||||
|
|
||||||
@ -240,9 +240,9 @@ p1.__proto__ === p2.__proto__
|
|||||||
//true
|
//true
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码中,p1和p2都是Point的实例,它们的原型都是Point,所以\_\_proto\_\_属性是相等的。
|
上面代码中,`p1`和`p2`都是Point的实例,它们的原型都是Point,所以`__proto__`属性是相等的。
|
||||||
|
|
||||||
这也意味着,可以通过实例的\_\_proto\_\_属性为Class添加方法。
|
这也意味着,可以通过实例的`__proto__`属性为Class添加方法。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
var p1 = new Point(2,3);
|
var p1 = new Point(2,3);
|
||||||
@ -257,7 +257,7 @@ var p3 = new Point(4,2);
|
|||||||
p3.printName() // "Oops"
|
p3.printName() // "Oops"
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码在p1的原型上添加了一个printName方法,由于p1的原型就是p2的原型,因此p2也可以调用这个方法。而且,此后新建的实例p3也可以调用这个方法。这意味着,使用实例的\_\_proto\_\_属性改写原型,必须相当谨慎,不推荐使用,因为这会改变Class的原始定义,影响到所有实例。
|
上面代码在`p1`的原型上添加了一个`printName`方法,由于`p1`的原型就是`p2`的原型,因此`p2`也可以调用这个方法。而且,此后新建的实例`p3`也可以调用这个方法。这意味着,使用实例的`__proto__`属性改写原型,必须相当谨慎,不推荐使用,因为这会改变Class的原始定义,影响到所有实例。
|
||||||
|
|
||||||
**(4)name属性**
|
**(4)name属性**
|
||||||
|
|
||||||
@ -832,22 +832,6 @@ foo.classMethod()
|
|||||||
|
|
||||||
上面代码中,`Foo`类的`classMethod`方法前有`static`关键字,表明该方法是一个静态方法,可以直接在`Foo`类上调用(`Foo.classMethod()`),而不是在`Foo`类的实例上调用。如果在实例上调用静态方法,会抛出一个错误,表示不存在该方法。
|
上面代码中,`Foo`类的`classMethod`方法前有`static`关键字,表明该方法是一个静态方法,可以直接在`Foo`类上调用(`Foo.classMethod()`),而不是在`Foo`类的实例上调用。如果在实例上调用静态方法,会抛出一个错误,表示不存在该方法。
|
||||||
|
|
||||||
需要注意的是,类只有静态方法,没有静态属性,像`Class.propname`这样的用法不存在。
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// 以下两种写法都无效,
|
|
||||||
// 但不会报错
|
|
||||||
class Foo {
|
|
||||||
// 写法一
|
|
||||||
prop: 2
|
|
||||||
|
|
||||||
// 写法二
|
|
||||||
static prop: 2
|
|
||||||
}
|
|
||||||
|
|
||||||
Foo.prop // undefined
|
|
||||||
```
|
|
||||||
|
|
||||||
父类的静态方法,可以被子类继承。
|
父类的静态方法,可以被子类继承。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
@ -883,6 +867,50 @@ class Bar extends Foo {
|
|||||||
Bar.classMethod();
|
Bar.classMethod();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Class的静态属性
|
||||||
|
|
||||||
|
静态属性指的是Class本身的属性,即`Class.propname`,而不是定义在实例对象(`this`)上的属性。
|
||||||
|
|
||||||
|
ES6明确规定,类只有静态方法,没有静态属性,即像`Class.propname`这样的用法不存在。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 以下两种写法都无效,
|
||||||
|
// 但不会报错
|
||||||
|
class Foo {
|
||||||
|
// 写法一
|
||||||
|
prop: 2
|
||||||
|
|
||||||
|
// 写法二
|
||||||
|
static prop: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
Foo.prop // undefined
|
||||||
|
```
|
||||||
|
|
||||||
|
ES7有一个静态属性的[提案](https://github.com/jeffmo/es-class-properties),目前Babel转码器支持。
|
||||||
|
|
||||||
|
这个提案对实例属性和静态属性,都规定了新的写法。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 实例属性的新写法
|
||||||
|
class MyClass {
|
||||||
|
myProp = 42;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
console.log(this.myProp); // 42
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 静态属性的新写法
|
||||||
|
class MyClass {
|
||||||
|
static myStaticProp = 42;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
console.log(MyClass.myProp); // 42
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## new.target属性
|
## new.target属性
|
||||||
|
|
||||||
new是从构造函数生成实例的命令。ES6为new命令引入了一个`new.target`属性,(在构造函数中)返回new命令作用于的那个构造函数。如果构造函数不是通过new命令调用的,`new.target`会返回undefined,因此这个属性可以用来确定构造函数是怎么调用的。
|
new是从构造函数生成实例的命令。ES6为new命令引入了一个`new.target`属性,(在构造函数中)返回new命令作用于的那个构造函数。如果构造函数不是通过new命令调用的,`new.target`会返回undefined,因此这个属性可以用来确定构造函数是怎么调用的。
|
||||||
|
102
docs/function.md
102
docs/function.md
@ -400,7 +400,7 @@ var nodeList = document.querySelectorAll('div');
|
|||||||
var array = [...nodeList];
|
var array = [...nodeList];
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码中,querySelectorAll方法返回的是一个nodeList对象,扩展运算符可以将其转为真正的数组。
|
上面代码中,`querySelectorAll`方法返回的是一个`nodeList`对象,扩展运算符可以将其转为真正的数组。
|
||||||
|
|
||||||
扩展运算符内部调用的是数据结构的Iterator接口,因此只要具有Iterator接口的对象,都可以使用扩展运算符,比如Map结构。
|
扩展运算符内部调用的是数据结构的Iterator接口,因此只要具有Iterator接口的对象,都可以使用扩展运算符,比如Map结构。
|
||||||
|
|
||||||
@ -426,7 +426,7 @@ var go = function*(){
|
|||||||
[...go()] // [1, 2, 3]
|
[...go()] // [1, 2, 3]
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码中,变量go是一个Generator函数,执行后返回的是一个遍历器对象,对这个遍历器对象执行扩展运算符,就会将内部遍历得到的值,转为一个数组。
|
上面代码中,变量`go`是一个Generator函数,执行后返回的是一个遍历器对象,对这个遍历器对象执行扩展运算符,就会将内部遍历得到的值,转为一个数组。
|
||||||
|
|
||||||
## name属性
|
## name属性
|
||||||
|
|
||||||
@ -580,15 +580,25 @@ headAndTail(1, 2, 3, 4, 5)
|
|||||||
|
|
||||||
箭头函数有几个使用注意点。
|
箭头函数有几个使用注意点。
|
||||||
|
|
||||||
(1)函数体内的this对象,绑定定义时所在的对象,而不是使用时所在的对象。
|
(1)函数体内的`this`对象,绑定定义时所在的对象,而不是使用时所在的对象。
|
||||||
|
|
||||||
(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
|
(2)不可以当作构造函数,也就是说,不可以使用`new`命令,否则会抛出一个错误。
|
||||||
|
|
||||||
(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替。
|
(3)不可以使用`arguments`对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替。
|
||||||
|
|
||||||
(4)不可以使用yield命令,因此箭头函数不能用作Generator函数。
|
(4)不可以使用`yield`命令,因此箭头函数不能用作Generator函数。
|
||||||
|
|
||||||
上面四点中,第一点尤其值得注意。this对象的指向是可变的,但是在箭头函数中,它是固定的。下面的代码是一个例子,将this对象绑定定义时所在的对象。
|
上面四点中,第一点尤其值得注意。`this`对象的指向是可变的,但是在箭头函数中,它是固定的。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
[1, 2, 3].map(n => n * 2);
|
||||||
|
|
||||||
|
// 等同于
|
||||||
|
|
||||||
|
[1, 2, 3].map(function(n) { return n * 2; }, this);
|
||||||
|
```
|
||||||
|
|
||||||
|
下面的代码是一个例子,将this对象绑定定义时所在的对象。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
var handler = {
|
var handler = {
|
||||||
@ -605,7 +615,7 @@ var handler = {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码的init方法中,使用了箭头函数,这导致this绑定handler对象,否则回调函数运行时,`this.doSomething`这一行会报错,因为此时this指向全局对象。
|
上面代码的`init`方法中,使用了箭头函数,这导致`this`绑定`handler`对象,否则回调函数运行时,`this.doSomething`这一行会报错,因为此时`this`指向全局对象。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
function Timer () {
|
function Timer () {
|
||||||
@ -690,15 +700,11 @@ var fix = f => (x => f(v => x(x)(v)))
|
|||||||
|
|
||||||
## 函数绑定
|
## 函数绑定
|
||||||
|
|
||||||
箭头函数可以绑定this对象,大大减少了显式绑定this对象的写法(call、apply、bind)。但是,箭头函数并不适用于所有场合,所以ES7提出了“函数绑定”(function bind)运算符,用来取代call、apply、bind调用。虽然该语法还是ES7的一个提案,但是Babel转码器已经支持。
|
箭头函数可以绑定`this`对象,大大减少了显式绑定`this`对象的写法(`call`、`apply`、`bind`)。但是,箭头函数并不适用于所有场合,所以ES7提出了“函数绑定”(function bind)运算符,用来取代`call`、`apply`、`bind`调用。虽然该语法还是ES7的一个[提案](https://github.com/zenparsing/es-function-bind),但是Babel转码器已经支持。
|
||||||
|
|
||||||
函数绑定运算符是并排的两个双引号(::),双引号左边是一个对象,右边是一个函数。该运算符会自动将左边的对象,作为上下文环境(即this对象),绑定到右边的函数上面。
|
函数绑定运算符是并排的两个双冒号(::),双冒号左边是一个对象,右边是一个函数。该运算符会自动将左边的对象,作为上下文环境(即this对象),绑定到右边的函数上面。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
let log = ::console.log;
|
|
||||||
// 等同于
|
|
||||||
var log = console.log.bind(console);
|
|
||||||
|
|
||||||
foo::bar;
|
foo::bar;
|
||||||
// 等同于
|
// 等同于
|
||||||
bar.call(foo);
|
bar.call(foo);
|
||||||
@ -706,6 +712,42 @@ bar.call(foo);
|
|||||||
foo::bar(...arguments);
|
foo::bar(...arguments);
|
||||||
i// 等同于
|
i// 等同于
|
||||||
bar.apply(foo, arguments);
|
bar.apply(foo, arguments);
|
||||||
|
|
||||||
|
const hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||||
|
function hasOwn(obj, key) {
|
||||||
|
return obj::hasOwnProperty(key);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
如果双冒号左边为空,右边是一个对象的方法,则等于将该方法绑定在该对象上面。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var method = obj::obj.foo;
|
||||||
|
// 等同于
|
||||||
|
var method = ::obj.foo;
|
||||||
|
|
||||||
|
let log = ::console.log;
|
||||||
|
// 等同于
|
||||||
|
var log = console.log.bind(console);
|
||||||
|
```
|
||||||
|
|
||||||
|
由于双冒号运算符返回的还是原对象,因此可以采用链式写法。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 例一
|
||||||
|
import { map, takeWhile, forEach } from "iterlib";
|
||||||
|
|
||||||
|
getPlayers()
|
||||||
|
::map(x => x.character())
|
||||||
|
::takeWhile(x => x.strength > 100)
|
||||||
|
::forEach(x => console.log(x));
|
||||||
|
|
||||||
|
// 例二
|
||||||
|
let { find, html } = jake;
|
||||||
|
|
||||||
|
document.querySelectorAll("div.myClass")
|
||||||
|
::find("p")
|
||||||
|
::html("hahaha");
|
||||||
```
|
```
|
||||||
|
|
||||||
## 尾调用优化
|
## 尾调用优化
|
||||||
@ -897,3 +939,35 @@ factorial(5) // 120
|
|||||||
上面代码中,参数 total 有默认值1,所以调用时不用提供这个值。
|
上面代码中,参数 total 有默认值1,所以调用时不用提供这个值。
|
||||||
|
|
||||||
总结一下,递归本质上是一种循环操作。纯粹的函数式编程语言没有循环操作命令,所有的循环都用递归实现,这就是为什么尾递归对这些语言极其重要。对于其他支持“尾调用优化”的语言(比如Lua,ES6),只需要知道循环可以用递归代替,而一旦使用递归,就最好使用尾递归。
|
总结一下,递归本质上是一种循环操作。纯粹的函数式编程语言没有循环操作命令,所有的循环都用递归实现,这就是为什么尾递归对这些语言极其重要。对于其他支持“尾调用优化”的语言(比如Lua,ES6),只需要知道循环可以用递归代替,而一旦使用递归,就最好使用尾递归。
|
||||||
|
|
||||||
|
## 函数参数的尾逗号
|
||||||
|
|
||||||
|
ES7有一个[提案](https://github.com/jeffmo/es-trailing-function-commas),允许函数的最后一个参数有尾逗号(trailing comma)。
|
||||||
|
|
||||||
|
目前,函数定义和调用时,都不允许有参数的尾逗号。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function clownsEverywhere(
|
||||||
|
param1,
|
||||||
|
param2
|
||||||
|
) { /* ... */ }
|
||||||
|
|
||||||
|
clownsEverywhere(
|
||||||
|
'foo',
|
||||||
|
'bar'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
如果以后要在函数的定义之中添加参数,就势必还要添加一个逗号。这对版本管理系统来说,就会显示,添加逗号的那一行也发生了变动。这看上去有点冗余,因此新提案允许定义和调用时,尾部直接有一个逗号。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function clownsEverywhere(
|
||||||
|
param1,
|
||||||
|
param2,
|
||||||
|
) { /* ... */ }
|
||||||
|
|
||||||
|
clownsEverywhere(
|
||||||
|
'foo',
|
||||||
|
'bar',
|
||||||
|
);
|
||||||
|
```
|
||||||
|
108
docs/intro.md
108
docs/intro.md
@ -52,9 +52,9 @@ Node.js和io.js(一个部署新功能更快的Node分支)是JavaScript语言
|
|||||||
$ curl -o- https://raw.githubusercontent.com/creationix/nvm/<version number>/install.sh | bash
|
$ curl -o- https://raw.githubusercontent.com/creationix/nvm/<version number>/install.sh | bash
|
||||||
```
|
```
|
||||||
|
|
||||||
上面命令的version number处,需要用版本号替换。本书写作时的版本号是v0.25.4。
|
上面命令的`version number`处,需要用版本号替换。本节写作时的版本号是`v0.25.4`。
|
||||||
|
|
||||||
该命令运行后,nvm会默认安装在用户主目录的`.nvm`子目录。然后,激活nvm。
|
该命令运行后,`nvm`会默认安装在用户主目录的`.nvm`子目录。然后,激活`nvm`。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ source ~/.nvm/nvm.sh
|
$ source ~/.nvm/nvm.sh
|
||||||
@ -78,7 +78,7 @@ $ nvm use node
|
|||||||
$ nvm use iojs
|
$ nvm use iojs
|
||||||
```
|
```
|
||||||
|
|
||||||
需要注意的是,Node.js对ES6的支持,需要打开harmony参数,iojs不需要。
|
需要注意的是,Node.js对ES6的支持,需要打开`harmony`参数,iojs不需要。
|
||||||
|
|
||||||
```
|
```
|
||||||
$ node --harmony
|
$ node --harmony
|
||||||
@ -139,7 +139,9 @@ input.map(function (item) {
|
|||||||
|
|
||||||
上面的原始代码用了箭头函数,这个特性还没有得到广泛支持,Babel将其转为普通函数,就能在现有的JavaScript环境执行了。
|
上面的原始代码用了箭头函数,这个特性还没有得到广泛支持,Babel将其转为普通函数,就能在现有的JavaScript环境执行了。
|
||||||
|
|
||||||
它的安装命令如下。
|
### 命令行环境
|
||||||
|
|
||||||
|
命令行下,Babel的安装命令如下。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ npm install --global babel
|
$ npm install --global babel
|
||||||
@ -195,7 +197,17 @@ $ babel -d build-dir source-dir
|
|||||||
$ babel -d build-dir source-dir -s
|
$ babel -d build-dir source-dir -s
|
||||||
```
|
```
|
||||||
|
|
||||||
Babel也可以用于浏览器。
|
### 浏览器环境
|
||||||
|
|
||||||
|
Babel也可以用于浏览器。它的浏览器版本,可以通过安装`babel-core`模块获取。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install babel-core
|
||||||
|
```
|
||||||
|
|
||||||
|
运行上面的命令以后,就可以在当前目录的`node_modules/babel-core/`子目录里面,找到`babel`的浏览器版本`browser.js`(未精简)和`browser.min.js`(已精简)。
|
||||||
|
|
||||||
|
然后,将下面的代码插入网页。
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<script src="node_modules/babel-core/browser.js"></script>
|
<script src="node_modules/babel-core/browser.js"></script>
|
||||||
@ -204,14 +216,18 @@ Babel也可以用于浏览器。
|
|||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码中,`browser.js`是Babel提供的转换器脚本,可以在浏览器运行。用户的ES6脚本放在script标签之中,但是要注明`type="text/babel"`。
|
上面代码中,`browser.js`是Babel提供的转换器脚本,可以在浏览器运行。用户的ES6脚本放在`script`标签之中,但是要注明`type="text/babel"`。
|
||||||
|
|
||||||
Babel配合Browserify一起使用,可以生成浏览器能够直接加载的脚本。
|
这种写法是实时将ES6代码转为ES5,对网页性能会有影响。生产环境需要加载已经转码完成的脚本。
|
||||||
|
|
||||||
|
`Babel`配合`Browserify`一起使用,可以生成浏览器能够直接加载的脚本。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ browserify script.js -t babelify --outfile bundle.js
|
$ browserify script.js -t babelify --outfile bundle.js
|
||||||
```
|
```
|
||||||
|
|
||||||
|
上面代码将ES6脚本`script.js`,转为`bundle.js`。浏览器直接加载后者就可以了,不用再加载`browser.js`。
|
||||||
|
|
||||||
在`package.json`设置下面的代码,就不用每次命令行都输入参数了。
|
在`package.json`设置下面的代码,就不用每次命令行都输入参数了。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
@ -225,6 +241,38 @@ $ browserify script.js -t babelify --outfile bundle.js
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Node环境
|
||||||
|
|
||||||
|
Node脚本之中,需要转换ES6脚本,可以像下面这样写。
|
||||||
|
|
||||||
|
先安装`babel-core`。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$ npm install --save-dev babel-core
|
||||||
|
```
|
||||||
|
|
||||||
|
然后在脚本中,调用`babel-core`的`transform`方法。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
require("babel-core").transform("code", options);
|
||||||
|
```
|
||||||
|
|
||||||
|
上面代码中,`transform`方法的第一个参数是一个字符串,表示ES6代码。
|
||||||
|
|
||||||
|
Node脚本还有一种特殊的`babel`用法,即把`babel`加载为`require`命令的一个钩子。先将`babel`全局安装。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install -g babel
|
||||||
|
```
|
||||||
|
|
||||||
|
然后,在你的应用的入口脚本(entry script)头部,加入下面的语句。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
require("babel/register");
|
||||||
|
```
|
||||||
|
|
||||||
|
有了上面这行语句,后面所有通过`require`命令加载的后缀名为`.es6`、`.es`、`.jsx`和`.js`的脚本,都会先通过`babel`转码后再加载。
|
||||||
|
|
||||||
## Traceur转码器
|
## Traceur转码器
|
||||||
|
|
||||||
Google公司的[Traceur](https://github.com/google/traceur-compiler)转码器,也可以将ES6代码转为ES5代码。
|
Google公司的[Traceur](https://github.com/google/traceur-compiler)转码器,也可以将ES6代码转为ES5代码。
|
||||||
@ -342,7 +390,7 @@ $ traceur --script calc.es6.js --out calc.es5.js --experimental
|
|||||||
|
|
||||||
命令行下转换的文件,就可以放到浏览器中运行。
|
命令行下转换的文件,就可以放到浏览器中运行。
|
||||||
|
|
||||||
### Node.js环境的用法
|
### Node.js环境的用法
|
||||||
|
|
||||||
Traceur的Node.js用法如下(假定已安装traceur模块)。
|
Traceur的Node.js用法如下(假定已安装traceur模块)。
|
||||||
|
|
||||||
@ -373,16 +421,42 @@ fs.writeFileSync('out.js.map', result.sourceMap);
|
|||||||
|
|
||||||
2013年3月,ES6的草案封闭,不再接受新功能了。新的功能将被加入ES7。
|
2013年3月,ES6的草案封闭,不再接受新功能了。新的功能将被加入ES7。
|
||||||
|
|
||||||
ES7可能包括的功能有:
|
任何人都可以向ES7提案,从提案到变成正式标准,需要经历五个阶段。每个阶段的变动都需要由TC39委员会批准。
|
||||||
|
|
||||||
(1)**Object.observe**:用来监听对象(以及数组)的变化。一旦监听对象发生变化,就会触发回调函数。
|
- Stage 0 - Strawman(展示阶段)
|
||||||
|
- Stage 1 - Proposal(征求意见阶段)
|
||||||
|
- Stage 2 - Draft(草案阶段)
|
||||||
|
- Stage 3 - Candidate(候选人阶段)
|
||||||
|
- Stage 4 - Finished(定案阶段)
|
||||||
|
|
||||||
(2)**Async函数**:在Promise和Generator函数基础上,提出的异步操作解决方案。
|
一个提案只要能进入Stage 2,就差不多等于肯定会包括在ES7里面。
|
||||||
|
|
||||||
(3)**Multi-Threading**:多线程支持。目前,Intel和Mozilla有一个共同的研究项目RiverTrail,致力于让JavaScript多线程运行。预计这个项目的研究成果会被纳入ECMAScript标准。
|
|
||||||
|
|
||||||
(4)**Traits**:它将是“类”功能(class)的一个替代。通过它,不同的对象可以分享同样的特性。
|
|
||||||
|
|
||||||
其他可能包括的功能还有:更精确的数值计算、改善的内存回收、增强的跨站点安全、类型化的更贴近硬件的低级别操作、国际化支持(Internationalization Support)、更多的数据结构等等。
|
|
||||||
|
|
||||||
本书的写作目标之一,是跟踪ECMAScript语言的最新进展。对于那些明确的、或者很有希望列入ES7的功能,尤其是那些Babel已经支持的功能,都将予以介绍。
|
本书的写作目标之一,是跟踪ECMAScript语言的最新进展。对于那些明确的、或者很有希望列入ES7的功能,尤其是那些Babel已经支持的功能,都将予以介绍。
|
||||||
|
|
||||||
|
本书介绍的ES7功能清单如下。
|
||||||
|
|
||||||
|
**Stage 0**:
|
||||||
|
|
||||||
|
- es7.comprehensions:数组推导
|
||||||
|
- es7.classProperties:类的属性
|
||||||
|
- es7.functionBind:函数的绑定运算符
|
||||||
|
|
||||||
|
**Stage 1**:
|
||||||
|
|
||||||
|
- es7.decorators:修饰器
|
||||||
|
- es7.exportExtensions:export的扩展写法
|
||||||
|
- es7.trailingFunctionCommas:函数参数的尾逗号
|
||||||
|
|
||||||
|
**Stage 2**:
|
||||||
|
|
||||||
|
- es7.exponentiationOperator:指数运算符
|
||||||
|
- es7.asyncFunctions:ansyc函数
|
||||||
|
- es7.objectRestSpread:对象的Rest参数和扩展运算符
|
||||||
|
|
||||||
|
Babel转码器对Stage 2及以上阶段的功能,是默认支持的。对于那些默认没有打开的功能,需要手动打开。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ babel --stage 0
|
||||||
|
$ babel --optional es7.decorators
|
||||||
|
```
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
ES6的Class只是面向对象编程的语法糖,升级了ES5的构造函数的原型链继承的写法,并没有解决模块化问题。Module功能就是为了解决这个问题而提出的。
|
ES6的Class只是面向对象编程的语法糖,升级了ES5的构造函数的原型链继承的写法,并没有解决模块化问题。Module功能就是为了解决这个问题而提出的。
|
||||||
|
|
||||||
历史上,JavaScript一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,比如Ruby的require、Python的import,甚至就连CSS都有@import,但是JavaScript任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍。
|
历史上,JavaScript一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,比如Ruby的`require`、Python的`import`,甚至就连CSS都有`@import`,但是JavaScript任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍。
|
||||||
|
|
||||||
在ES6之前,社区制定了一些模块加载方案,最主要的有CommonJS和AMD两种。前者用于服务器,后者用于浏览器。ES6在语言规格的层面上,实现了模块功能,而且实现得相当简单,完全可以取代现有的CommonJS和AMD规范,成为浏览器和服务器通用的模块解决方案。
|
在ES6之前,社区制定了一些模块加载方案,最主要的有CommonJS和AMD两种。前者用于服务器,后者用于浏览器。ES6在语言规格的层面上,实现了模块功能,而且实现得相当简单,完全可以取代现有的CommonJS和AMD规范,成为浏览器和服务器通用的模块解决方案。
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ function setName(element) {
|
|||||||
|
|
||||||
上面代码的`import`命令,就用于加载`profile.js`文件,并从中输入变量。`import`命令接受一个对象(用大括号表示),里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块(`profile.js`)对外接口的名称相同。
|
上面代码的`import`命令,就用于加载`profile.js`文件,并从中输入变量。`import`命令接受一个对象(用大括号表示),里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块(`profile.js`)对外接口的名称相同。
|
||||||
|
|
||||||
如果想为输入的变量重新取一个名字,import命令要使用as关键字,将输入的变量重命名。
|
如果想为输入的变量重新取一个名字,import命令要使用`as`关键字,将输入的变量重命名。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
import { lastName as surname } from './profile';
|
import { lastName as surname } from './profile';
|
||||||
@ -115,9 +115,9 @@ class Car extends Vehicle {
|
|||||||
export { Car }
|
export { Car }
|
||||||
```
|
```
|
||||||
|
|
||||||
上面的模块先加载Vehicle模块,然后在其基础上添加了move方法,再作为一个新模块输出。
|
上面的模块先加载`Vehicle`模块,然后在其基础上添加了`move`方法,再作为一个新模块输出。
|
||||||
|
|
||||||
如果在一个模块之中,先输入后输出同一个模块,import语句可以与export语句写在一起。
|
如果在一个模块之中,先输入后输出同一个模块,`import`语句可以与`export`语句写在一起。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
export { es6 as default } from './someModule';
|
export { es6 as default } from './someModule';
|
||||||
@ -129,9 +129,21 @@ export default es6;
|
|||||||
|
|
||||||
上面代码中,`export`和`import`语句可以结合在一起,写成一行。但是从可读性考虑,不建议采用这种写法,而应该采用标准写法。
|
上面代码中,`export`和`import`语句可以结合在一起,写成一行。但是从可读性考虑,不建议采用这种写法,而应该采用标准写法。
|
||||||
|
|
||||||
## 模块的整体输入
|
另外,ES7有一个[提案](https://github.com/leebyron/ecmascript-more-export-from),简化先输入后输出的写法,拿掉输出时的大括号。
|
||||||
|
|
||||||
下面是一个circle.js文件,它输出两个方法area和circumference。
|
```javascript
|
||||||
|
// 提案的写法
|
||||||
|
export v from "mod";
|
||||||
|
|
||||||
|
// 现行的写法
|
||||||
|
export {v} from "mod";
|
||||||
|
```
|
||||||
|
|
||||||
|
## 模块的整体加载
|
||||||
|
|
||||||
|
除了指定加载某个输出值,还可以使用整体加载,即用星号(`*`)指定一个对象,所有输出值都加载在这个对象上面。
|
||||||
|
|
||||||
|
下面是一个`circle.js`文件,它输出两个方法`area`和`circumference`。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// circle.js
|
// circle.js
|
||||||
@ -145,7 +157,7 @@ export function circumference(radius) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
然后,main.js文件输入circle.js模块。
|
现在,加载这个模块。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// main.js
|
// main.js
|
||||||
@ -156,7 +168,7 @@ console.log("圆面积:" + area(4));
|
|||||||
console.log("圆周长:" + circumference(14));
|
console.log("圆周长:" + circumference(14));
|
||||||
```
|
```
|
||||||
|
|
||||||
上面写法是逐一指定要输入的方法。另一种写法是整体输入。
|
上面写法是逐一指定要加载的方法,整体加载的写法如下。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
import * as circle from './circle';
|
import * as circle from './circle';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user