总结
ES6 import 循环依赖 惰性加载
amd 异步 依赖管理 (require.js)
cmd 异步 依赖就近 (sea.js)
commonjs 同步
PS:
cmd (还要解析一下字符串,比amd多一道工序)
define(function(require,exports,module) {
var clock = require('clock');
clock.start();
});
amd
require(['clock'],function(clock){
clock.start();
});
ES6模块不会缓存运行结果,而是动态地去被加载的模块取值,以及变量总是绑定其所在的模块。
ES6通过惰性加载解决了循环引用问题
ES6模块的运行机制与CommonJS(以及requireJS
= CommonJS的加载方式)不一样,它遇到模块加载命令import时,不会去执行模块,而是只生成一个引用。等到真的需要用到时,再到模块里面去取值。
因此,ES6模块是动态引用,不存在缓存值的问题,而且模块里面的变量,绑定其所在的模块。请看下面的例子。
ES6根本不会关心是否发生了"循环加载",只是生成一个指向被加载模块的引用,需要开发者自己保证,真正取值的时候能够取到值。
ES6不取值,而是给引用
// a.js
import {bar} from './b.js';
export function foo() {
bar();
console.log('执行完毕');
}
foo();
// b.js
import {foo} from './a.js';
export function bar() {
if (Math.random() > 0.5) {
foo();
}
}
按照CommonJS规范,上面的代码是没法执行的。a先加载b,然后b又加载a,这时a还没有任何执行结果,所以输出结果为null,即对于b.js来说,变量foo的值等于null,后面的foo()就会报错。
但是,ES6可以执行上面的代码。
- 默认参数
ES5
function point(x,y,isFlag){
x = x || 0;
y = y || -1;
isFlag = isFlag || true;
console.log(x,isFlag);
}
point(0,0) // 0 -1 true
point(0,false) // 0 -1 true
point(1) // 1 -1 true
point() // 0 -1 true
以上,如果默认参数是布尔值或将值设为 0,是没有用的。必须改为:
ES5
function point(x,isFlag){
x = x || 0;
y = typeof(y) === 'undefined' ? -1 : y;
isFlag = typeof(isFlag) === 'undefined' ? true : isFlag;
console.log(x,0) // 0 0 true
point(0,false) // 0 0 false
point(1) // 1 -1 true
point() // 0 -1 true
ES6
//有了 ES6,现在可以用更少的代码实现更好的效果了。
ES6
function point(x = 0,y = -1,isFlag = true){
console.log(x,false) // 0 0 false
point(1) // 1 -1 true
var link = function(height = 50,color = 'red',url = 'http://azat.co') {
...
}
- 模板对象
var name = `Your name is ${first} ${last}. `;
var url = `http://localhost:3000/api/messages/${id}`;
- 解构赋值
var { house,mouse} = $('body').data(); // we'll get house and mouse variables
var {jsonMiddleware} = require('body-parser');
var {username,password} = req.body;
- 箭头函数
var logUpperCase = function() {
this.string = this.string.toUpperCase();
return () => console.log(this.string);
}
logUpperCase.call({ string: 'ES6 rocks' })();
原因:箭头函数没有独立执行上下文( this ),所以其内部引用 this 对象会直接访问父级。
- promise
var wait1000 = ()=> new Promise((resolve,reject)=> {setTimeout(resolve,1000)});
wait1000()
.then(function() {
console.log('Yay!')
return wait1000()
})
.then(function() {
console.log('Wheeyee!')
});
console.log('bibiniao')
//bibiniao
//Yay!
//Wheeyee!
- 块作用域和构造let和const
function calculateTotalAmount (vip) {
var amount = 0; // probably should also be let,but you can mix var and let
if (vip) {
let amount = 1; // first amount is still 0
}
{ // more crazy blocks!
let amount = 100; // first amount is still 0
{
let amount = 1000; // first amount is still 0
}
}
return amount;
}
console.log(calculateTotalAmount(true)); // 0
- const let
//谈到const,就更加容易了;它就是一个不变量,也是块级作用域就像let一样。下面是一个演示,这里有一堆常量,它们互不影响,因为它们属于不同的块级作用域:
function calculateTotalAmount (vip) {
const amount = 0;
if (vip) {
const amount = 1;
}
{ // more crazy blocks!
const amount = 100 ;
{
const amount = 1000;
}
}
return amount;
}
console.log(calculateTotalAmount(true));
//从我个人看来,let 和const使这个语言变复杂了。没有它们的话,我们只需考虑一种方式,现在有许多种场景需要考虑。
就 let 而言,他的使用场景应该是相对较少的,我们只会在 loop(for,while 循环)及少量必须重定义的变量上用到他。
猜想:就执行效率而言,const 由于不可以重新赋值的特性,所以可以做更多语法静态分析方面的优化,从而有更高的执行效率。
所以上面代码中,所有使用 let 的部分,其实都应该是用 const 的。
- 解构
const bookSet = ['UED','TB fed','Not find'];
const bookCollection = () => {
return {book1: 'UED',book2: 'TB fed'};
};
// 1. 解构也可以设置默认值
const {book1,book3 = 'Not find'} = bookCollection();
// 2. 解构数组时候是可以跳过其中某几项的
const [book1,book3] = bookSet; // book1 = 'UED',book3 = 'Not find'
// 3. 解构可以取到指定对象的任何属性,包括它包含的方法
const {length: setLength} = bookSet; // setLength = 3
- 增强的对象字面量
const _bookNum = 4;
const basicConfig = {
level: 5
}
const config = {
// 直接指定原型对象
proto: basicConfig,// 属性简写
_bookNum,// 方法简写
getBookNum() {
return this.bookNum;
}
}
- Classes (类)
class baseModel {
constructor(options,data) { // class constructor,node.js 5.6暂时不支持options = {},data = []这样传参
this.name = 'Base';
this.url = 'http://azat.co/api';
this.data = data;
this.options = options;
}
getName() { // class method
console.log(`Class name: ${this.name}`);
}
}
- Modules (模块)
它们是用于描述JavaScript库与类之间交互的接口标准.
AMD依赖前置,js可以方便知道依赖模块是谁,立即加载;
而CMD就近依赖,需要使用把模块变为字符串解析一遍才知道依赖了那些模块,这也是很多人诟病CMD的一点,牺牲性能来带来开发的便利性,实际上解析模块用的时间短到可以忽略。
commonJS实例: node模块加载方式
这对服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于"假死"状态。
首先,加载的时候,浏览器会停止网页渲染,加载文件越多,网页失去响应的时间就会越长;其次,由于js文件之间存在依赖关系,因此必须严格保证加载顺序(比如上例的1.js要在2.js的前面),依赖性最大的模块一定要放到最后加载,当依赖关系很复杂的时候,代码的编写和维护都会变得困难。
普通方式,文件请求会同步发送,但浏览器会等待文件回来,然后一个个执行,此时浏览器假死。
- require.js的诞生,就是为了解决这两个问题:
(1)实现js文件的异步加载,避免网页失去响应;
(2)管理模块之间的依赖性,便于代码的编写和维护。
- 加载非规范的模块
理论上,require.js加载的模块,必须是按照AMD规范、用define()函数定义的模块。但是实际上,虽然已经有一部分流行的函数库(比如jQuery)符合AMD规范,更多的库并不符合。那么,require.js是否能够加载非规范的模块呢?
回答是可以的。
这样的模块在用require()加载之前,要先用require.config()方法,定义它们的一些特征。
举例来说,underscore和backbone这两个库,都没有采用AMD规范编写。如果要加载它们的话,必须先定义它们的特征。
require.config({
shim: {
'underscore':{
exports: '_'
}, 'backbone': {
deps: ['underscore','jquery'], exports: 'Backbone'
}
}
});
\7. 下列符合规范的AMD模块ID有?(A)
A ./foo/boo/woo B foo/boo/Woo C foo/boo/woo.js D ../../foo/Boo/WoO
参考:AMD规范
symbol
应用:
var obj = {
a: 1
};
var safeKey = Symbol("know");
console.log(safeKey);//Symbol(know)
obj[safeKey] = 'value';
console.log(obj[safeKey]); // value
obj["know"]
undefined
obj
{ a: 1 }
obj[safeKey]
'value'
var bKey = Symbol('know')
undefined
bKey === safeKey
false
bKey == safeKey
false
...rest替代arguments
function foo(a,b,...rest) {
console.log('a = ' + a);
console.log('b = ' + b);
console.log(rest);
}
foo(1,2,3,4,5);
// 结果:
// a = 1
// b = 2
// Array [ 3,5 ]