- ECMAScript 与 Javascript
- ECMAScript的发展过程
- ECMAScript2015的新特性
- And more...
- ECMAScript (ES) 通常看作Javascript的标准化规范 实际上JavaScript是ECMAScript的扩展语言
- ECMAScript只提供了最基本的语法
- JavaScript @ Web (在浏览器环境中): JavaScript = ECMAScript + Web APIs(DOM + BOM)
- JavaScript @ Nodejs (在浏览器环境中): JavaScript = ECMAScript + Node APIs(fs + net + etc.)
- Javascript语言本身是ECMAScript
#### ES6
- 解决原油语法上的一些问题或者不足
- 对原有语法进行增强
- 全新的对象、全新的方法、全新的功能
- 全新的数据类型和数据结构
#### let与块级作用域
- 作用域 - 某个成员能够起作用的范围
- 全局作用域 函数作用域 块级作用域
- 块 : 代码中花括号包裹起来的范围
- let没有声明提升
```javascript
// 块
// if(true){
// // var foo = 'foo'
// let foo = 'foo'
// console.log(foo)
// }
// // console.log(foo)
// ----------------------------------------------------
// for循环中的计数器
// for(var i = 0; i < 3; i++){
// for(var i = 0; i < 3; i++){
// console.log(i)
// }
// console.log('i out', i)
// }
// for(let i = 0; i < 3; i++){
// for(let i = 0; i < 3; i++){
// console.log(i)
// }
// console.log('i out', i)
// }
// --------------------------------
// var elements = [{}, {}, {}]
// for(var i = 0; i< elements.length; i++){
// elements[i].onclick = (function (i) {
// return function () {
// console.log(i)
// }
// })(i)
// }
// for(let i = 0; i< elements.length; i++){
// elements[i].onclick = function () {
// console.log(i)
// }
// }
// elements[0].onclick()
// --------------------------------------------
// for(let i = 0; i< 3;i++){
// let i = 'foo'
// console.log(i)
// }
// let i = 0
// if(i < 3){
// let i = 'foo'
// console.log(i)
// }
// i++
// if(i < 3){
// let i = 'foo'
// console.log(i)
// }
// i++
// if(i < 3){
// let i = 'foo'
// console.log(i)
// }
// i++
// if(i < 3){
// let i = 'foo'
// console.log(i)
// }
// i++
// ------------------------------------
// let 没有声明提升
// console.log(foo)
// var foo = 'foo'
// console.log(foo)
// let foo = 'foo'
```
#### const
- let基础上多了**只读**特性, 声明过后不允许在被修改且变量声明必须直接赋值
- 最佳实践: 不用var, 主用const, 配合let
```javascript
// const变量声明 不能修改
// const name = 'const'
// name = 'liuchao'
// const变量声明必须直接赋值
// const name
// name = 'const'
// ------------------
// const obj = {}
// obj.name = 'const'
// obj = {}
```
#### 数组的解构
```javascript
const arr = [100, 200, 300]
// const foo = arr[0]
// const bar = arr[1]
// const baz = arr[2]
// const [foo, bar, baz] = arr
// console.log(foo, bar, baz)
// const [, , baz] = arr
// console.log( baz)
// const [foo, ...rest] = arr
// console.log(rest)
// const [foo, bar, baz, more] = arr
// console.log(more)
// const [foo, bar, baz, more = 'more'] = arr
// console.log(more)
```
#### 对象的解构
```javascript
const obj = {name: 'const', age: 10}
// const {name, age} = obj
// console.log(name, age)
// const name = 'tom'
// const {name: objName = 'objName'} = obj
// console.log(objName, name)
const {log} = console
log('xxx')
log('aaa')
log('adkdk')
```
#### 模版字符串字面量
```javascript
const str = 'hello es2015, this is a string'
const str = `hello es2015,
this is a \`string\``
// ``内支持转义字符 支持换行 多行字符串
console.log(str)
// 支持变量嵌入 简单运算 js内置函数
const name = 'liuchao'
const msg = `hey, ${name}--------${1 + 3}---------${Math.random()}`
console.log(msg)
```
#### 模版字符串标签函数
```javascript
// const str = console.log`hello world`
const name = 'luchao'
const gender = true
function myTagFunc(str, name, gender){
// console.log(str, name, gender)
// return '123'
const sex = gender ? 'man' : 'women'
return str[0] + name + str[1] + sex + str[2]
}
const result = myTagFunc`hey, ${name} is a ${gender}.`
console.log(result)
```
#### 字符串的扩展方法
- includes
- startWith
- endWith
```javascript
const msg = 'Error: foo is not defined.'
console.log(
msg.startsWith('Error'),
msg.endsWith('.'),
msg.includes('foo')
)
```
#### 参数默认值
```javascript
// function foo(enable){
// // enable = enable || true
// enable = enable === undefined ? true : enable
// console.log('foo invoked - enable: ')
// console.log(enable)
// }
// 带默认值的参数在最后
function foo(bar,enable = true){
console.log('foo invoked - enable: ')
console.log(enable)
}
foo(false)
```
#### 剩余参数
```javascript
// function foo(){
// console.log(arguments)
// }
// ...操作符只能用在最后一个参数 并且只能使用一次
function foo(...args){
console.log(args)
}
foo(1, 2, 4, 45)
```
#### 展开数组参数
```javascript
const arr = ['foo', 'bar', 'baz']
// console.log(
// arr[0],
// arr[1],
// arr[2]
// )
// console.log.apply(console, arr)
console.log(...arr)
```
#### 箭头函数
- 箭头函数不会改变this的指向
```javascript
// function inc(num){
// return num + 1
// }
// const inc = n => n+1
// console.log(inc(100))
const arr = [1, 2, 3, 4, 5, 6, 7]
// arr.filter(function(item){
// return item % 2
// })
const result = arr.filter(item => item % 2)
console.log(result)
// 箭头函数与 this
const person = {
name : 'tom',
// sayHi: function(){
// console.log(`hi, my name is ${this.name}`)
// }
sayHi: () => {
// 箭头函数没有this机制 不会改变this指向
console.log(`hi, my name is ${this.name}`)
},
sayHiAsync: function () {
// const _this = this
// setTimeout(function(){
// console.log(_this.name)
// }, 1000)
setTimeout(()=>{
console.log(this.name)
}, 1000)
}
}
person.sayHi()
person.sayHiAsync()
```
#### 对象字面量增强
- 计算属性名
```javascript
const bar = '2233'
const obj = {
name: '111',
// bar: bar,
bar,
// method1: function(){
// console.log('mothed111')
// },
method1(){
console.log('mothed111')
console.log(this)
},
// Math.random(): 111
// 计算属性名
[Math.random()]: 1113
}
obj[Math.random()] = 1112
console.log(obj)
obj.method1()
```
#### 对象扩展方法
- Object.assign 将多个源对象中的属性复制到一个目标对象中(如果有相同, 源对象中的属性就会覆盖目标对象中的属性)
```javascript
// const source = {
// a: 111,
// b: 233
// }
// const source2 = {
// d: 111,
// f: 233
// }
// const target = {
// a: 444,
// c: 666
// }
// const result = Object.assign(target, source, source2)
// console.log(result)
// console.log(result === target)
function func(obj){
// obj.name = 'func obj'
// console.log(obj)
const funcObj = Object.assign({}, obj)
funcObj.name = 'func obj'
console.log(funcObj, 'funcObj')
return funcObj
}
const obj = {name: 'global obj'}
func(obj)
console.log(obj)
```
- is
```javascript
console.log(
0 == false,
0 === false,
+0 === -0,
NaN === NaN,
Object.is(+0, -0),
Object.is(NaN, NaN),
)
```
#### Proxy 代理对象(Object.defineProperty)以及Proxy的优势
- defineProperty只能监视属性的读写 Proxy能够监视更多对象操作(delete操作、对对象方法的调用等)
- Proxy更好的支持数组对象的监视 (以往监视数组操作方法是重写数组的操作方法)
- Proxy是以非侵入的方式监管了对象的读写(一个定义好的对象,不需要对对象本身进行操作就可以监视到内部成员的读写, 而defineProperty通过特定的方式单独定义需要被监视的属性,需要去做很多额外的操作)
```javascript
// Proxy 对象
const person = {
name: 'liuchao',
age: 18
}
// const personProxy = new Proxy(person, {
// // 代理处理对象
// get(target, property){
// return property in target ? target[property] : 'default'
// // console.log(target, property)
// // return 100
// },
// set(target, property, value){
// if(property === 'age'){
// if(!Number.isInteger(value)){
// throw new TypeError(`${value} is not an int`)
// }
// }
// target[property] = value
// // console.log(target, property, value)
// }
// })
// personProxy.age = 99
// personProxy.gender = 'man'
// console.log(personProxy.name)
// console.log(personProxy.xxx)
// -----------------------------------
// Proxy 监听对象delete属性行为
// const personProxy = new Proxy(person, {
// deleteProperty(target, property){
// console.log('delete', target, property)
// delete target[property]
// }
// })
// delete personProxy.age
// console.log(person)
// -------––-------––-------––-------––-------––
// 如何使用Proxy对象监听数组
const list = []
const listProxy = new Proxy(list, {
set(target, property, value){
console.log('set', property, value)
target[property] = value
return true //表示设置成功
}
})
listProxy.push(100)
```
#### Reflect 统一的对象操作API
- 属于一个静态类, 不能通过new 构建实例对象, 只能调用静态类中的一些方法
- Reflect内部封装了一系列对对象的底层操作(14个, 一个被废弃; 方法名和Proxy中的方法名相同)
- Reflect成员方法就是Proxy处理对象的默认实现
- **最大的作用** 统一提供一套用于操作对象的API
```javascript
// Reflect 对象
// const obj = {
// foo: '122',
// bar: '222'
// }
// const proxy = new Proxy(obj, {
// get(target, property){
// console.log('watch login~')
// return Reflect.get(target, property)
// }
// })
// console.log(proxy.foo)
// example
const obj = {
name: 'liuchao',
age: 18
}
// console.log('name' in obj)
// console.log(delete obj['age'])
// console.log(Object.keys(obj))
console.log(Reflect.has(obj, 'name'))
console.log(Reflect.deleteProperty(obj, 'age'))
console.log(Reflect.ownKeys(obj))
```
#### Promise
- 一种更优的异步编程解决方案, 解决了传统异步编程中回调函数嵌套过深的问题
#### class类
- 静态成员 static
- 类的继承 extends
```javascript
// Class 关键词
// function Person (name){
// this.name = name
// }
// Person.prototype.say = function(){
// console.log(`hi, my name is ${this.name}`)
// }
// class Person {
// constructor(name){
// this.name = name
// }
// say(){
// console.log(`hi, my name is ${this.name}`)
// }
// }
// const p = new Person('liuchao')
// p.say()
// ----------------------------------------
// 静态方法 static
// class Person {
// constructor(name){
// this.name = name
// }
// say(){
// console.log(`hi, my name is ${this.name}`)
// }
// static create(name){
// return new Person(name)
// }
// }
// Person.create('liuchao').say()
// ----------------------------------------
// 类的继承 extends
class Person {
constructor(name){
this.name = name
}
say(){
console.log(`hi, my name is ${this.name}`)
}
}
class Student extends Person {
constructor(name, num){
super(name)
this.num = num
}
hello(){
super.say()
console.log(`my school number is ${this.name}`)
}
}
const s = new Student('liuchao', 200)
s.hello()
```
#### Set数据结构
```javascript
const s = new Set()
// 向Set数据结构中添加数据 重复添加会覆盖(后边覆盖前边的)
s.add(1).add(2).add(3).add(4).add(1)
console.log(s)
// 遍历成员
s.forEach(i => console.log('foreach' + i))
for(let i of s){
console.log('for+ of' + i)
}
// 获取Set数据集合的长度
console.log(s.size)
// 判断Set数据中是否有某个元素
console.log(s.has(100))
// 删除Set数据中某个元素
console.log(s.delete(1), s)
// 清除Set数据中所有元素
console.log(s.clear(), s)
// 作用 : 数组去重
const arr = [1, 1, 3, 4, 2, 4, 2]
// const result = Array.from(new Set(arr))
const result = [...new Set(arr)]
console.log(result)
```
#### Map数据结构
- 普通对象只能使用string作为键值(就算不是也会转换成string)
- Map数据结构键值可以为任意值
```javascript
// const obj = {}
// obj[true] = 'value'
// obj[123] = 'value'
// obj[{a:1}] = 'value'
// console.log(Object.keys(obj))
// console.log(obj['[object Object]'])
const m = new Map()
const tom = {name: 'tom'}
m.set(tom, 90)
console.log(m)
console.log(m.get(tom))
// m.has()
// m.delete()
// m.clear()
// 遍历
m.forEach((value, key) => {
console.log(value, key)
})
```
#### Symbol 一种全新的原始数据类型
- 最主要的作用就是为对象添加独一无二的属性名
- 唯一性
```javascript
// // shared.js ======================
// const cache = {}
// // a.js ===========================
// cache['foo'] = Math.random()
// cache['a_foo'] = Math.random()
// // b.js ===========================
// cache['foo'] = '123'
// cache['b_foo'] = '123'
// console.log(cache)
const s = Symbol()
console.log(s)
console.log(typeof s)
console.log(Symbol() === Symbol())
console.log(Symbol('foo'))
console.log(Symbol('bar'))
console.log(Symbol('baz'))
const obj = {
[Symbol()]: '122'
}
console.log(obj)
// a.js ================================
const name = Symbol()
const person = {
[name]: 'liuchao',
say(){
console.log(this[name])
}
}
// b.js ================
// console.log(person[Symbol()]) //得不到结果
person.say()
// Symbol补充
// 唯一性
console.log(
Symbol() === Symbol(),
Symbol('foo') === Symbol('foo'),
)
// 全局服用一个相同的Symbol值 方式:1. 全局变量 2. Symbol.for方法
const s1 = Symbol.for('foo')
const s2 = Symbol.for('foo')
console.log(s1 === s2)
// ***** 特别注意: Symbol.for方法中维护的注册表是字符串string关联的, 非string也会转换成string
console.log(
Symbol.for(true) === Symbol.for('true')
)
// Symbol提供的Symbol常量
console.log(Symbol.iterator)
console.log(Symbol.hasInstance)
const obj1 = {
[Symbol.toStringTag]: 'Symbol'
}
console.log(obj1.toString())
// Symbol作为键值通过for in循环无法拿到 Object.key()也无法获取到 字符串化JSON.stringigy也会被忽略掉
const obj2 = {
[Symbol()]: '222',
name: 'liuchao'
}
for(let key in obj2){
console.log(key)
}
console.log(Object.keys(obj2))
console.log(JSON.stringify(obj2))
// 可以通过Object.getOwnPropertySymbols()
console.log(Object.getOwnPropertySymbols(obj2))
```
#### BigInt 原始数据类型(用于储存更长的数字) 处在stage-4阶段
#### for of循环 作为遍历所有数据结构的统一方式
- ES2015提供了Iterable接口(可迭代接口)
- 实现Iterable接口就是for...of的前提
- 实现可迭代接口
- 迭代器模式
- 迭代器意义: 对外提供统一遍历接口, 不用关心数据内部结构
```javascript
const arr = [100, 200 , 399 ,3999]
// for(const item of arr){
// console.log(item)
// }
// arr.forEach(item => {
// console.log(item)
// })
// for of 可以使用break关键字 forEach循环不可以
// for(const item of arr){
// console.log(item)
// if(item > 200){
// break
// }
// }
// arr.forEach(item => {
// console.log(item)
// // if(item > 200){
// // break
// // }
// })
// 终止遍历方法 arr.some() arr.every()
// ----------------------------------------------
// 遍历Set数据和Map数据
const s = new Set(['foo', 'bar'])
for(const item of s){
console.log('set ' + item)
}
const m = new Map()
m.set('foo', '133')
m.set('bar', '13dkdk')
for(const [key, value] of m){
console.log('map', key, value)
}
// -----------------------------
// 遍历普通对象
const obj = {
foo: 122,
bar: 333
}
// for(const item of obj){ //TypeError: obj is not iterable 不可被迭代
// console.log('obj ' + item)
// }
```
#### 迭代器[Iterator]
```javascript
// 迭代器 [Iterator]
const set = new Set(['foo', 'bar', 'baz'])
const iterator = set[Symbol.iterator]()
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
// 实现可迭代接口
// const obj = {
// [Symbol.iterator]: function(){ //Iterator接口
// return {
// next: function(){ //IteratorREsult
// return {
// value: 'liuchao',
// done: true
// }
// }
// }
// }
// }
const obj = {
store: ['foo', 'bar', 'baz'],
[Symbol.iterator]: function(){ //Iterator接口
let index = 0
const self = this
return {
next: function(){ //IteratorREsult
const result = {
value: self.store[index],
done: index >= self.store.length
}
index++
return result
}
}
}
}
for(const item of obj){
console.log(item)
}
// 迭代器模式
// 场景: 多人协同开发一个任务清单应用
// 我的代码=====================
const todos = {
life: ['吃饭', '睡觉', '打豆豆'],
learn: ['语文', '数学', '英语'],
work: ['喝茶'],
each(callback){
const all = [].concat(this.life, this.learn, this.work)
for(const item of all){
callback(item)
}
},
[Symbol.iterator]: function(){
const all = [...this.life, ...this.learn, ...this.work]
let index = 0
return {
next: function(){
return {
value: all[index],
done: index++ >= all.length
}
}
}
}
}
// 你的代码=====================
// for(const item of todos.life){
// console.log(item)
// }
// for(const item of todos.learn){
// console.log(item)
// }
// for(const item of todos.work){
// console.log(item)
// }
todos.each(item => {
console.log(item)
})
console.log('-----------------')
for(const item of todos){
console.log(item)
}
```
#### 生成器函数Generator
- 避免异步编程中回调嵌套过深,从而提供更好的异步编程解决方案
```javascript
// function * foo(){
// console.log('foo')
// return 100
// }
// const result = foo()
// console.log(result.next())
function * foo(){
console.log(11111)
yield 100
console.log(2222)
yield 200
console.log(3333)
yield 300
}
const generator = foo()
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
console.log(generator.next())
// ---------------------------------
// 生成器的应用 Generator
// 案例一: 发号器
console.log('-----------------------------')
// function * createIdMaker(){
// let id = 1
// while(true){
// yield id++
// }
// }
// const idMaker = createIdMaker()
// console.log(idMaker.next().value)
// console.log(idMaker.next().value)
// console.log(idMaker.next().value)
// console.log(idMaker.next().value)
// console.log(idMaker.next().value)
// 案例二: 使用Generator函数实现iterator接口
const todos = {
life: ['吃饭', '睡觉', '打豆豆'],
learn: ['语文', '数学', '英语'],
work: ['喝茶'],
[Symbol.iterator]: function *(){
const all = [...this.life, ...this.learn, ...this.work]
for(const item of all){
yield item
}
}
}
for(const item of todos){
console.log(item,'yield')
}
```
#### ES Modules 语言层面的模块化标准
#### ES2016
- 1. Array.prototype.includes
- 2. 指数运算符 console.log(2 ** 20)
```javascript
// 1. Array.prototype.includes
const arr = ['foo', 1, NaN, false]
// indexOf不能用于查找NaN
console.log(arr.indexOf('foo'))
console.log(arr.indexOf('bar'))
console.log(arr.indexOf(NaN))
console.log(arr.includes('foo'))
console.log(arr.includes('bar'))
console.log(arr.includes(NaN))
// 指数运算符
console.log(Math.pow(2, 10))
console.log(2 ** 20)
```
#### ES2017
- 1. Object.values
- 2. Object.entries
- 3. Object.getOwnPropertyDescriptors
- 4. String.prototype.padStart / String.prototype.padEnd
- 5. 在函数参数中添加尾逗号
- 6. Async/await
```javascript
const obj = {
foo: 'value1',
bar: 'value2'
}
// - 1. Object.values
console.log(Object.values(obj))
// - 2. Object.entries
console.log(Object.entries(obj))
for(const [key, value] of Object.entries(obj)){
console.log(key, value)
}
console.log(new Map(Object.entries(obj)))
// - 3. Object.getOwnPropertyDescriptors
const p1 = {
firstName: 'lei',
lastName: 'wang',
get fullName(){
return this.firstName + ' ' + this.lastName
}
}
console.log(p1.fullName)
// const p2 = Object.assign({}, p1)
// p2.firstName = 'liuchao'
// console.log(p2.fullName)
const descriptors = Object.getOwnPropertyDescriptors(p1)
console.log(descriptors)
const p2 = Object.defineProperties({}, descriptors)
p2.firstName = 'liuchao'
console.log(p2.fullName)
// - 4. String.prototype.padStart / String.prototype.padEnd
const books = {
html: 3,
css: 15,
js: 22
}
// for(const [key, value] of Object.entries(books)){
// console.log(key, value)
// }
for(const [key, value] of Object.entries(books)){
console.log(`${key.padEnd(16, '-')}|${value.toString().padStart(3, '0')}`)
}
// - 5. 在函数参数中添加尾逗号
function foo2(bar, baz,) {
return 100
}
const arr2 = [100,200,300,]
```