ECMAScript6

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 = 123
2if (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 都是存在的,都可以拿到顶层对象。

变量的解构赋值

默认值

ES6 内部使用严格相等运算符===判断一个位直是否有值 所以,如果一个数组成员不严格等于 undefined ,默认位是不会生效的

1let [x = 1] = [undefined]
2// 1
3
4let [x = 1] = [null]
5// 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      line1,
5      column5
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

上面代码的写法会报错 因为 JavaScript 引擎会将{}理解成一个代码块,从而发生语法错误。只有不将大括号写在行首,避免 JavaSript 将其解释为代码块,才能解决这个问题

1// 正确的写法
2let x
3({x} = {x : 1})

其他类型

1const [a , b, c , d , e] = 'hello'
2// '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'// false
2/^\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/y
2r.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'// false
2/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}/u
2regexGreekSymbol.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 // 4
22 ** 3 // 8
3
4a **= 2
5// 等同于 a = a * a

注意: 在 V8 引擎中,指数运算符与 Math.pow 的实现不相同,对于特别大的运算结果,两者会有细微的差异

新提案

提案github.com/tc39/proposal-bigint 其中引入了新的数据类型 Integer (整数)来表示整数,没有位数的限制,任何位数的整数都可以精确表示。用以提升js Math.MAX_SAFE_INTEGER 的上限

1typeof 123n
2// 'bigint'

函数的扩展

尾调用

尾调用(TailCall)是函数式编程一个重要概念,是指某个函数的最后一步是调用另一个函数
函数调用自身称为递归。如果尾调用自身就称为尾递归。

作“尾调用优化”,即只保留内层函数的调用帧,如果所有函数都是尾调用,那么完全可以做到每次执行时调用帧只有一项,这将大大节省内存。这就是“尾调用优化”的意义。

提案

github.com/zenparsing/es-function-bind 函数绑定运算符是井排的双冒号::,双冒号左边是一个对象,右边是一个函数,运算符会自动将左边的对象作为上下文环境(即 this 对象〉绑定到右边的函数上。

1foo::bar
2// 等同于
3bar.bind(foo)
4
5foo::bar (...arguments)
6// 等同于
7bar.apply(foo, arguments)

ES2017 中有一个提案github.com/jeffmo/es-trailing-function-commas,允许函数的最后一个参数有尾逗号.

数组的扩展

扩展运算符

扩展运算符能够正确识别32位的 Unicod 字符。

1'x𠮷𠮷'.length // 5
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()

数组实例的 copyWithin 方法会在当前数组内部将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法会修改当前数组。

1[12 , 3 , 45].copyWithin(03)
2// [4, 5 , 3 , 4, 5]
3
412 , 3 , 45].copyWithin(034)
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 // true
2NaN === NaN // false
3
4Object.is(+0-0// false
5Object.is(NaNNaN// 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

  1. Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与proxy handlers的方法相同。 Reflect对象有4个意义:
    • 从Reflect对象上可以拿到语言内部的方法。
    • 操作对象出现报错时返回false
    • 让操作对象都变为函数式编程
    • 保持和proxy对象的方法一一对象
  2. Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。

vue3 的核心代码

Iterator 和 for … of

遍历器Iterator是一种机制。它是一种接口,为各种不同的数据结构提供统一访问机制。任何数据结构,只要部署 Iterator 接口,就可以完成遍历操作。
Iterator 接口主要供 for … of 消费。

1let arr = ['a''b''c']
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 函数内部的每一个状态。

1functionhelloWorldGenerator() {
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 属性上。

1functionobjectEntries () {
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 函数。

1functionfoo () {
2  yield 'a'
3  yield 'b'
4}
5
6functionbar() {
7  yield 'x'
8  yield* foo()
9  yield 'y'
10}
11
12// 等同于
13functionbar() {
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 异步处理对比

  1. Promise 的写法相比回调函数的写法大大改进,但是一眼看上去,代码完全是 Promise的 API (then catch 等),操作本身的语义反而不容易看出来
  2. 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}

静态方法

如果在一个方法前加上 static 关键字,就表示该方法不会被实例继承,而是直接通过类调用,称为“静态方法”。

1class Foo {
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 类的修饰器

修饰器Decorator是一个函数,用来修改类的行为。修饰器不仅可以修饰类,还可以修饰类的属性。

1@testable
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的语法以及函数的实现都做了详尽而清晰的描述


posted @ 2022-02-02 20:00  前端订阅  阅读(57)  评论(0编辑  收藏  举报