ECMAScript6
ECMAScript 6 (以下简称 ES6 )是 JavaScript 语言的下一代标准,己于 2015 月正式发布。
它的目标是使 JavaScript 语言可以用于编写复杂的大型应用程序,成为企业级开发语言
语法提案的批准流程
任何人都可以向标准委员会(又称 TC39 委员会)提案,要求修改语言标准。
一种新的语法从提案到变成正式标准,需要经历五个阶段。每个阶段的变动都要由 TC39委员会批准。
- Stage 0: Strawman (展示阶段)
- Stage 1 : Proposal (征求意见阶段〉
- Stage 2: Draft (草案阶段)
- Stage 3: Candidate (候选阶段)
- Stage 4: Finished (定案阶段)
ECMAScript当前的所有提案都可以在 TC39 的官方网站 Github.com/tc39/ecma262 中查看。
let和const命令
let和const声明的变量存在暂时性死区
1var tmp = 1232if (true) {
3 tmp = 'abc' // ReferenceError
4 let tem
5}
6
7......
8typeof x // ReferenceError
9let x
10
11......
12function bar(x = y, y = 2) {
13 return [x , y]
14}
15bar() // 报错
相关新提案
wiki.ecmascript.org/doku.php?id=strawman:do expressions,使得块级作用域可以变为表达式,即可以返回值,办法就是在块级作用域之前加上do,使它变为 do 表达式。
1let x = do {2 let t = f()
3 t * t + l
4}
https://github.com/tc39/proposal-global,在语言标准的层面引入 global 作为顶层对象。也就是说,在所有环境下,global 都是存在的,都可以拿到顶层对象。
变量的解构赋值
默认值
1let [x = 1] = [undefined]ES6 内部使用严格相等运算符===判断一个位直是否有值 所以,如果一个数组成员不严格等于 undefined ,默认位是不会生效的
2x // 1
3
4let [x = 1] = [null]
5x // null
对象的解构赋值
如果变量名与属性名不一致,必须写成下面这样。
1var { foo : baz } = { foo: 'aaa', bar: 'bbb' }2baz // 'aaa'
1let { foo: foo , bar: bar } = { foo: 'aaa', bar: 'bbb'}实际上说明,对象的解构赋值是下面形式的简写
1let { foo : baz } = { foo: 'aaa', bar: 'bbb'}也就是说,对象的解构赋值的内部机制是先找到同名属性,然后再赋值给对应的变量。真正被赋值的是后者,而不是前者。
2baz // 'aaa'
3foo // error: foo is not defined
4// 上面的代码中, foo 是匹配的模式, baz 才是变量。真正被赋值的是变量 baz ,而不是模式foo。
demo
1var node = {2 loc: {
3 start: {
4 line: 1,
5 column: 5
6 }
7 }
8}
9let { loc, loc: { start } , loc: { start: { line } } } = node
10line // 1
11loc // Object {start: Object}
12start // Object {line: 1, column: 5}
上面的代码有三次解构赋值,分别是对 loc start line 三个属性的解构赋值。需要注意的是,最后一次对 line 属性的解构赋值之中,只有 line 是变量, loc start 都是模式,不是变量。
将一个已经声明的变量用于解构赋值
1// 错误的写法2let x
3{x} = {x : 1}
4// SyntaxError: syntax error
1// 正确的写法上面代码的写法会报错 因为 JavaScript 引擎会将{}理解成一个代码块,从而发生语法错误。只有不将大括号写在行首,避免 JavaSript 将其解释为代码块,才能解决这个问题
2let x
3({x} = {x : 1})
其他类型
1const [a , b, c , d , e] = 'hello'2a // 'h'
3
4let {length : len} = 'hello'
5len // 5
6
7......
8// 如果等号右边是数值和布尔值 ,则会先转为对象。
9let {toString : s} = 123
10s === Number.prototype.toString // true
11let {toString: s} = true
12s === Boolean.prototype.toString // true
13// undefined null 无法转为对象,所以对它们进行解构赋值时都会报错。
14let { prop: x } = undefined // TypeError
15let { prop: y } = null // TypeError
字符串的扩展
方法
1codePointAt()2String.fromCodePoint()
3at()
4normalize()
5
6// 处理4个字节储存的字符
由 for … of 循环遍历字符串可以识别大于 OxFFFF 的码点
- includes:返回布尔值,表示是否找到了参数字符串
- starts With: 返回布尔值,表示参数字符串是否在源字符串的头部
- ends With:返回布尔值, 表示参数字符串是否在源字符串的尾部
- repeat 方法返回一个新字符串,表示将原字符串重复n次。
- padStart 用于头部补全
- padEnd 用于尾部补全
正则的扩展
u
ES6 对正则表达式添加了u修饰符,含义为“Unicode 模式”,用来正确处理大于\uFFFF的Unicode 字符。也就是说,可以正确处理4个字节的 UTF-16 编码。
1/^\uD83D/u . test ('\uD83D\uDC2A') // false2/^\uD83D/ .test ('\uD83D \uDC2A') // true
3/{2}/.test('') // false
4/{2}/u.test('') // true
一个正确返回字符串长度的函数。
1function codePointLength(text) {2 var result= text.match(/[\s\S]/gu)
3 return result ? result.length : 0
4}
5
6var s = ''
7s.length // 4
8codePointLength(s) // 2
y
ES6 还为正则表达式添加了y修饰符,叫作“粘连”( sticky )修饰符
y修饰符的设计本意就是让头部匹配^的标志在全局匹配中都有效。
属性
与y修饰符相匹配, ES6 的正则对象多了 sticky 属性,表示是否设置了y修饰符。
1var r = /hello\d/y2r.sticky // true
ES6 为正则表达式新增了 flags 属性,会返回正则表达式的修饰符。
1// ES5 source 属性 返回正则表达式的正文2// ES6 flags 属性 返回正则表达式的修饰符
3/abc/ig.source
4// 'abc'
5/abc/ig.flags
6// 'gi'
新提案
github.com/mathiasbynens/es-regexp-dotall-flag:引入s修饰符,使得 .可以匹配任意单个字符。
1/foo.bar/.test ('foo\nbar') // false2/foo.bar/s.test ('foo\nbar') // true
https://github.com/goyakin/es-regexp-lookbehind被提出:引入后行断言,其中 V8 引擎 4.9 版本己经支持。
“先行断言”指的是,x只有在y前面才匹配,必须写成/x(?=y)/的形式。“先行否定断言”指的是,x只有不在y前面才匹配,必须写成 /x(?!y)/的形式
“后行断言”正好与“先行断言”相反,x只有在y后面才匹配,必须写成/(?<=y)x/的形式。“后行否定断言”则与“先行否定断言”相反,x只有不在y后面才匹配,必须写成/(?<!y)x/的形式。
github.com/mathiasbynens/es-regexp-unicode-property-escapes 中引入了一种新的写法:\p{ … }和\p { … },允许正则表达式匹配符合 Unicode 某种属性的所有字符。
1const regexGreekSymbol = /\p{Script=Greek}/u2regexGreekSymbol.test('Π') // true
3// 上面的代码中,\p{Script=Greek}指定匹配一个希腊文字母,所以匹配Π成功。
“具名组匹配”(Named Capture Groups) 提案 github.com/tc39/proposal-regexpd-named-groups,其中允许为每一个组匹配指定一个名字,既便于阅读代码,又便于引用。
1const R_ DATE = /(?<year>\d{4})-(?<month>\d{2} )-(?<day>\d{2} )/2const matchObj = RE_DATE.exec ('1999-1 2-31');
3const year= matchObj.groups.year // 1999
4const month= matchObj.groups.month // 12
5const day= matchObj.groups.day // 31
如果要在正则表达式内部引用某个“具名组匹配”,可以使用\k<组名>的写法。
数值的扩展
方法
1Number.isFinite() // 检查一个数值是否为有限的2Number.isNaN() // 检查一个值是否为 NaN
3
4// parselnt,parseFloat移植到了 Number 对象上面
5Number.parselnt()
6Number.parseFloat()
7
8Nurnber islnteger() // 判断一个值是否为整数
9
10Number.EPSILON // 浮点数计算的合理误差范围
11
12// js安全整数
13Number.MAX_SAFE_INTEGER // Math.pow(2, 53) - 1
14Number.MIN_SAFE_INTEGER // -Number.MAX SAFE INTEGER
15Number.isSafeinteger() // 用来判断一个整数是否落在安全范围之内
Math扩展
- Math.trunc 方法用于去除一个数的小数部分,返回整数部分。
- Math.sign 方法用来判断一个数到底是正数、负数,还是零。
- Math.cbrt 方法用于计算一个数的立方根。
- Math.clz32 方法返回一个数的 32 位无符号整数形式有多少个前导0。
- Math.imul 方法返回两个数以 32 位带符号整数形式相乘的结果,返回的也是 32 位的带符号整数。
- Math.fround 方法返回一个数的单精度浮点数形式
- Math.hypot 方法返回所有参数的平方和的平方根
- Math.expml(x) 返回 e^x-1
- Math.loglp(x) 方法返回 ln(l+x)
- Math.loglO(x) 返回以 10 为底的 x 的对数
- Math.log2(x) 返回以 2 为底的 x 的对数
- Math.sinh(x) 返回x的双曲正弦( hyperbolic sine)
- Math.cosh(x) 返回x的双曲余弦( hyperbolic cosine)
- Math.tanh(x) 返回x的双曲正切( hyperbolic tangent)
- Math.asinh(x) 返回x的反双曲正弦( inverse hyperbolic sine)
- Math.acosh(x) 返回x的反双曲余弦( inverse hyperbolic cosine)
- Math.atanh(x) 返回x的反双曲正切( inverse hyperbolic tangent)
- Math.signbit() 方法判断一个数的符号位是否己经设置。
ES2016 新增了一个指数运算符**
12 ** 2 // 422 ** 3 // 8
3
4a **= 2
5// 等同于 a = a * a
注意: 在 V8 引擎中,指数运算符与 Math.pow 的实现不相同,对于特别大的运算结果,两者会有细微的差异
新提案
提案github.com/tc39/proposal-bigint 其中引入了新的数据类型 Integer (整数)来表示整数,没有位数的限制,任何位数的整数都可以精确表示。用以提升js Math.MAX_SAFE_INTEGER 的上限
1typeof 123n2// 'bigint'
函数的扩展
尾调用
尾调用(TailCall)是函数式编程一个重要概念,是指某个函数的最后一步是调用另一个函数
函数调用自身称为递归。如果尾调用自身就称为尾递归。
作“尾调用优化”,即只保留内层函数的调用帧,如果所有函数都是尾调用,那么完全可以做到每次执行时调用帧只有一项,这将大大节省内存。这就是“尾调用优化”的意义。
提案
github.com/zenparsing/es-function-bind 函数绑定运算符是井排的双冒号::,双冒号左边是一个对象,右边是一个函数,运算符会自动将左边的对象作为上下文环境(即 this 对象〉绑定到右边的函数上。
1foo::bar2// 等同于
3bar.bind(foo)
4
5foo::bar (...arguments)
6// 等同于
7bar.apply(foo, arguments)
ES2017 中有一个提案github.com/jeffmo/es-trailing-function-commas,允许函数的最后一个参数有尾逗号.
数组的扩展
扩展运算符
1'x'.length // 5扩展运算符能够正确识别32位的 Unicod 字符。
2[...'x'].length // 3
3
4'x'.split('').reverse().join('')
5// '\uDFB7\uD842x'
6[...'x'].reverse().join('')
7// 'x'
任何 Iterator 接口的对象(参见第 15 章)都可以用扩展运算符转为真正的数组。
1let nodeList = document.querySelectorAll('div')2let array = [...nodeList]
Array.from()
Array.from 方法用于将两类对象转为真正的数组,类似数组的对象(array-like object)和可遍历(iterable)对象,包括ES6新增的数据结 Set和Map
Array.of()
Array.of 方法用于将一组值转换为数组。这个方法的主要目的是弥补数组构造函数 Array() 的不足 因为参数个数的不同会导致Array()的行为有差异。
copyWithin()
1[1, 2 , 3 , 4, 5].copyWithin(0, 3)数组实例的 copyWithin 方法会在当前数组内部将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法会修改当前数组。
2// [4, 5 , 3 , 4, 5]
3
4[ 1, 2 , 3 , 4, 5].copyWithin(0, 3, 4)
5// [4 , 2 , 3 , 4, 5]
find()和 findIndex()
数组实例的find 方法用于找出第一个符合条件的数组成员。如果没有符合条件的成员,则返回 undefined;数组实例的 findindex 方法的用法与 find 方法非常类似 返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1
fill()
fill 方法使用给定值填充一个数组。
entries() keys() values()
keys()是对键名的遍历 values() 是对键值的遍历 entrie()是对键值对的遍历。
includes()
includes 方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的 includes 方法类似。
另外, Map和Set 数据结构有一个 has 方法,需要注意与 includes 区分。
- Map 结构的 has 方法是用来查找键名的,比如 Map.prototype.has(key)、WeakMap prototype.has(key)、 Reflect has(target propertyKey)。
- Set 结构的 has 方法是用来查找值的,比如 Set.prototype.has(value)、WeakSet.prototype.has(value)。
对象的扩展
ES6 允许字面量定义对象时,把表达式放在方括号内。
1let propKey = 'foo'2let obj = {
3 [propKey]: true,
4 ['a' + 'b']: 123,
5 [propKey + 'c']() {
6 return '0'
7 }
8}
Object.is()
同值相等对比
1+0 === -0 // true2NaN === NaN // false
3
4Object.is(+0, -0) // false
5Object.is(NaN, NaN) // true
Object.assign()
Object.assign 方法用于将源对象的所有可枚举属性复制到目标对象。Object.assign方法实行的是浅复制
`proto`、 `Object.setPrototypeOf()`、`Object.getPrototypeOf()`
操作对象原型方法
`Object.keys()`、 `Object.values()`、 `Object.entries()`
ES2017 中有 个提案github.com/tc39/proposal-object-values-entries,其中引入了与Object.keys 配套的 Object.values Object.entries 作为遍历一个对象的补充手段,供for ... of 循环使用。
其他方法
-
Object.getOwnPropertyDescriptors 方法,返回指定对象所有自身
属性(非继承属性)的描述对象。 -
提案github.com/tc39/proposal-object-valuesentries,其中引入了“Null 传导运算符” ?.,可以简化属性读取判断。
obj?.prop:读取对象属性
obj?.[expr]:同上
func?.(…args):函数或对象方法的调用
new C?.(… args):构造函数的调用
Symbol
Symbol 值作为对象属性名时不能使用点运算符, Symbol 值必须放在方括号中.
Symbol.for()、 Symbol.keyFor()
有时,我们希望重新使用同一个 Symbol 值, Symbol.for 接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个Symbol 值,否则就新建井返回一个以该字符串为名称的Symbol 值。Symbol.keyFor 方法返回一个己登记的 Symbol 类型值的 key
1var sl = Symbol.for('foo')2var s2 = Symbol.for('foo')
3sl === s2 // true
4......
5var sl = Symbol.for('foo')
6Symbol.keyFor(sl) // 'foo'
7var s2 = Symbol('foo')
8Symbol.keyFor(s2) // undefined
Symbol.for 为 Symbol 登记的名字是全局环境的,可以在不同的 iframe service worker 中取到同一个值
内置的Symbol值
- Symbol.hasinstance 属性指向一个内部方法,对象使用 instanceof 运算符时会调用这个方法,判断该对象是否为某个构造函数的实例。
- Symbol.isConcatSpreadable 属性等于一个布尔值,表示该对象使用
Array.prototype.concat()时是否可以展开。 - Symbol.species 属性指向当前对象的构造函数。创造实例时默认会调用这个方法,即使用这个属性返回的函数当作构造函数来创造新的实例对象
- Symbol.match 属性指向一个函数,当执行 str.match(myObject)时 ,如果该属性存在,会调用它返回该方法的返回值。
- Symbol.replace 属性指向一个方法,当对象被 String.prototype.replace方法调用时会返回该方法的返回值。
- Symbol.search 属性指向一个方法,当对象被 String.prototype.search方法调用时会返回该方法的返回值。
- Symbol.split 属性指向一个方法,当对象被 String.prototype.split方法调用时会返回该方法的返回值。
- Symbol.iterator 属性指向该对象的默认遍历器方法。
- Symbol.toPrimitive 属性指向 个方法,对象被转为原始类型的值时会调用这个方法,返回该对象对应的原始类型值。
- Symbol.toStringTag 属性指向一个方法,在对象上调用 Object.prototype.toString方法时,如果这个属性存在,其返回值会出现在 toString 方法返回的字符串中,表示对象的类型。也就是说,这个属性可用于定制[object Object]或[object Array]中 object 后面的字符串。
- Symbol.unscopables 属性指向一个对象,指定了使用 with 关键字时哪些属性会被 with 环境排除。
Set 和 Map
Set对象是值的集合 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。
Map 对象保存键值对。任何值都可以作为一个键或一个值WeakSet 结构与 Set 类似,也是不重复的值的集合,WeakSet 的成员只能是对象,而不能是其他类型的值
WeakSet 中的对象都是弱引用,不影响js垃圾回收机制。
WeakMap 对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。
原生的 WeakMap 持有的是每个键对象的“弱引用”,不影响js垃圾回收机制。
Proxy和Reflect
- Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与proxy handlers的方法相同。
Reflect对象有4个意义:
- 从Reflect对象上可以拿到语言内部的方法。
- 操作对象出现报错时返回false
- 让操作对象都变为函数式编程
- 保持和proxy对象的方法一一对象
- Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
vue3 的核心代码
Iterator 和 for … of
1let arr = ['a', 'b', 'c']遍历器Iterator是一种机制。它是一种接口,为各种不同的数据结构提供统一访问机制。任何数据结构,只要部署 Iterator 接口,就可以完成遍历操作。
Iterator 接口主要供 for … of 消费。
2let iter = arr[Symbol.iterator]()
3iter.next() // { value: 'a', done: false }
4iter.next() // { value: 'b', done : false }
5iter.next() // { value: 'c', done: false }
6iter.next() // { value: undefined, done: true }
Generator
Generator 函数是 ES6 提供的一种异步编程解决方案。执行 Generator 函数会返回一个遍历器对象。返回的遍历器对象可以依次遍历 Generator 函数内部的每一个状态。
1function* helloWorldGenerator() {2 yield 'hello'
3 yield 'world'
4 return 'ending'
5}
6var hw = helloWorldGenerator()
7
8hw.next()
9// { value: 'hello', done: false }
10hw.next()
11// { value: 'world', done: false }
12hw.next()
13// { value: 'ending', done: true }
14hw.next()
15// { value: undefined, done: true }
16
17......
18for(let i of hw) {
19 console.log(i)
20}
21// hello
22// world
yield 语句与 return 语句既有相似之处,又有区别。相似之处在于都能返回紧跟在语句后的表达式的值 区别在于每次遇到 yield 函数暂停执行,下一次会从该位置继续向后执行,return 语句不具备位置记忆的功能。
yield 语句本身没有返回值,或者说总是返回 undefined, next 方法可以带有一个参数,该参数会被当作上 yield 语句的返回值
注意:yield 表达式如果用在另一个表达式之中,必须放在圆括号里面。用作函数参数或放在赋值表达式的右边,可以不加括号。
对象原生不具备 Iterator 口,无法用 for ... of 遍历。这时,我们
通过 Generator 函数为它加上遍历器接口,这样就可以用 for ... of 遍历了。另一种写法是,将 Generator 函数加到对象的 Symbol.iterator 属性上。
2 let propKeys = Object.keys(this)
3 for (let propKey of propKeys) {
4 yield [propKey , this[propKey]]
5 }
6}
7let jane = { first: 'Jane', last: 'Doe'}
8jane[Symbol.iterator] = objectEntries
9
10for (let [key , value] of jane) {
11 console.log(`${key}: ${value}`)
12}
13
14// first: Jane
15// last: Doe
Generator.prototype.throw()
Generator 函数返回的遍历器对象都有 throw 方法,可以在函数体外抛出错误,然后 Generator 函数体内捕获
Generator.prototype.return()
Generator 函数返回的遍历器对象还有一个 return 方法,可以返回给定的值,并终结 Generator 函数的遍历。
yield*
yield* 语句,用来在一个 Generator 函数里面执行另 Generator 函数。
1function* foo () {2 yield 'a'
3 yield 'b'
4}
5
6function* bar() {
7 yield 'x'
8 yield* foo()
9 yield 'y'
10}
11
12// 等同于
13function* bar() {
14 yield 'x'
15 yield 'a'
16 yield 'b'
17 yield 'y'
18}
作为对象属性的 Generator 函数
如果一个对象的属性是 Generator 函数,那么可以简写成下面的形式。
1let obj = {2 * myGeneratorMethod() {
3 // codes
4 }
5}
async await
async 函数使得异步操作变得更加方便。 async 函数就是 Generator 函数的语法糖
demo
1var fs = required('fs')2var readFile = function(fileName) {
3 return new Promise(function(resolve, reject) {
4 fs.readFile(fileName, function(error, data) {
5 if (error) return reject(error)
6 resolve(data)
7 })
8})
9// Generator
10var gen = function* () {
11 var fl = yield readFile ('/etc/fstab')
12 var f2 = yield readFile ('/etc/shells')
13 console.log(fl.toString())
14 console.log(f2.toString())
15}
16// async
17var asyncReadFile = async function() (
18 var fl = await readFile ('/etc/fstab')
19 var f2 = await readFile ('/etc/shells')
20 console.log(fl.toString())
21 console.log(f2.toString())
22}
23
通过比较就会发现,async 函数就是将 Generator 函数的星号*替换成 async ,将 yield替换成 await ,仅此而己。
async 对比 Generator
- 内置执行器
- 更好的语义
- 更广的使用性
- 返回值是Promise
Promise Generator async 异步处理对比
- Promise 的写法相比回调函数的写法大大改进,但是一眼看上去,代码完全是 Promise的 API (then catch 等),操作本身的语义反而不容易看出来
- Generator 语义比 Promise 写法更清晰。这个写法的问题在于,必须有一个任务运行器自动执行 Generator 函数,而且必须保证 yield 语句后面的表达式返回 Promise
- async 函数的实现最简洁,最符合语义,几乎没有与语义不相关的代码。
Class
ES6 引入了 Class (类)这个概念作为对象的模板。通过 class 关键字可以定义类。
类的构造方法 constructor 默认指向本身,也可指定返回另一个对象
私有方法与私有属性
私有方法是常见需求,但 ES6 不提供,只能通过变通方法来模拟实现。
与私有方法一样, ES6 不支持私有属性。
提案 github.com/tc39/proposal-classfields#private-fields 为 Class 加了私有属性。方法是在属性名之前,使用#来表示。
1class Point {2 #x
3 #square() { return this.#x ** 2 }
4
5 constructor(x = 0) {
6 this.#x = +x
7 }
8
9 get x() { return this.#square() }
10 set x(value) { this.#x = +value }
11}
静态方法
1class Foo {如果在一个方法前加上 static 关键字,就表示该方法不会被实例继承,而是直接通过类调用,称为“静态方法”。
2 static classMethod() {
3 return 'hello'
4 }
5}
6
7Foo.classMethod() // 'hello'
8
9var foo = new Foo()
10foo.classMethod()
11// TypeError: foo.classMethod is not a function
12
13// 父类的静态方法可以被子类继承。
14class Bar extends Foo {}
15Bar.classMethod() // 'hello'
静态方法也可以从 super 对象上调用。Class 内部调用 new.target ,返回当前 Class
继承
Class 可以通过 extends 关键字实现继承,子类通过 super 调用父类的属性和方法
1class Point {}2
3class ColorPoint extends Point {
4 constructor(x, y, color) {
5 super(x, y)
6 this.color = color
7 }
8
9 toString() {
10 return this.color + '' + super.toString()
11 }
子类必须在 constructor 方法中调用 super 方法 否则新建实例时会报错。这是因为子类没有自己的 this 对象,而是继承父类的 this 对象,然后对其进行加工。如果不调用 super 方法,子类就得不到 this 对象。如果子类没有定义 constructor 方法,那么这个方法会被默认添加。
Object.getPrototypeOf 方法可以用来从子类上获取父类。
Decorator 类的修饰器
1@testable修饰器Decorator是一个函数,用来修改类的行为。修饰器不仅可以修饰类,还可以修饰类的属性。
2class MyTestableClass {
3 @readonly
4 name() { return `${this.first}${this.last}`}
5}
6
7function testable(target) {
8 target.isTestable = true
9}
10function readonly(target, name, descriptor) {
11 // descriptor 对象原来的值如下
12 // {
13 // value: specifiedFunction,
14 // enumerable : false ,
15 // configurable: true ,
16 // writable : true
17 // }
18 descriptor.writable = false
19 return
20}
21MyTestableClass.isTestable // true
由于存在函数提升,修饰器不能用于函数。
module
`export`与 `import`的复合写法
1export { foo, bar } from 'my_module'2// 等同于
3import { foo, bar J from 'my_module'
4export { foo, bar }
ES6 模块输出的是值的引用,是编译时输出接口。
ES6编程风格
-
let 完全可以取代 var ,因为两者语义相同,而且 let 没有副作用。
-
let const 之间,建议优先使用 const
const 可以提醒阅读程序的人,这个变量不应该改变
const 比较符合函数式编程思想,运算不改变值,只是新建值,而且这样也有利于将来的分布式运算
JavaScript 编译器会对 const 进行优化,所以多使用 const 有利于提供程序的运行效率 -
静态字符串一律使用单引号或反引号,不使用双引号。动态字符串使用反引号。
-
使用数组成员对变量赋值时,或者函数的参数如果是对象的成员,优先使用解构赋值。
-
单行定义的对象,最后一个成员不以逗号结尾。多行定义的对象,最后一个成员以逗号结尾
-
使用扩展运算符…复制数组,使用 Array.from 方法将类似数组的对象转为数组。
-
,只有模拟实体对象时才使用 Object 。如果只是需要 key: value 的数据结构 ,则使用 Map 。因为 Map 有内建的遍历机制
-
总是用 Class 取代需要 prototype 的操作。因为 class 写法更简洁,更易于理解。
ECMA 官方标准
规格文件 https://262.ecma-international.org/6.0/
上述地址为ECMA国际标准组织的官方网站,其中对ES6的语法以及函数的实现都做了详尽而清晰的描述