makeIterator是个遍历器,生成遍历器对象itnode
var it = makeIterator(['a', 'b']); it.next() // { value: "a", done: false } it.next() // { value: "b", done: false } it.next() // { value: undefined, done: true } function makeIterator(array) { var nextIndex = 0; return { next: function() { return nextIndex < array.length ? {value: array[nextIndex++], done: false} : {value: undefined, done: true}; } }; }
对象具备Symbol.iterator
方法就是部署了iterable接口,但若是Symbol.iterator
返回的不是遍历对象,会报错。下面对象部署了iterable接口git
var obj = { value : 1, [Symbol.iterator](){ let me = this return { next(){ return { value: me.value++, done: me.value >= 10 } } } } }
iterable接口为数据结构提供统一的数据访问机制,具备线性访问特色。es6
原生部署的有:数组、相似数组对象、Set和Map,数组、Set、Map的entries、keys、values方法的返回。
相似数组对象如 arguments对象 DOM NodeList对象;github
对象部署Iterator接口:ajax
function* entries(obj){ for(let key of Object.keys(obj)){ yield [key, obj[key]] } }
解构赋值、扩展运算符、yield *、for…of、Array.from、Map()、Set() Promise.all Promise.tracechrome
for…of
对比for…in
for…in编程
获取键名,无序的,循环对象全部可枚举的属性,做用数组时,返回的key是字符串json
for…of数组
of后面跟遍历器对象,或者部署了遍历器接口的数据类型。返回有序元素,相对与forEach能够break、continuepromise
若是跟makeIterator,会报错
for(let v of [1,2,3]){console.log(v)} for(let v of [1,2,3][Symbol.iterator]()){console.log(v)}//两结果同样
通常用在for...of循环中提早退出break时或者throw,会执行定义在遍历器中return方法,return方法必须返回一个对象
'use strict'; /** * node v6.9.0 chrome52下执行return * @type {{cur: number}} */ var obj = { cur: 1, next() { return { value: this.cur++, done: false } }, return() { this.cur = 0 console.log('execute iterator\'s method of return') return { done: true } }, [Symbol.iterator]() { // 没有会报错 return this; } }; for (let v of obj[Symbol.iterator]()) { //throw new Error(); 也会执行return if (v > 10) { break; } if (v == 8) { continue; } console.log(v); }
g.return(data) 返回{value:data,done:true},nodeJs6.2.2支持。
/** * return nodeJs暂不支持,调用会报错,nodeJs6.2.2支持 */ function* gen(){ yield 1; try{ yield 11; yield 22; yield 33; }finally{ console.log('a') yield 21; yield 22; } yield 2; } let itr = gen() console.log(itr.next()) // { value: 1, done: false } console.log(itr.return(31)) // { value: 31, done: true } console.log(itr.next()) // { value: undefined, done: true } console.log(itr.next()) // { value: undefined, done: true } console.log(itr.next()) // { value: undefined, done: true }
g.throw()
遍历器对象能够执行throw,generator函数内部有try...catch
,再被catch,直到运行结束或者执行时遇到下一个next。若是没被捕获,好比连续throw两次,会抛到generator外面。
/** * 一开始没执行next,就执行throw,内部没法捕获。 */ function* gen(){ try{ console.log('start') yield 1; }catch(e){ console.log('内部捕获',e) } yield 2; } var g = gen(); try{ //g.throw('a'); console.log(g.next()) console.log(g.throw('b')) }catch(e){ console.log('外部捕获',e) } /** * 遍历器对象连续两次throw */ function* errCnt() { try { yield 5 // { value: 5, done: false } } catch (e) { console.log('内部捕获', e) //throw new Error('cry') } yield 6 yield 7 } var itr = errCnt() console.log(itr.next()) console.log(itr.throw('a')) // { value: 6, done: false } try { console.log(itr.throw('a')) } catch (e) { console.log('外部捕获', e) console.log(itr.next()) // { value: undefined, done: true } }
Generator函数体内报错,外部捕获后,下次还执行next,done为true,value undefined
'use strict'; function* foo() { let x = yield 3; let y = x.toUpperCase(); yield y; yield 5; } var it = foo(); console.log(it.next()); // { value:3, done:false } try { console.log(it.next(42)); } catch (err) { console.log(err); console.log(it.next()); // {value: undefined, done: true} }
Generator的遍历器对象抛错后,内部没捕获,外部捕获,下次执行next,同上
AA必须是部署了遍历器接口的数据结构,A若是是Generator生成的,且Generator有return,会将值赋值到a。
function* gen1(){ yield 1; yield 2; return 3; } function* gen2(gen){ yield 11; let rtn = yield* gen(); console.log('rtn: ' + rtn) yield 22; } let itr1 = gen1() console.log(itr1.next()) // { value: 1, done: false } console.log(itr1.next()) // { value: 2, done: false } console.log(itr1.next()) // { value: 3, done: true } let itr2 = gen2(gen1) console.log(itr2.next()) // { value: 11, done: false } console.log(itr2.next()) // { value: 1, done: false } console.log(itr2.next()) // { value: 2, done: false } // rtn: 3 console.log(itr2.next()) // { value: 22, done: false } console.log(itr2.next()) // { value: undefined, done: true } for(let v of gen2(gen1)){ console.log(v) }
AA若是直接是Generator,会报错;for...of若是直接是Generator,也会报错。
function* gen(){ yield 1; yield 2; yield this.name = ’sprying' } gen.prototype.sayHi = () =>console.log(‘hi') const g = gen() g.next() g.next() g.next() g.sayHi() g.name // null
若是上面const g = gen()换成
const g = gen.apply(gen.prototype)
这时候g.name就有值
注意new gen会报错
Generator函数的原型时Generator.prototype(假设是ownProto);
ownProto原型是sharedProto,sharedProto.hasOwnProperty("next")
sharedProto的原型是iterProto,iterProto.hasOwnProperty(Symbol.iterator)
函数遇到异步操做时,yield暂停,异步回调触发时,再继续执行。具体实现流程以下:
promise
yield返回结果是promise对象,异步响应时,触发then,即
// 业务代码 function * gen(){ yield new Promise((resolve, reject) => { setTimeout(()=>{ resolve({ok: true}) }, 500) }) } // 执行器背后的核心处理逻辑,执行next后,value是promise promise.then(function(data){ g.next(data) })
thunkify
yield返回参数是函数,传入回调,执行这个函数。异步响应时,执行回调,回调的参数是data,即。
// 业务代码 function * gen(){ yield function(callback){ setTimeout(()=>{ callback({ok: true}) }, 500) } } // 执行器背后的核心处理逻辑 hook((data) = >{ g.next(data) })
假设下面场景
fs.readFile('/etc/passwd', (err, data) => { if (err) throw err; console.log(data); }); yield function(callback){ fs.readFile('/etc/passwd', (err, data) => { if (err) throw err; callback(data) }); }
每次都这样写,是否是很麻烦?咱们能够封装个函数
function thunkify(callback){ return function(...args){ return function(hook){ callback(...[...args, hook]) } } } // 下面就简洁了不少 const readFile = thunkify(fs.readFile) function * gen(){ yield readFile(filename) }
function* gen(){ yield 1; yield 2; yield 3; return 4; } for(let v of gen()){ console.log(v) }//1,2,3
resolve能够传入下一个promise实例
/** * 注意执行顺序 */ setTimeout(()=>console.log(1),0) new Promise(function (resolve, reject) { console.log(2); x/2; resolve(); }).then(function () { console.log(3) //throw new Error('err') },function(err){ console.log(err) }); console.log(4)
错误没被捕获,触发下面
process.on('unhandledRejection', function (err, p) {}) /** * reject未被catch,会被传递到unhandledRejection * Promise内部throw没被catch,会被传递到unhandledRejection,其它不作任何处理 * throw后有catch,还有then,可是then应该是catch生成的默认resolve的promise */ new Promise((resolve, reject) => { //reject(new Error('err')) //setTimeout(() => { throw new Error('err-1') //}, 0) //resolve('done') }) .catch(err => console.log('err: ', err)) .then((data) => { console.log('then: ' + data) }) process.on('unhandledRejection', function (err, p) { //console.error(err.stack) }); var someAsyncThing = function () { return new Promise(function (resolve, reject) { // 下面一行会报错,由于x没有声明 resolve(x + 2); }); }; // 注意执行时间 ///* someAsyncThing() .catch(function (error) { console.log('oh no', error); }) .then(function () { console.log('carry on'); }); //*/
参数是具备Iterator接口的对象,若是都是fulfilled,触发的then的参数回调的参数是数组。若是首先一个出来rejected,错误捕获的回调参数是首先出现错误那个。Promise.all对应的各个值若是不是Promise,调用Promise.resolve
谁先改变状态,结果和回调的参数就听谁
var jsPromise = Promise.resolve($.ajax('/whatever.json'));
参数是个thenable对象,当即执行thenable对象的then
参数是其它值,好比字符串
参数是空的
/** * Created by yingchun.fyc@alibaba-inc.com on 16/7/13. */ let thenable = { then(resolve, reject){ resolve('thenable') } } Promise.resolve(thenable).then(res => console.log(res)) var p = Promise.resolve('hi') p.then(res => console.log(res)) // 输出 // hi // thenable
参数同上
generator函数容许执行暂停。当遇到yield时暂停,Js代码能够控制继续执行,好比控制yield后面的异步响应后,继续执行。暂停、和继续执行是咱们写的Js代码控制,也就是,实现个执行器,让这些操做自动执行,就能够实现同步方式写异步代码。
执行器是个函数,返回promise。约定yield后面跟promise或者thunk函数。执行器执行到g.next(),返回obj,obj.value里的异步响应后,若是obj.done为false,继续执行,将响应的值传给g.next(data),如此循环。若是何时g.next,返回的done是true,那么等待value异步响应后,resolve响应的值,结束循环。
若是g.next时,报错,首先g.throw给generator处理,处理完继续next;处理失败,直接结束。若是异步响应出错,一样处理。thunk函数,异步响应时执行callback,规定第一参数传错误信息。
要并行处理异步时,yield后面跟数组、对象,执行器Promise.all下处理。
若是Generator函数,再套了一个Generator,放在yield后面。执行器里就再调一次执行器。
上面就是co的实现原理。
如今咱们反思这样一个问题,同步方式写异步代码,遇到异步时,程序等待异步响应后,再继续执行,那么nodeJs异步处理优点就没了吗?一开始,我也是这么认为的,可是co执行Generator函数时,虽然内部yield暂停了,可是整个Generator并无所以卡住,仍是会继续执行co后续逻辑。因此nodeJs异步处理的优点还在。
async属于ES7,不过引入babel的transform-async-to-generator就能够转码使用。await在ES6是保留字,ES5中使用它是合法。
普通函数中使用await会报错。
Async函数有多种使用形式。
// 函数声明 async function foo() {} // 函数表达式 const foo = async function () {}; // 对象的方法 let obj = { async foo() {} }; // 箭头函数 const foo = async () => {};
async、await组合跟执行器co很像,只是它再也不须要执行器了,像通常函数调用,返回的仍然是Promise。await相比co的yield,后面能够是基本数据类型。
至于错误处理机制,实际上跟co结果同样。不想结束,也是须要try...catch。或者promise后加catch。
至于并发执行,先都生成Promise实例,而后await。或者Promise.all([asyncMethod1(), asyncMethod2()])
/** * Created by yingchun.fyc@alibaba-inc.com on 16/9/12. */ async function f() { try { await Promise.reject('出错了'); } catch(e) { } return await Promise.resolve('hello world'); } f() .then(v => console.log(v)) async function dbFuc(fn) { let docs = [1,2,3]; let promises = docs.map((doc) => fn(doc)); let results = await Promise.all(promises); console.log(results); } // 或者使用下面的写法 // 两个运行实际差很少,由于都是先生成了promise,再调用await。await、yield,参数默认值都是延迟执行的。 async function dbFuc1(fn) { let docs = [4,5,6]; let promises = docs.map((doc) => fn(doc)); let results = []; for (let promise of promises) { results.push(await promise); } console.log(results); } var startTime = (new Date()).getTime() dbFuc1((data) => { return new Promise((resolve, reject) => { setTimeout(()=>{ resolve(data) }, 1000) }) }).then(() =>{ console.log((new Date().getTime() - startTime)) })
关键字class定义类,方法之间没有,
,方法名能够是变量[defineVar],定义的class只能new,不然报错。方法名不可枚举。构造函数在constructor方法中定义
可使用Object.assign新增方法。
class 不存在变量提高
class表达式
let myClass = class Me {} //Me.name只能内部调用,Me也只能内部用 myClass.name // Me
实例的__proto__,指向原型对象
私有方法
使用Symbol定义方法名
const bar = Symbol(“bar")
继承重写constructor,要在constructor中先调用super(),不然会报错。
super在对象方法中均可以使用,可是奇怪的是,下面第一个正常,第二个会报错。
var obj = { toString() { return "MyObject: " + super.toString(); } }; var obj = { toString: function() { return "MyObject: " + super.toString(); } };
能够继承原生的构造函数,可是继承Object时,没法向父类的构造函数传入参数。
相比es5的继承不能继承原生的构造函数,即便es5继承了,也不具有原生的能力。
Object.getPrototypeOf获取类的父类
Object.getPrototypeOf(B) === A Object.setPrototypeOf Object.setPrototypeOf = function (obj, proto) { obj.__proto__ = proto; return obj; }
方法名前可加set/get,而且是定义在descriptor上,Object.getOwnPropertyDescriptor
方法名前可加static
es7才支持属性名直接在类里定义
关于class具体使用例子,这里就不贴出来了。
支持嵌套,等号右边是可遍历(iterator)的结构
等号两边能够不彻底匹配,或左边小,或右边没相应值(这时undefined)
支持默认值,默认值若是是表达式,表达式是惰性求值的
let [x=y,y=1] = [1,2] let [a=b,b=1] = []// Uncaught ReferenceError: b is not defined(…) var [a, , [b], c] = [5, null, [6]]; var [a, , [b], c] = [5, undefined, [6]]; var [a, , [b], c] = [5, , [6]];// a = 5; b = 6; c = undefined var [a, b, c] = "ab"; // a = 'a'; b = 'b'; c = undefined var [c] = "𠮷𠮶"; // c = "𠮷" var [a,] = [1]; // a = 1
变量名和属性名不一致,是经过模式对应起来
var { foo: baz } = { foo: 'aaa', bar: 'bbb' }; baz // "aaa" foo // Uncaught ReferenceError: foo is not defined(…)
声明语句、赋值语句
let foo; let {foo} = {foo: 1};// Uncaught SyntaxError: Identifier 'foo' has already been declared ({foo} = {foo: 1})// 不报错
嵌套
var node = { loc: { start: { line: 1, column: 5 } } }; var { loc: { start: { line }} } = node; line // 1 loc // Uncaught ReferenceError: loc is not defined(…) start // error: start is undefined let obj = {}; let arr = []; ({ foo: obj.prop, bar: arr[0] } = { foo: 123, bar: true }); obj // {prop:123} arr // [true]
默认值
var {x:y =3} = {x:5} // y 5
赋值时严格的等于undefined
解构失败
var {foo} = {bar: 'baz'}; foo // undefined
其它
var {foo: {bar}} = {baz: 'baz'}; Uncaught TypeError: Cannot match against 'undefined' or 'null'.(…) var qux = "corge"; var { [qux]: grault } = { corge: "garply" } // grault = "garply"
解构赋值容许,等号左边的模式之中,不存在任何变量名
({} = [true, false]);
能够将对象的方法,解构赋值给变量
let { log, sin, cos } = Math;
字符串、数值、布尔值的解构赋值
let {length : len} = 'hello'; let {toString: s} = 123; s === Number.prototype.toString // true
解构赋值的规则是,只要右边的值不是对象,先转为对象。因为undefined和null没法转为对象,因此解构赋值时,会报错
函数参数的解构赋值
function move({x = 0, y = 0} = {}) { return [x, y]; }
函数参数的默认值
[1, undefined, 3].map((x = 'yes') => x);
可使用()场景
赋值语句,非模式部分
[(b)] = [3]; // 正确 ({ p: (d) } = {}); // 正确 [(parseInt.prop)] = [3]; // 正确 var a, b; ({a,b} = {a:1,b:2}); try { eval("({a,b}) = {a:3,b:4};");//报错 } catch(e) { return a === 1 && b === 2; } var [a,b] = [5,6], {c,d} = {c:7,d:8};
var [c, ...d] = [6]; // c = 6; d.length=0
[x, y] = [y, x];
函数返回多个值
var [a, b, c] = example();
函数参数的默认值
jQuery.ajax = function (url, { async = true, beforeSend = function () {}, cache = true, complete = function () {}, crossDomain = false, global = true, // ... more config }) { // ... do stuff };
遍历Map结构
var map = new Map(); map.set('first', 'hello'); map.set('second', 'world'); for (let [key, value] of map) { console.log(key + " is " + value); }
输入模块的指定方法
const { SourceMapConsumer, SourceNode } = require("source-map");
函数参数若是是对象的解构赋值,执行的时候对象的解构赋值要有值,不论是函数参数默认值或者是传入的值
函数参数的默认值是个表达式,表达式会延迟执行
函数参数默认值的做用域,是函数的做用域
var x = 1; function foo(x, y = function() { x = 2; }) { var x = 3; y(); console.log(x); } foo() // 3
感受比较奇怪,按理来讲,y执行时的做用域,就是定义时的做用域,参数中的做用域都是函数范围内做用域。因此参数中x就应该与下面一致。实际上最后输出的x为3
把var换成let,在chrome 52下就报错了
函数的参数默认值位置
建议位于后面几个参数,若是有默认值的参数,后面还有要输入的参数,执行的时候,foo(undefined, 1)
函数的length
除去有默认值参数,以及后面的全部参数
function testfn1(a = 1, x, h, y =2){} testfn1.length // 0
function throwIfMissing(){ throw new Error('Missing parameter') } function foo(mustBeProvided = throwIfMissing()){ return mustBeProvided } foo()
对应的变量是个数组,后面不能再跟其它参数,function的length是除了rest参数的函数长度;
arguments.length仍是按之前方式
(function (foo, ...args) { foo = "qux"; // The arguments object is not mapped to the // parameters, even outside of strict mode. return arguments.length === 3 && arguments[0] === "foo" && arguments[1] === "bar" && arguments[2] === "baz"; }("foo", "bar", "baz"))
后跟部署iterale接口的数据结构,将数据结构变成逗号相隔的参数序列
console.log(1, ...[2, 3, 4], 5) function add(x, y) { return x + y; } var numbers = [4, 38]; add(...numbers) // 42 let arrayLike = { '0': 'a', '1': 'b', '2': 'c', length: 3 }; [...arrayLike] Uncaught TypeError: arrayLike[Symbol.iterator] is not a function(…) Array.from(arrayLike) ["a", "b", "c"]
函数表达式定义方式中,相比es5,es6中函数的name是被赋值的名字。
bind的函数,返回bound开头
let fn1 = function(){} let fn2 = fn1 n2.name //fn1 (new Function).name //anonymous fn1.bind({}).name //bound fn1 (function(){}).name // (function(){}).bind({}).name //"bound "
let arrowFn1 = ({x,y}) => [x,y] let arrowFn2 = (...arr) => [...arr]
不具备this,不能当作构造函数,没有本身的arguments。
this指向定义时所处的this,使用bind无效
es7内容,前面是绑定的对象,后面是函数,babel转码器已经支持
直接量写法中,容许属性值省略,方法简写,容许属性名或方法名使用[]包围的表达式,但这时不能省略属性值。
let obj = { * m(){ yield 'hello world' } } module.export = {add, remove}
对象方法也有name,同函数name状况,可是这里,若是是Symbol,返回的是Symbol描述。若是是set、get方法,返回加上前缀set get
Object.assign 第一个参数是目标对象,没法转为对象(undefined\null)时会报错,源对象是任意,当是数字、布尔值、undefined\null时,忽略。
Object.assign 拷贝自身可枚举的对象属性、Symbol属性,浅拷贝
Object.assign 一个参数时,返回自身
Object.assign(true) // Boolean {[[PrimitiveValue]]: true} Object.assign([1, 2, 3], [4, 5]) // [4, 5, 3]
克隆对象
Object.assign({}, origin)
克隆继承属性
function clone(origin){ originObj = Object.getPrototypeOf(origin) return Object.assign(Object.create(originObj), origin) }
JSON.stringify() 串行化对象自身可枚举属性
getOwnPropertyDescriptor
Object.getOwnPropertyDescriptor([], 'length').enumerable // false 我觉得要在原型上取到length,由于Object.getOwnPropertyDescriptor({},"toString”)// null
for…of
包括继承的全部可枚举的属性,不包含SymbolObject.keys
全部自身可枚举的属性,不包含SymbolObejct.getOwnPropertyNames
自身全部属性,包括不可枚举,不包含SymbolObject.getOwnPropertySymbols(obj)
Reflect.ownKeys
返回全部自身属性的数组,不论是不是可枚举、Symbol遍历次序
按数字排序;字符串属性,按生成时间排序;Symbol属性,按生成时间排序
Same-value equality,相对于===,但NaN等于自身,+0不等-0
本身实现以下
Object.defineProperty(Object, ‘is’, { value: function(x, y){ if(x === y){ return x ===0 || 1/x === 1/y } return x !==x && y !==y }, configurable: true, enumerable: false, writable: true })
__proto__
es6附录中,只规定浏览器端支持,为了保险,仍是用
Object.setPrototypeOf`` Object.getPrototypeOf
Object.create
Object.getPrototypeOf({__proto__: null})// null
Object.values
es7 提案,取值规则、顺序同keys,
Object.values(2)//[] Object.values(true)//[] Object.keys(2)//[]
Object.entries
同 keys
var obj = { foo: 'bar', baz: 42 }; var map = new Map(Object.entries(obj)); map // Map { foo: "bar", baz: 42 }
es7的提案,试了下,在chrome 52中不能识别
let { x, y, ...z } = null; // 运行时错误 let { ...x, y, z } = obj; // 句法错误
浅拷贝
let obj = { a: { b: 1 } }; let { ...x } = obj; obj.a.b = 2; x.a.b // 2
不会拷贝继承自原型对象的属性
function baseFunction({ a, b }) { // ... } function wrapperFunction({ x, y, ...restConfig }) { // 使用x和y参数进行操做 // 其他参数传给原始函数 return baseFunction(restConfig); }
let z = { a: 3, b: 4 }; let n = { ...z }; n // { a: 3, b: 4 } // 等同于 let aClone = Object.assign({}, a); let ab = { ...a, ...b }; // 等同于 let ab = Object.assign({}, a, b); let newVersion = { ...previousVersion, name: 'New Name' // Override the name property }; let emptyObject = { ...null, ...undefined }; // 不报错
扩展运算符的参数对象之中,若是有取值函数get,这个函数是会执行的。
// 并不会抛出错误,由于x属性只是被定义,但没执行 let aWithXGetter = { ...a, get x() { throws new Error('not thrown yet'); } }; // 会抛出错误,由于x属性被执行了 let runtimeError = { ...a, ...{ get x() { throws new Error('thrown now'); } } };
let 块做用域内声明,不存在变量提高,打破了typeof在es5语法中永不报错,
{ typeof varmy // nodeJs端报错,ReferenceError: varmy is not defined let varmy } typeof unbar //不报错,结果undefined
不能重复声明
{ var x = 1 let y = 2 } function foo(x=y, y=1){ return {x,y} }
const命令声明时就需赋值,不然报错,后续不能改值,若是指向对象,只要指向对象地址不变,均可以。其它同let。
若是想冻结对象,Object.freeze。
var constantize = (obj) => { Object.freeze(obj); Object.keys(obj).forEach( (key, value) => { if ( typeof obj[key] === 'object' ) { constantize( obj[key] ); } }); };
es5中两种声明变量,var function
es6中 新增let const class import
SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode
浏览器端let const class声明的变量,再也不是全局对象的属性
浏览器端的模块化方案AMD,NodeJs端的CommonJs;都是运行时加载。ES6的模块化是编译时加载。
with
语句arguments.caller
和arguments.callee
protected
、static
、interface
export var i = 1 export function fn(){} export class Person{} // 或者先定义,后扔出 export {i, fn, Person} // 重命名 export { i as j, fn as fn1, Person as Man }
export导出的变量,值是动态绑定的;export必须处于模块的顶层,不然报错
大括号里的变量名和要导出文件里的变量名要一致;或者使用as重命名。import具备命名提高效果。
import能够只执行后面文件
import 'exec.js'
先导入后导出,能够简写
import {yo} from "yo.js" export default yo // 简写后 export {yo as default} from "yo.js"
导出的时候,能够只导出一个默认值,这样import时,不用关心要导入文件的对应的方法名和变量名,import时,能够取任一名字。
// yo.js function fn(){ //... } export default fn import method1 from 'yo.js'
除了这个特性外,default至关于一个特殊的名称,拥有通常名称所拥有的行为。
// yo.js function fn(){ // ... } export {fn as default} import {default} from 'yo.js'
export default var a = 1;// 会报错 export default function fn(){} import customName, { otherMethod } from './export-default'; export default 42; import * as obj from 'yo.js'
模块的继承
export * from 'yo.js'
export *
不会导出default
导出的变量,是动态的,变量的值变,导出的结果就变。若是导出是默认,默认值等于一个变量,变量的值变,默认值不变。若是导出的是个引用,不能对引用从新赋值
// lib.js export let obj = {}; // main.js import { obj } from './lib'; obj.prop = 123; // OK obj = {}; // TypeError