本文作者:IMWeb 张颖 原文出处:IMWeb社区 未经同意,禁止转载
个人感觉ECMAScript 6总体上来说:添加了块级作用域,增加了一些语法糖,增强了字符串的处理,引入Generator函数控制函数的内部状态的变化,原生提供了Promise对象,引入了Class(类)的概念,并且在语言规格的层面上实现了模块功能。 注:
1、ES6的支持性可以查看:http://kangax.github.io/compat-table/es6/
2、Google V8引擎已经部署了ES6的部分特性,使用Node.js 0.12版,可以试验这些特性。
3、使用Traceur转码器、Babel转码器等可以将ES6方式编写的程序转为ES5代码。
一个花括号{}代表一个块级作用域,作用域嵌套时外层代码块不受内层代码块的影响,立即执行匿名函数(IIFE)原本的作用是为了形成局部作用域,防止变量污染,块级作用域的的出现使得获得广泛应用的立即执行匿名函数不再必要了。
需要注意: 函数本身的作用域,在其所在的块级作用域之内。ES5存在函数提升,不管函数在何处声明,函数声明都会提升到当前作用域的顶部,得到执行;而ES6支持块级作用域,其内部声明的函数皆不会影响到作用域的外部。
function f() { console.log('I am outside!'); }
(function () {
if(false) {
// 重复声明一次函数f
function f() { console.log('I am inside!'); }
}
f();
}());
//ES5 result: I am inside!
//ES6 result: I am outside!
let命令,用来声明变量,它的用法类似于var,但是所声明的变量,只在let命令所在代码块内有效。
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6]();
//result: 10
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6]();
//result: 6
使用时有几点需要需要注意:
var tmp = 123;
if (true) {
// TDZ开始
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError
let tmp; // TDZ结束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
const用来声明常量,一旦声明,常量的值就不能改变。
使用时需注意:
const foo = {};
foo.prop = 123;
foo.prop
// 123
foo = {} // 不起作用
const foo = Object.freeze({});
foo.prop = 123; // 不起作用
全局对象是最顶层的对象,在浏览器环境指的是window对象,在Node.js指的是global对象。ES5规定,所有全局变量都是全局对象的属性。
ES6规定,var命令和function命令声明的全局变量,属于全局对象的属性;let命令、const命令、class命令声明的全局变量,不属于全局对象的属性。
ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。解构只能用于数组或对象,所以应该注意,其他原始类型的值都可以转为相应的对象,除了undefined和null。
本质上,解构写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。
let [foo, [[bar], baz]] = [1, [[2], 3]];
let [x, y='b'] = ['a', undefined]; // x='a', y='b'
let [a, b, c] = new Set(["a", "b", "c"]);
var { foo, bar } = { foo: "aaa", bar: "bbb" };
var {x, y = 5} = {x: 1};
console.log(x, y) // 1, 5
let { log, sin, cos } = Math;
function move({x=0, y=0} = {}) {
return [x, y];
}
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]
JavaScript内部,字符以UTF-16的格式储存,每个字符固定为2个字节。对于那些需要4个字节储存的字符(Unicode码点大于0xFFFF的字符),JavaScript会认为它们是两个字符。在ECMAScript6中,增强了对码点大于0xFFFF的字符的整体处理和正则匹配。
具体增加的一些处理方法如下:
var s = " ";
/^.$/.test(s) // false
/^.$/u.test(s) // true
使用规则:
`In JavaScript'\n' is a line-feed.`
// 多行字符串
console.log(`string text line 1
string text line 2`);
// 字符串中嵌入变量
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
//反斜杠转义
var greeting = `\`Yo\` World!`;
//对象属性
var obj = {x: 1, y: 2};
console.log(`${obj.x + obj.y}`)
//函数调用
function fn() {
return "Hello World";
}
console.log(`foo ${fn()} bar`);
模板字符串可以紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串。函数的参数第一个为模板字符串中没有变量替换的部分组成的数组,第一个参数之后的参数,都是模板字符串各个变量依次被替换后的值。
var a = 5;
var b = 10;
tag`Hello ${ a + b } world ${ a * b}`;
//等价于
tag(['Hello ', ' world '], 15, 50);
注意处理函数的第一个参数,拥有一个raw属性。它也是一个数组,成员与处理函数的第一个参数完全一致,唯一的区别是字符串是被转义前的原始格式。
二进制和八进制数值的新的写法,分别用前缀0b和0o表示
0b111110111 === 503 // true
0o767 === 503 // true
- Math.trunc(x)方法用于去除一个数的小数部分,返回整数部分;
- Math.sign(x)方法用来判断一个数到底是正数、负数、还是零;
- Math.acosh(x) 返回x的反双曲余弦(inverse hyperbolic cosine);
- Math.asinh(x) 返回x的反双曲正弦(inverse hyperbolic sine);
- Math.atanh(x) 返回x的反双曲正切(inverse hyperbolic tangent);
- Math.cbrt(x) 返回x的立方根;
- Math.clz32(x) 返回x的32位二进制整数表示形式的前导0的个数;
- Math.cosh(x) 返回x的双曲余弦(hyperbolic cosine);
- Math.expm1(x) 返回eˆx - 1;
- Math.fround(x) 返回x的单精度浮点数形式;
- Math.hypot(...values) 返回所有参数的平方和的平方根;
- Math.imul(x, y) 返回两个参数以32位整数形式相乘的结果;
- Math.log1p(x) 返回1 + x的自然对数;
- Math.log10(x) 返回以10为底的x的对数;
- Math.log2(x) 返回以2为底的x的对数;
- Math.tanh(x) 返回x的双曲正切(hyperbolic tangent)。
数组推导就是直接通过现有数组生成新数组的一种简化写法,通过for...of结构,允许多重循环。注:新数组会立即在内存中生成,这时如果原数组是一个很大的数组,将会非常耗费内存。
var a1 = [1, 2, 3, 4];
var a2 = [for (i of a1) i * 2];
a2 // [2, 4, 6, 8]
var years = [ 1954, 1974, 1990, 2006, 2010, 2014 ];
[for (year of years) if (year > 2000 && year < 2010) year];
// [ 2006]
Array.from({ 0: "a", 1: "b", 2: "c", length: 3 });
// [ "a", "b" , "c" ]
Array.from(arrayLike, x => x * x);
// 等同于
Array.from(arrayLike).map(x => x * x);
Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array(3) // [undefined, undefined, undefined]
ES6允许直接写入变量和函数,作为对象的属性和方法。
var Person = {
name: '张三',
//等同于birth: birth
birth,
// 等同于hello: function ()...
hello() { console.log('我的名字是', this.name); }
};
ES6允许字面量定义对象时,用表达式作为对象的属性名,即把表达式放在方括号内,允许变量渗入 key 中。
var lastWord = "last word";
var a = {
"first word": "hello",
[lastWord]: "world"
};
a["first word"] // "hello"
a[lastWord] // "world"
a["last word"] // "world"
Object.is():用来比较两个值是否严格相等。它与严格比较运算符(===)的行为基本一致,不同之处只有两个:一是+0不等于-0,二是NaN等于自身。
Object.assign():用来将源对象(source)的所有可枚举属性,复制到目标对象(target)。它至少需要两个对象作为参数,第一个参数是目标对象,后面的参数都是源对象。如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。
var target = { a: 1, b: 1 };
var source1 = { b: 2, c: 2 };
var source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
var mySymbol = Symbol('Test');
mySymbol.name
// Test
// 有参数的情况
var s1 = Symbol("foo");
var s2 = Symbol("foo");
s1 === s2 // false
//不能与其他类型的值进行运算
var sym = Symbol('My symbol');
'' + sym
// TypeError: Cannot convert a Symbol value to a string
//可以转换为字符串
String(sym)
// 'Symbol(My symbol)'
注意:
var person = {
name: "张三"
};
var proxy = new Proxy(person, {
get: function(target, property) {
return property in target ? target[property] : "米有";
}
});
proxy.name // "张三"
proxy.title // "米有"
let target = {};
let handler = {};
let {proxy, revoke} = Proxy.revocable(target, handler);
proxy.foo = 123;
proxy.foo // 123
revoke();
proxy.foo // TypeError: Revoked
Proxy支持的拦截操作:
属性 | 返回值 | 拦截操作 |
---|---|---|
defineProperty(target, propKey, propDesc) | 布尔值(Boolean) | Object.defineProperty(proxy, propKey, propDesc) |
deleteProperty(target, propKey) | 布尔值 | delete proxypropKey |
enumerate(target) | 遍历器 | for (x in proxy) |
get(target, propKey, receiver) | 类型不限 | 对象属性的读取 |
set(target, propKey, value, receiver) | 布尔值 | 对象属性的设置 |
getOwnPropertyDescriptor(target, propKey) | 属性的描述对象 | Object.getOwnPropertyDescriptor(proxy, propKey) |
getPrototypeOf(target) | 对象 | Object.getPrototypeOf(proxy) |
has(target, propKey) | 布尔值 | propKey in proxy |
isExtensible(target) | 布尔值 | Object.isExtensible(proxy) |
ownKeys(target) | 数组 | Object.getOwnPropertyPropertyNames(proxy)、Object.getOwnPropertyPropertySymbols(proxy)、Object.keys(proxy) |
preventExtensions(target) | 布尔值 | Object.preventExtensions(proxy) |
setPrototypeOf(target, proto) | 布尔值 | Object.setPrototypeOf(proxy, proto) |
apply(receiver, ...args) | 不限 | 函数的调用、call和apply操作 |
construct | 对象 | Proxy实例作为构造函数调用的操作,比如new proxy(···) |
操作 | 作用 |
---|---|
add | 添加属性 |
update | 属性值的变化 |
delete | 删除属性 |
setPrototype | 设置原型 |
reconfigure | 属性的attributes对象发生变化 |
preventExtensions | 对象被禁止扩展 |
ES6允许为函数的参数设置默认值,使用=形式直接写在参数定义的后面。
使用注意事项:
function Point(x = 0, y = 0) {
this.x = x;
this.y = y;
}
var p = new Point();
// p = { x:0, y:0 }
fetch(url, { method='GET' } = {}){
console.log(method);
}
...+变量名形式与...+数组形式相当于互逆操作:
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3) // 10
(function(a, ...b) {}).length // 1
const date = new Date(...[2015, 1, 1]);
函数定义的简化表示法,
var f = () => 5;
// 等同于
var f = function (){ return 5 };
var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
return num1 + num2;
};
使用箭头函数需要注意:
为什么尾调用会优化:函数调用会在内存形成一个“调用记录”,又称“调用帧”(call frame),保存调用位置和内部变量等信息。如果在函数A的内部调用函数B,那么在A的调用帧上方,还会形成一个B的调用帧。等到B运行结束,将结果返回到A,B的调用帧才会消失。如果函数B内部还调用函数C,那就还有一个C的调用帧,以此类推。所有的调用帧,就形成一个“调用栈”(call stack)。尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用帧,因为调用位置、内部变量等信息都不会再用到了,只要直接用内层函数的调用帧,取代外层函数的调用帧就可以了。所以如果所有函数都是尾调用,那么完全可以做到每次执行时,调用帧只有一项,这将大大节省内存。
尾调用的应用——尾递归:递归非常耗费内存,因为需要同时保存成千上百个调用帧,很容易发生“栈溢出”错误(stack overflow)。但对于尾递归来说,由于只存在一个调用帧,所以永远不会发生“栈溢出”错误。比如阶乘函数的为递归改写:
function factorial(n, total) {
if (n === 1) return total;
return factorial(n - 1, n * total);
}
factorial(5, 1) // 120
Set结构类似于数组,但是成员的值都是唯一的,没有重复的值。
var items = new Set([1,2,3,4,5,5,5,5]);
for (i of items) {console.log(i)}
// 1 2 3 4 5
items.size
// 5
注意:向Set加入值的时候,不会发生类型转换,所以5和“5”是两个不同的值。Set内部判断两个值是否不同,使用的算法类似于精确相等运算符(===),唯一的例外是NaN等于自身。这意味着,两个对象总是不相等的。
let set = new Set();
set.add({})
set.size // 1
set.add({})
set.size // 2
Set结构有以下属性:
Set数据结构有以下方法:
Set结构有一个values方法,返回一个遍历器,同时Set结构的默认遍历器就是它的values方法,所以可以直接用for...of循环进行遍历。
WeakSet是一个与Set类似的结构,也是不重复的值的集合。但是,它与Set有两个区别:
注意Map的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。这就解决了同名属性碰撞(clash)的问题,我们扩展别人的库的时候,如果使用对象作为键名,就不用担心自己的属性与原作者的属性同名。只有对同一个对象的引用,Map结构才将其视为同一个键。
var map = new Map();
map.set(['a'], 555);
map.get(['a']) // undefined
Map结构的属性和方法:
同样与WeakSet类似,WeakMap结构与Map结构基本类似,唯一的区别是它只接受对象作为键名(null除外),不接受原始类型的值作为键名,而且键名所指向的对象,不计入垃圾回收机制,有助于防止内存泄漏。
遍历器属于一种接口规格,任何数据结构只要部署这个接口,就可以完成遍历操作,即依次处理该结构的所有成员。它的作用有两个,一是为各种数据结构,提供一个统一的、简便的接口,二是使得数据结构的成员能够按某种次序排列。
简化的讲,所谓Iterator接口,就是指调用这个接口,会返回一个遍历器对象。该对象具备next方法,每次调用该方法,会返回一个具有value和done两个属性的新对象,指向部署了Iterator接口的数据结构的一个成员。下面代码就展示了一个典型的Iterator接口:
function idMaker(){
var index = 0;
return {
next: function(){
return {value: index++, done: false};
}
}
}
var it = idMaker();
it.next().value // '0'
it.next().value // '1'
it.next().value // '2'
当使用for...of循环,遍历某种数据结构时,该循环会自动去寻找Iterator接口。在ES6中,有三类数据结构原生具备Iterator接口:数组、某些类似数组的对象、Set和Map结构。
默认的Iterator接口部署在数据结构的Symbol.iterator属性,也就是说调用Symbol.iterator方法,就会得到当前数据结构的默认遍历器。
let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator]();
iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }
const arr = ['red', 'green', 'blue'];
let iterator = arr[Symbol.iterator]();
for(let v of arr) {
console.log(v); // red green blue
}
for(let v of iterator) {
console.log(v); // red green blue
}
除了上述三类之外,其他数据结构(主要是对象)的Iterator接口,都需要自己在Symbol.iterator属性上面部署,这样才会被for...of循环遍历。
function Obj(value){
this.value = value;
this.next = null;
}
Obj.prototype[Symbol.iterator] = function(){
var iterator = {
next: next
};
var current = this;
function next(){
if (current){
var value = current.value;
var done = current == null;
current = current.next;
return {
done: done,
value: value
}
} else {
return {
done: true
}
}
}
return iterator;
}
var one = new Obj(1);
var two = new Obj(2);
var three = new Obj(3);
one.next = two;
two.next = three;
for (var i of one){
console.log(i)
}
// 1
// 2
// 3
进行以下操作时,会自动去调用默认iterator接口:
Generator是一个普通函数,有两个特征:一是function命令与函数名之间有一个星号;二是函数体内部使用yield语句,定义遍历器的每个成员。
Generator可以理解成一个函数的内部状态的遍历器,每调用一次,函数的内部状态发生一次改变。换句话说,它就是一个改装了的 Iterator 遍历器,通过 yield 来增加一个 next() 节点。
先看一下Generator函数的完整作用: 用Generator函数定义helloWorldGenerator,它的遍历器有两个成员“hello”和“world”。当调用Generator函数的时候,该函数并不执行,而是返回一个遍历器(可以理解成暂停执行)。以后,每次调用这个遍历器的next方法,就从函数体的头部或者上一次停下来的地方开始执行(可以理解成恢复执行),直到遇到下一个yield语句为止。
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }
for...of循环可以自动遍历Generator函数,且此时不再需要调用next方法。
function *foo() {
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
return 6;
}
for (let v of foo()) {
console.log(v);
}
// 1 2 3 4 5
yield语句本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数会被当作上一个yield语句的返回值。这个功能有很重要的语法意义,Generator函数从暂停状态到恢复运行,它的上下文状态(context)是不变的。通过next方法的参数,就有办法在Generator函数开始运行之后,继续向函数体内部注入值。
function* foo(x) {
var y = 2 * (yield (x + 1));
var z = yield (y / 3);
return (x + y + z);
}
var it = foo(5);
it.next()
// { value:6, done:false }
it.next(12)
// { value:8, done:false }
it.next(13)
// { value:42, done:true }
如果yield命令后面跟的是一个遍历器,需要在yield命令后面加上星号,表明它返回的是一个遍历器,则遍历有递归的效果。如果yield*后面跟着一个数组,就表示该数组会返回一个遍历器,因此就会遍历数组成员。
function* iterTree(tree) {
if (Array.isArray(tree)) {
for(let i=0; i < tree.length; i++) {
yield* iterTree(tree[i]);
}
} else {
yield tree;
}
}
const tree = [ 'a', ['b', 'c'], ['d', 'e'] ];
for(let x of iterTree(tree)) {
console.log(x);
}
// a
// b
// c
// d
// e
使用yield*语句遍历完全二叉树的应用如下:
// 下面是二叉树的构造函数,
// 三个参数分别是左树、当前节点和右树
function Tree(left, label, right) {
this.left = left;
this.label = label;
this.right = right;
}
// 下面是中序(inorder)遍历函数。
// 由于返回的是一个遍历器,所以要用generator函数。
// 函数体内采用递归算法,所以左树和右树要用yield*遍历
function* inorder(t) {
if (t) {
yield* inorder(t.left);
yield t.label;
yield* inorder(t.right);
}
}
// 下面生成二叉树
function make(array) {
// 判断是否为叶节点
if (array.length == 1) return new Tree(null, array[0], null);
return new Tree(make(array[0]), array[1], make(array[2]));
}
let tree = make([[['a'], 'b', ['c']], 'd', [['e'], 'f', ['g']]]);
// 遍历二叉树
var result = [];
for (let node of inorder(tree)) {
result.push(node);
}
result
// ['a', 'b', 'c', 'd', 'e', 'f', 'g']
function* main() {
var result = yield request("http://some.url");
var resp = JSON.parse(result);
console.log(resp.value);
}
function request(url) {
makeAjaxCall(url, function(response){
it.next(response);
});
}
var it = main();
it.next();
step1(function (value1) {
step2(value1, function(value2) {
step3(value2, function(value3) {
step4(value3, function(value4) {
// Do something with value4
});
});
});
});
function* longRunningTask() {
try {
var value1 = yield step1();
var value2 = yield step2(value1);
var value3 = yield step3(value2);
var value4 = yield step4(value3);
// Do something with value4
} catch (e) {
// Handle any error from step1 through step4
}
}
function scheduler(task) {
setTimeout(function() {
var taskObj = task.next(task.value);
// 如果Generator函数未结束,就继续调用
if (!taskObj.done) {
task.value = taskObj.value
scheduler(task);
}
}, 0);
}
scheduler(longRunningTask());
function* iterEntries(obj) {
let keys = Object.keys(obj);
for (let i=0; i < keys.length; i++) {
let key = keys[i];
yield [key, obj[key]];
}
}
let myObj = { foo: 3, bar: 7 };
for (let [key, value] of iterEntries(myObj)) {
console.log(key, value);
}
// foo 3
// bar 7
先解释下在Promises/A规范中,每个任务都有三种状态:默认(pending)、完成(fulfilled)、失败(rejected)。
ES6的Promise对象是一个构造函数,用来生成Promise实例。Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve方法和reject方法。
promise实例生成以后,可以用then方法分别指定resolve方法和reject方法的回调函数。
var promise = new Promise(function(resolve, reject) {
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
promise.then(function(value) {
// success
}, function(value) {
// failure
});
Promise.prototype.then方法返回的是一个新的Promise对象,因此可以采用链式写法。多个then执行时前一个回调函数完成以后,会将返回结果作为参数,传入后一个回调函数。如果前一个回调函数返回的是Promise对象,这时后一个回调函数就会等待该Promise对象有了运行结果,才会进一步调用。
Promise.prototype.catch方法是Promise.prototype.then(null, rejection)的别名,用于指定发生错误时的回调函数。
getJSON("/posts.json").then(function(posts) {
// some code
}).catch(function(error) {
// 处理前一个回调函数运行时发生的错误
console.log('发生错误!', error);
});
Promise.all():用于将多个Promise实例,包装成一个新的Promise实例。
var p = Promise.all([p1,p2,p3]);
p的状态由p1、p2、p3决定,分成两种情况:
Promise.race():与Promise.all()形式类似,不同的是只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的Promise实例的返回值,就传递给p的返回值。
Class定义了一个“类”,constructor方法表示构造方法,而this关键字则代表实例对象。
//定义类
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '('+this.x+', '+this.y+')';
}
}
Point.prototype在ES6继续存在,也就是说,除了constructor方法以外,类的方法都定义在类的prototype属性上面。
var point = new Point(2, 3);
point.toString() // (2, 3)
point.hasOwnProperty('x') // true
point.hasOwnProperty('y') // true
point.hasOwnProperty('toString') // false
point.__proto__.hasOwnProperty('toString') // true
Class之间可以通过extends关键字,实现继承。
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // 等同于parent.constructor(x, y)
this.color = color;
}
toString() {
return this.color + ' ' + super.toString(); // 等同于parent.toString()
}
}
注意事项:
Class作为构造函数的语法糖,同时有prototype属性和proto属性:
class B extends A {
}
B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true
有三种特殊情况需要注意:
class A extends null {
}
A.__proto__ === Function.prototype // true
A.prototype.__proto__ === null // true
ES6模块通过export命令显式指定输出的代码,输入时也采用静态命令的形式。
// profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export {firstName, lastName, year};
// main.js
import {firstName, lastName, year} from './profile';
function sfirsetHeader(element) {
element.textContent = firstName + ' ' + lastName;
}
module命令可以取代import语句,达到整体输入模块的作用。
// circle.js
export function area(radius) {
return Math.PI * radius * radius;
}
export function circumference(radius) {
return 2 * Math.PI * radius;
}
// main.js
module circle from 'circle';
console.log("圆面积:" + circle.area(4));
console.log("圆周长:" + circle.circumference(14));
export default命令定义模块的默认方法。
// export-default.js
export default function () {
console.log('foo');
}
// import-default.js
import customName from './export-default';
customName(); // 'foo'
模块之间的继承使用这条命令:
// circleplus.js
export * from 'circle';
circleplus模块,继承了circle模块,“export *”表示输出circle模块的所有属性和方法。
浏览器目前还不支持ES6模块,为了现在就能使用,可以将其转为ES5的写法,目前比较好用的工具有:
整个ES6的新特性和用法学习下来,觉得比较有用的点如下: