ECMAScript和JavaScript的关系是,前者是后者的规格,后者是前者的一种实现
初学者一开始学习JavaScript,其实就是在学3.0版的语法。
Node.js是JavaScript语言的服务器运行环境,对ES6的支持度比浏览器更高。通过Node,可以体验更多ES6的特性。建议使用版
本管理工具nvm,来安装Node,因为可以*切换版本。不过, nvm 不支持Windows系统,如果你使用Windows系统,下面的
操作可以改用nvmw或nvm-windows代替。
Babel是一个广泛使用的ES6转码器,可以将ES6代码转为ES5代码,从而在现有环境执行
ES6新增了 let 命令,用来声明变量。它的用法类似于 var ,但是所声明的变量,只在 let 命令所在的代码块内有效
let 不像 var 那样会发生“变量提升”现象。所以,变量一定要在声明后使用,否则报错。
只要块级作用域内存在 let 命令,它所声明的变量就“绑定”(binding)这个区域,ES6明确规定,如果区块中存在 let 和 const 命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是
在声明之前就使用这些变量,就会报错。
let不允许在相同作用域内,重复声明同一个变量
为什么需要块级作用域?ES5只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景
第一种场景,内层变量可能会覆盖外层变量
第二种场景,用来计数的循环变量泄露为全局变量。
let 实际上为JavaScript新增了块级作用域
ES5存在函数提升,不管会
不会进入 if 代码块,函数声明都会提升到当前作用域的顶部,得到执行
而ES6支持块级作用域,不管会不会进入if代码
块,其内部声明的函数皆不会影响到作用域的外部。
const 声明一个只读的常量。一旦声明,常量的值就不能改变。
const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。
const的作用域与let命令相同:只在声明所在的块级作用域内有效,也是不提升,存在暂时性死区,不可重复声明
对于复合类型的变量(对象),变量名不指向数据,而是指向数据所在的地址,不可变的只是这个地址,但对象本身是可变的,所以依然可以为其添加新属性
ES5只有两种声明变量的方法: var 命令和 function 命令。ES6除了添加 let 和 const 命令另外
两种声明变量的方法: import 命令和 class 命令。所以,ES6一共有6种声明变量的方法。
全局对象是最顶层的对象,在浏览器环境指的是 window 对象,在Node.js指的是 global 对象
var 命令和 function 命令声明的全局变量,依旧是全局对象的属性;另一方面规定,let 命令、 const 命令、 class 命令声明的全局变量,不属于全局对象的属性。
数组解构的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。
解构赋值的规则是,只要等号右边的值不是对象,就先将其转为对象,由于 undefined 和 null 无法转为对象,所以对它们
进行解构赋值,都会报错。
解构赋值对提取JSON对象中的数据,函数参数的默认值,尤其有用。
avaScript内部,字符以UTF-16的格式储存,每个字符固定为2个字节,对于那些需要4个字节储存的字符(Unicode码点大于
0xFFFF的字符),JavaScript会认为它们是两个字符。
JavaScript只有 indexOf 方法,可以用来确定一个字符串是否包含在另一个字符串中,ES6又提供了三种新方法
includes():返回布尔值,表示是否找到了参数字符串
startsWith():返回布尔值,表示参数字符串是否在源字符串的头部
endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部
这三个方法都支持第二个参数,表示开始搜索的位置
repeat 方法返回一个新字符串,表示将原字符串重复 n 次
padStart 和 padEnd 字符串补全长度的功能,一共接受两个参数,第一个参数用来指定字符串的最小长度,第二个参数是用来补全的
字符串。
模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符
串,或者在字符串中嵌入变量。模板字符串中嵌入变量,需要将变量名写在 ${} 之中。
${}如果大括号中的值不是字符串,将按照一般的规则转为字符串。比如,大括号中是一个对象,将默认调用对象的 toString 方
法。大括号内部,就是执行JavaScript代码,因此如果大括号内部是一个字符串,将会原样输出
ES6还为原生的String对象,提供了一个 raw 方法。String.raw 方法,往往用来充当模板字符串的处理函数,返回一个斜杠都被转义(即斜杠前面再加一个斜杠)的字符串,对
应于替换变量后的模板字符串。
Number.isFinite()用来检查一个数值是否为有限的(finite),即不是Infinity。
Number.isNaN()用来检查一个值是否为NaN。
Number.isFinite()对于非数值一律返回false, Number.isNaN()只有对于NaN才返回true,非NaN一律返回false。
ES6 将全局方法parseInt()和parseFloat(),移植到Number对象上面
Number.isInteger()用来判断一个数值是否为整数。
Math.trunc方法用于去除一个数的小数部分,返回整数部分。
Math.sign方法用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值。
它会返回五种值。
- ● 参数为正数,返回+1;
- ● 参数为负数,返回-1;
- ● 参数为 0,返回0;
- ● 参数为-0,返回-0;
- ● 其他值,返回NaN。
ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面function Point(x = 0, y = 0)
参数变量是默认声明的,所以不能用let或const再次声明。参数默认值是惰性求值的
undefined,结果触发了默认值,null,就没有触发默认值。
这是因为length属性的含义是,该函数预期传入的参数个数。某个参数指定默认值以后,预期传入的参数个数就不包括这个参数了。如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了。
一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域
ES6 引入 rest
参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
注意,rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。函数的length属性,不包括 rest 参数
ES6 允许使用“箭头”(=>)定义函数。箭头函数和普通函数。前者的this绑定定义时所在的作用域,后者的this指向运行时所在的作用域。this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。正是因为它没有this,所以也就不能用作构造函数。除了this,以下三个变量在箭头函数之中也是不存在的,指向外层函数的对应变量:arguments、super、new.target。
ES6 中只要使用尾递归,就不会发生栈溢出,相对节省内存。这将大大节省内存。这就是“尾调用优化”的意义。ES6 的尾调用优化只在严格模式下开启,正常模式是无效的。
扩展运算符(spread)是三个点(...)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。容易和rest混淆,两个不是一个东西。
function f(v, w, x, y, z) { }
const args = [0, 1];
f(-1, ...args, 2, ...[3]);
1.由于扩展运算符可以展开数组,所以不再需要apply方法,将数组转为函数的参数了。
2.数组是复合的数据类型,直接复制的话,只是复制了指向底层数据结构的指针,而不是克隆一个全新的数组。扩展运算符提供了复制数组的简便写法。
const a1 = [1, 2];
// 写法一
const a2 = [...a1];
// 写法二
const [...a2] = a1;
3.合并数组
// ES5
[1, 2].concat(more)
// ES6
[1, 2, ...more]
4.扩展运算符可以与解构赋值结合起来,用于生成数组。如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。
const [...butLast, last] = [1, 2, 3, 4, 5];
// 报错
5.扩展运算符还可以将字符串转为真正的数组。
[...'hello']
// [ "h", "e", "l", "l", "o" ]
扩展运算符内部调用的是数据结构的 Iterator 接口,因此只要具有 Iterator 接口的对象,都可以使用扩展运算符,如果对没有 Iterator 接口的对象,使用扩展运算符,将会报错。
Array.from方法用于将两类对象转为真正的数组,扩展运算符背后调用的是遍历器接口(Symbol.iterator),如果一个对象没有部署这个接口,就无法转换。Array.from方法还支持类似数组的对象。所谓类似数组的对象,本质特征只有一点,即必须有length属性。因此,任何有length属性的对象,都可以通过Array.from方法转为数组,而此时扩展运算符就无法转换。
Array.from还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组。第三个参数,用来绑定this。
Array.of方法用于将一组值,转换为数组。
Array.of(3, 11, 8) // [3,11,8]
Array.of基本上可以用来替代Array()或new Array(),并且不存在由于参数不同而导致的重载。它的行为非常统一。
数组实例的copyWithin方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组。
[1, 2, 3, 4, 5].copyWithin(0, 3)
// [4, 5, 3, 4, 5]
上面代码表示将从 3 号位直到数组结束的成员(4 和 5),复制到从 0 号位开始的位置,结果覆盖了原来的 1 和 2。
数组实例的 find() 和 findIndex()
[1, 4, -5, 10].find((n) => n < 0)
// -5
数组实例的findIndex方法的用法与find方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。
这两个方法都可以发现NaN,弥补了数组的indexOf方法的不足。
ES6 提供三个新的方法——entries(),keys()和values()——用于遍历数组。keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。
数组实例的 includes(),没有该方法之前,我们通常使用数组的indexOf方法,检查是否包含某个值。indexOf方法有两个缺点,一是不够语义化,它的含义是找到参数值的第一个出现位置,所以要去比较是否不等于-1,表达起来不够直观。二是,它内部使用严格相等运算符(===)进行判断,这会导致对NaN的误判。
ES6 明确将数组的空位转为undefined。
ES5 比较两个值是否相等,只有两个运算符:相等运算符()和严格相等运算符(=)。它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0 用来解决这个问题。Object.is就是部署这个算法的新方法。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。
Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。Object.assign拷贝的属性是有限制的,只拷贝源对象的自身属性(不拷贝继承属性),也不拷贝不可枚举的属性(enumerable: false)。
注意点(1)浅拷贝(2)同名属性的替换(3)数组的处理(4)取值函数的处理
常见用途(1)为对象添加属性(2)为对象添加方法(3)克隆对象(4)合并多个对象(5)为属性指定默认值
对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为。Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象。描述对象的enumerable属性,称为”可枚举性“,如果该属性为false,就表示某些操作会忽略当前属性。
“可枚举”(enumerable)这个概念的最初目的,就是让某些属性可以规避掉for...in操作,不然所有内部属性和方法都会被遍历到。比如,对象原型的toString方法,以及数组的length属性,就通过“可枚举性”,从而避免被for...in遍历到。
Object.getOwnPropertyDescriptor(Object.prototype, 'toString').enumerable
// false
从兼容性的角度,都不要使__proto__
用这个属性,而是使用下面的 Object.setPrototypeOf() (写操作)、 Object.getPrototypeOf() (读操
作)、 Object.create() (生成操作)代替。
Object.entries 的基本用途是遍历对象的属性,
Object.entries 方法的一个用处是,将对象转为真正的 Map 结构。var map = new Map(Object.entries(obj));
扩展运算符( ... )用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。等同于使用 Object.assign 方法。
let aClone = { ...a };
// 等同于
let aClone = Object.assign({}, a);
ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。它是JavaScript语言的第七种数据类型,前六种是:Undefined、
Null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。
Symbol值通过 Symbol
函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增
的Symbol类型。凡是属性名属于Symbol类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。
在对象的内部,使用Symbol值定义属性时,Symbol值必须放在方括号之中。
let s = Symbol();
typeof s
// "symbol"
Symbol值可以显式转为字符串。也可以转为布尔值,但是不能转为数值。
Symbol作为属性名,该属性不会出现在 for...in 、 for...of 循环中,也不会
被 Object.keys() 、 Object.getOwnPropertyNames() 返回,它也不是私有属性,有一
个 Object.getOwnPropertySymbols 方法,可以获取指定对象的所有Symbol属性名。这就造成了一种非私有的内部方法的效果。
Symbol.for() 与 Symbol() 这两种写法,都会生成新的Symbol。它们的区别是,前者会被登记在全局环境*搜索,后者
不会。 Symbol.for() 不会每次调用就返回一个新的Symbol类型的值,而是会先检查给定的key是否已经存在,如果不存在才
会新建一个值。
Proxy可以译为“代理器”,ES6原生提供Proxy构造函数,用来生成Proxy实例。
var proxy = new Proxy(target, handler);
Proxy对象的所有用法,都是上面这种形式,不同的只是 handler 参数的写法。其中, new Proxy() 表示生成一个Proxy实
例,target参数表示所要拦截的目标对象, handler 参数也是一个对象,用来定制拦截行为。
Proxy.revocable方法返回一个可取消的Proxy实例
Reflect 对象与 Proxy 对象一样,也是ES6为了操作对象而提供的新API。
将 Object 对象的一些明显属于语言内部的方法(比如 Object.defineProperty ),放到 Reflect 对象上。
ES6提供了新的数据结构Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。通过 add 方法向Set结构加入成员,结果表明Set结构不会添加重复的值,一种去除数组重复成员的方法
// 去除数组的重复成员
[...new Set(array)] 或 Array.from(new Set(array));
操作:add(value),delete(value),has(value),clear()
遍历:keys(),values(),entries(),forEach()
WeakSet结构与Set类似,也是不重复的值的集合。但是,它与Set有两个区别。
首先,WeakSet的成员只能是对象,而不能是其他类型的值。
其次,WeakSet中的对象都是弱引用,即垃圾回收机制不考虑WeakSet对该对象的引用,也就是说,如果其他对象都不再引用该
对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于WeakSet之中。这个特点意味着,无法引用
WeakSet的成员,因此WeakSet是不可遍历的。
ES6提供了Map数据结构,Object结构提供了“字符串—值”的对应,Map结构提供了“值—值”的对应,是一种更
完善的Hash结构实现,Map的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键
Map原生提供三个遍历器生成函数keys(),values(),entries(),forEach()
与其他数据结构的互相转换
- (1)Map转为数组:就是使用扩展运算符(...)。
- (2)数组转为Map:将数组转入Map构造函数,就可以转为Map。
- (3)Map转为对象:Object.create()
- (4)对象转为Map:Object.keys(obj) strMap.set(k, obj[k]);
- (5)Map转为JSON,JSON转MAP
任何数据结构只要部署,Iterator接口,就可以完成遍历操作,引入了 for...of 循环,作为遍历所有数据结构的统一的方法。一个数据结构只要部署
了 Symbol.iterator 属性,就被视为具有iterator接口,就可以用 for...of 循环遍历它的成员。也就是说, for...of 循环
内部调用的是数据结构的 Symbol.iterator 方法。
与其他遍历语法的比较:
for循环:写法比较麻烦;
数组提供内置forEach:无法中途跳出
forEach 循环,break命令或return命令都不能奏效;
for...in循环有几个缺点:
数组的键名是数字,但是for...in循环是以字符串作为键名“0”、“1”、“2”等等。
for...in循环不仅遍历数字键名,还会遍历手动添加的其他键,甚至包括原型链上的键。
某些情况下,for...in循环会以任意顺序遍历键名。
总之, for...in 循环主要是为遍历对象而设计的,不适用于遍历数组
for...of
循环相比上面几种做法:
有着同for...in一样的简洁语法,但是没有for...in那些缺点。
不同用于forEach方法,它可以与break、continue和return配合使用。
提供了遍历所有数据结构的统一操作接口。
Promise 是异步编程的一种解决方案,原生提供了Promise对象,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败),一旦状态改变,就不会再变,任何时候都可以得到这个结果,Promise也有一些缺点。首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved)
reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected)
then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法
Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。一般来说,不要在then方法里面定义 Reject 状态的回调函数(即then的第二个参数),总是使用catch方法。
const p = Promise.all([p1, p2, p3]);
Promise.all方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例。
p的状态由p1、p2、p3决定,分成两种情况。
(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
Promise.race方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.race([p1, p2, p3]);
上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
将现有对象转为 Promise 对象,Promise.resolve方法就起到这个作用
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
async 函数是什么?一句话,它就是 Generator 函数的语法糖。async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已。
async函数对 Generator 函数的改进,体现在以下四点:
(1)内置执行器。(2)更好的语义。(3)更广的适用性。(4)返回值是 Promise。
只有async函数内部的异步操作执行完,才会执行外面的then方法指定的回调函数
// 函数声明
async function foo() {}
// 函数表达式
const foo = async function () {};
// 箭头函数
const foo = async () => {};
如果希望多个请求并发执行,可以使用Promise.all方法。
async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里。
next方法必须是同步的,只要调用就必须立刻返回值。也就是说,一旦执行next方法,就必须同步地得到value和done这两个属性。如果遍历指针正好指向同步操作,当然没有问题,但对于异步操作,就不太合适了。目前的解决方法是,Generator 函数里面的异步操作,返回一个 Thunk 函数或者 Promise 对象,即value属性是一个 Thunk 函数或者 Promise 对象,等待以后返回真正的值,而done属性则还是同步产生的
for...of循环用于遍历同步的 Iterator 接口。新引入的for await...of循环,则是用于遍历异步的 Iterator 接口。
异步 Generator 函数出现以后,JavaScript 就有了四种函数形式:普通函数、async 函数、Generator 函数和异步 Generator 函数
Generator函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同,形式上,Generator函数是一个普通函数,但是有两个特征。一是, function 关键字与函数名之间有一个星号;二是,函数体
内部使用 yield 语句,定义不同的内部状态.
Generator函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用Generator函数后并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,必须调用遍历器对象的next方法,使得指针移向下一个状态。也就是说,每次调用 next 方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个 yield 语句(或 return 语句)为止。
Generator函数是分段执行的, yield 语句是暂停执行的标记,而 next 方法可以恢复执行。
next方法返回值的value属性,是Generator函数向外输出数据;next方法还可以接受参数,这是向Generator函数体内输入数据,传入
Generator 函数,作为上个阶段异步任务的返回结果
Generator函数可以返回一系列的值,因为可以有任意多个 yield,yield后面的表达式 123 + 456 ,不会立即求值,只会在 next 方法将指针移到这一句时,才会求值,Generator函数可以不用 yield 语句,这时就变成了一个单纯的暂缓执行函数。
yield 句本身没有返回值,或者说总是返回 undefined 。 next 方法可以带一个参数,该参数就会被当作上一
个 yield 语句的返回值。
for...of 循环可以自动遍历Generator函数,且此时不再需要调用 next 方法。
yield* 语句,用来在一个Generator函数里面执行另一个Generator函数。yield* 不过是 for...of 的一种简写形式,yield 命令后面如果不加星号,返回的是整个数组,加了星号就表示返回的是数组的遍历器对象。
Generator函数不能用NEW(会报错),又想在函数里面用THIS ?
首先,生成一个空对象,使用 bind 方法绑定Generator函数内部的 this 。这样,构造函数调用以
后,这个空对象就是Generator函数的实例对象了。
Generator函数的一个重要实际意义就是用来处理异步操作,改写回调函数
async函数并不属于ES6,而是被列入了ES7,但是traceur、Babel.js、regenerator等转码器已经支持这个功能,转码后立刻就
能使用。异步编程对JavaScript语言太重要。Javascript语言的执行环境是“单线程”的,如果没有异步编程,根本没法用,非卡死不可。
ES6诞生以前,异步编程的方法,大概有下面四种。
回调函数
事件监听
发布/订阅
Promise 对象
类的数据类型就是函数,类本身就指向构造函数。
class Point{
// ...
}
typeof Point // "function"
constructor 方法是类的默认方法,通过 new 命令生成对象实例时,自动调用该方法。一个类必须有 constructor 方法,
如果没有显式定义,一个空的 constructor 方法会被默认添加。
实例的属性除非显式定义在其本身(即定义在 this 对象上),否则都是定义在原型上(即定义
在 class 上)。hasOwnProperty方法判断是本身属性还是原型属性。
Class不存在变量提升(hoist),这一点与ES5完全不同。 Class之间可以通过extends关键字实现继承,这比ES5的通过修改原型链实现继承,要清晰和方便很多
子类必须在 constructor 方法中调用 super 方法,否则新建实例时会报错。这是因为子类没有自己的 this 对象,而是继
承父类的 this 对象,然后对其进行加工。如果不调用 super 方法,子类就得不到 this 对象。只有调用 super 之后,才可以使用 this 关键字
类的继承是按照下面的模式实现的:Object.setPrototypeOf(B.prototype, A.prototype);
Object.getPrototypeOf 方法可以用来从子类上获取父类。因此,可以使用这个方法判断,一个类是否继承了另一个类。
如果在一个方法前,加上 static 关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
父类的静态方法,可以被子类继承
静态属性指的是Class本身的属性,即 Class.propname ,而不是定义在实例对象( this )上的属性。
修饰器(Decorator)是一个函数,用来修改类的行为。这是ES7的一个提案,目前Babel转码器已经支持。类似和类,方法,属性添加JAVA注解的功能
在ES6之前,社区制定了一些模块加载方案,最主要的有CommonJS和AMD两种。前者用于服务器,后者用于浏览器,ES6模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS和AMD模块,都只能在运行时确定这些东西。
let { stat, exists, readFile } = require('fs') 这种加载称为“运行时加载”
import { stat, exists, readFile } from 'fs' 这种加载称为“编译时加载”
ES6的模块自动采用严格模式
模块功能主要由两个命令构成: export 和 import,export 命令用于规定模块的对外接口, import 命令用于输入其他
模块提供的功能,export 和 import 命令如果处于块级作用域内,就会报错。因为处于条件代码块之中,就没法做静态优化了,违背了ES6模块的设计初衷
模块的整体加载 import * as circle from './circle';
export default 命令,为模块指定默认输出,一个模块只能有一个默认输出,所以, import 命令后面才不用加大括号,因为只可能对应一个方法
ES6模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块
编码规范:
(1)let取代var
(2)全局常量和线程安全:在 let 和 const 之间,建议优先使用 const ,尤其是在全局环境,不应该设置变量,只应设置常量。
..静态字符串一律使用单引号或反引号,不使用双引号。动态字符串使用反引号。
..使用数组成员对变量赋值时、函数返回多个值,优先使用解构赋值。
..单行定义的对象,最后一个成员不以逗号结尾。多行定义的对象,最后一个成员以逗号结尾。
..对象尽量静态化,一旦定义,就不得随意添加新的属性。如果添加属性不可避免,要使用 Object.assign 方法
..使用扩展运算符(...)拷贝数组,使用Array.from方法,将类似数组的对象转为数组。
..立即执行函数可以写成箭头函数的形式,那些需要使用函数表达式的场合,尽量用箭头函数代替。因为这样更简洁,而且绑定了this。
..不要在函数体内使用arguments变量,使用rest运算符(...)代替