一、内容概要
- ECMAScript 与 JavaScript
- ECMAScript 的发展过程
- ECMAScript 2015的新特性
- And more…
二、ECMAScript 概述
-
JavaScript 是在 ECMAScript 的基础上扩展的语言。
-
JavaScript @ web端:ECMAScript和WebAPI(DOM、BOM)组成
-
JavaScript @ node.js端:ECMAScript和 Node APIs (fs、net、etc)组成
三、ECMAScript 2015 (ES6)
1.ES6学习概要
- 解决原有语法上的一些问题或者不足
- 对原有语法进行增强
- 全新的对象、全新的方法、全新的功能
- 全新的数据类型和数据结构
2.准备工作(学习环境)
-
运行环境 : node(用) 和浏览器都行
-
自动监听 node 运行,用 nodemon 包:
/* 命令行输入*/ npm init // 初始化 npm install -g nodemon // 安装包 nodemon index.js // 启动项目
3.ES2015 let 和 块级作用域
作用域
- 全局作用域
- 函数作用域
- 块级作用域(es2015 新增)
- var 声明的变量会产生作用域提升。
- let 关键字声明的变量不会提升,必须先声明才能用,否则报错:引用异常
4.ES2015 const 关键词
-
const 是用来定义 JavaScript 的常量(恒量)的。
-
const 只是在 let 的基础上多了个只读的特性。
-
const 只有在声明的同时赋值,否则会报错。
const obj = {}; obj.name = 'linxing'; // 不会报错,因为没有改变obj的引用地址,只是增加了obj的属性 obj = {}; // 错误,修改了obj的引用值
-
最佳实践:不用 var , 主用 const,变量需要改变才用 let
5.ES2015 数组的解构
const arr = [100, 200, 300]
/* 不用解构的做法 */
// const foo = arr[0]
// const bar = arr[1]
// const baz = arr[2]
// console.log(foo, bar, baz) // 100 200 300
/*解构的做法*/
// const [foo, bar, baz] = arr
// console.log(foo, bar, baz) // 100 200 300
// const [, , baz] = arr // 不需要的参数可以用 , 分开
// console.log(baz) // 300
// const [foo, ...rest] = arr // 可以用 ...的语法获取剩余参数(只能放在最后面)
// console.log(rest) // [ 200, 300 ]
// const [foo, bar, baz, more] = arr // 当左边参数多于右边 多余的变成 undefined
// console.log(more) // undefined
// const [foo, bar, baz = 123, more = 'default value'] = arr // 给默认参数
// console.log(bar, more) // 200 default value
/*示例*/
const path = '/foo/bar/baz'
// const tmp = path.split('/')
// const rootdir = tmp[1]
const [, rootdir] = path.split('/')
console.log(rootdir)
5.ES2015 对象的解构
-
对象的解构采用的是 属性匹配
const obj = { name: 'zce', age: 18 } const { name } = obj console.log(name) // 'zce'
-
对象的解构重命名
const obj = { name: 'zce', age: 18 } const name = 'tom' const { name: objName } = obj console.log(objName) // 'zce'
-
对象的解构重命名+给默认值
const { log } = console; const obj = { name: 'zce', age: 18 } const name = 'tom' const { name: objName = 'linxing' } = obj log(objName) // 'zce'
6.ES2015 字符串模板(``)
-
字符串模板中间可以使用 ${变量名} 包裹变量
-
示例
const name = 'tom' const msg = `hey, ${name} --- ${1 + 2} ---- ${Math.random()}` console.log(msg) // hey, tom --- 3 ---- 0.15891994236543483
7.ES2015 带标签的模板字符串(``)
-
模板字符串的标签就是一个特殊的函数,用来重新渲染处理模板引擎。
-
使用这个标签就是调用这个函数
const name = 'tom' const gender = false function myTagFunc (strings, name, gender) { // strings 是个以 ${} 切割的数组 // console.log(strings, name, gender) // return '123' const sex = gender ? 'man' : 'woman' return strings[0] + name + strings[1] + sex + strings[2] } const result = myTagFunc`hey, ${name} is a ${gender}.` console.log(result)
8.ES2015 字符串的扩展语法
-
str.startsWith(params ) : 查找字符串中的开头是否以 params 开头,返回 boolean 类型。
-
str.endsWith(params ) : 查找字符串中的结尾是否以 params 开头,返回 boolean 类型。
-
str.includes(params ) : 查找字符串中是否包含 params ,返回 boolean 类型。
const message = 'Error: foo is not defined.' console.log( // message.startsWith('Error') // true // message.endsWith('.') // true message.includes('foo') // true )
9.ES2015 参数的默认值
-
es2015 之前使用短路 (||)的方式设置默认参数,这是错误的做法
function foo (enable) { // 短路运算很多情况下是不适合判断默认参数的,例如 0 '' false null // enable = enable || true enable = enable === undefined ? true : enable console.log('foo invoked - enable: ') console.log(enable) } foo(false)
-
默认参数一定是在形参列表的最后
function foo (enable = true) { console.log('foo invoked - enable: ') console.log(enable) } foo(false)
10.ES2015 剩余参数
-
es2015 之前是使用 arguments 的伪数组去接受参数。
-
es2015 中使用 … 去接受 ,但是参数有多个的时候需要放在最后面
function foo (first, ...args) { console.log(args) } foo(1, 2, 3, 4)
11.ES2015 展开数组
const arr = ['foo', 'bar', 'baz']
// console.log( // 原始方法
// arr[0],
// arr[1],
// arr[2],
// )
// console.log.apply(console, arr) // es2015 之前的方法
console.log(...arr) // ...方式
12.ES2015 箭头函数
-
使用箭头函数 能使代码更加简单易读。
const inc = n => n + 1 console.log(inc(100)) // 101
13.ES2015 箭头函数和this指向
const person = {
name: 'tom',
// sayHi: function () {
// console.log(`hi, my name is ${this.name}`)
// }
sayHi: () => {
console.log(`hi, my name is ${this.name}`) // hi, my name is undefined
},
sayHiAsync: function () {
// const _this = this // 用变量保存起来
// setTimeout(function () {
// console.log(_this.name)
// }, 1000)
console.log(this)
setTimeout(() => {
// console.log(this.name)
console.log(this)
}, 1000)
}
}
person.sayHiAsync()
14.ES1025 对象字面量的语法增强
const bar = '345'
const obj = {
foo: 123,
// bar: bar
// 属性名与变量名相同,可以省略 : bar
bar,
// method1: function () {
// console.log('method111')
// }
// 方法可以省略 : function
method1 () {
console.log('method111')
// 这种方法就是普通的函数,同样影响 this 指向。
console.log(this)
},
// Math.random(): 123 // 不允许
// 通过 [] 让表达式的结果作为属性名
[bar]: 123
}
// obj[Math.random()] = 123
console.log(obj)
obj.method1()
15.ES2015 Object.assign()
-
object.assig()方法是把源对象里的属性来赋给目标对象。(有则覆盖,没有则添加)。
const source1 = { a: 123, b: 123 } const source2 = { b: 789, d: 789 } const target = { a: 456, c: 456 } const result = Object.assign(target, source1, source2) // target为目标对象,其他的源对象 console.log(target) // { a: 123, c: 456, b: 789, d: 789 } console.log(result === target) // true
-
应用场景
function func (obj) { // obj.name = 'func obj' // console.log(obj) const funcObj = Object.assign({}, obj) funcObj.name = 'func obj' console.log(funcObj) } const obj = { name: 'global obj' } func(obj) console.log(obj)
16.ES2015 Object.is()
console.log(
// 0 == false // => true
// 0 === false // => false
// +0 === -0 // => true
// NaN === NaN // => false
// Object.is(+0, -0) // => false
// Object.is(NaN, NaN) // => true
)
17.ES2015 Proxy
-
这是vue.3 开始用 proxy 处理响应数据,vue3以前使用的是Object.defineProperty()方法响应数据。
-
const person = { name: 'zce', age: 20 } 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 = 100 personProxy.gender = true
18.ES2015 Proxy 对比 Object.defineProperty() 方法
-
Proxy 是以非入侵的方式监管了对象的读写
const person = {} Object.defineProperty(person, 'name', { get () { console.log('name 被访问') return person._name }, set (value) { console.log('name 被设置') person._name = value } }) Object.defineProperty(person, 'age', { get () { console.log('age 被访问') return person._age }, set (value) { console.log('age 被设置') person._age = value } }) person.name = 'jack' console.log(person.name)
-
Proxy 比 Object.defineProperty() 更加强大,可以直接监听数组,而不需要对数组进行额外操作。
const list = [] const listProxy = new Proxy(list, { set (target, property, value) { console.log('set', property, value) target[property] = value return true // 表示设置成功 } }) listProxy.push(100) listProxy.push(100)
-
Proxy 可以监视读写以外的操作
const person = { name: 'zce', age: 20 } const personProxy = new Proxy(person, { deleteProperty (target, property) { console.log('delete', property) delete target[property] } }) delete personProxy.age console.log(person)
-
Proxy 方式更为合理
const person2 = { name: 'zce', age: 20 } const personProxy = new Proxy(person2, { get (target, property) { console.log('get', property) return target[property] }, set (target, property, value) { console.log('set', property, value) target[property] = value } }) personProxy.name = 'jack' console.log(personProxy.name)
19.ES2015 Reflect方法
- Reflect 内部封装了一系列对对象的底层操作
const obj = {
name: 'zce',
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))
20.ES2015 Promise
21.ES2015 Class类
class Person {
constructor (name) {
this.name = name
}
say () {
console.log(`hi, my name is ${this.name}`)
}
}
const p = new Person('tom')
p.say()
22.ES2015 Class 类的静态方法
class Person {
constructor (name) {
this.name = name
}
say () {
console.log(`hi, my name is ${this.name}`)
}
static create (name) {
return new Person(name)
}
}
const tom = Person.create('tom')
tom.say()
23.ES2015 Class 类的继承(extends super())
class Person {
constructor (name) {
this.name = name
}
say () {
console.log(`hi, my name is ${this.name}`)
}
}
class Student extends Person {
constructor (name, number) {
super(name) // 父类构造函数
this.number = number
}
hello () {
super.say() // 调用父类成员
console.log(`my school number is ${this.number}`)
}
}
const s = new Student('jack', '100')
s.hello()
24.Set()数据解构
-
确保存储在 Set 数据结构的唯一性。
- 添加方法:setData.add (params)
- 删除方法:setData.delete(params)
- 查找方法:setData.has(parsms)
- 清除方法:setData.clear()
const s = new Set() s.add(1).add(2).add(3).add(4).add(2) console.log(s) s.forEach(i => console.log(i)) for (let i of s) { console.log(i) } console.log(s.size) console.log(s.has(100)) console.log(s.delete(3)) console.log(s) s.clear() console.log(s)
-
经常用来给数组去重
const arr = [1, 2, 1, 3, 4, 1] // const result = Array.from(new Set(arr)) const result = [...new Set(arr)] console.log(result)
25.ES2015 Map()数据结构
- 可以运用任意类型的值作为键
- 键 =》值
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)
})
26.ES2015 Symbol()数据结构
- 扩展对象,属性名冲突问题
- 两个 Symbol 永远不会相等
- 使用 Symbol 为对象添加用不重复的键
- Symbol 模拟实现私有成员
- 最主要功能就是为对象添加独一无二的属性名
const name = Symbol()
const person = {
[name]: 'zce',
say () {
console.log(this[name])
}
}
27.ES2015 for…fo循环
const arr = [100, 200, 300, 400]
for (const item of arr) {
console.log(item) // 100 200 300 400
if (item > 100) {
break
}
}
28.ES2015 实现可迭代接口
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())
while (true) {
const current = iterator.next()
if (current.done) {
break // 迭代已经结束了,没必要继续了
}
console.log(current.value)
}
29.迭代器模式
const todos = {
life: ['吃饭', '睡觉', '打豆豆'],
learn: ['语文', '数学', '外语'],
work: ['喝茶'],
// 提供统一遍历访问接口
each: function (callback) {
const all = [].concat(this.life, this.learn, this.work)
for (const item of all) {
callback(item)
}
},
// 提供迭代器(ES2015 统一遍历访问接口)
[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
}
}
}
}
}
todos.each(function (item) {
console.log(item)
})
console.log('-------------------------------')
for (const item of todos) {
console.log(item)
}
29.ES2015 生成器函数(Generator)
- 通过在函数的前面加个* 生成一个生成器函数的函数体,函数体会执行直到遇到 yield 停止执行。等再次调用.next() 再往下执行。
// function * foo () {
// console.log('zce')
// return 100
// }
// const result = foo()
// console.log(result.next())
function * foo () {
console.log('1111')
yield 100
console.log('2222')
yield 200
console.log('3333')
yield 300
}
const generator = foo()
console.log(generator.next()) // 第一次调用,函数体开始执行,遇到第一个 yield 暂停
console.log(generator.next()) // 第二次调用,从暂停位置继续,直到遇到下一个 yield 再次暂停
console.log(generator.next()) // 。。。
console.log(generator.next()) // 第四次调用,已经没有需要执行的内容了,所以直接得到 undefined
29.ES2015 生成器应用
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)
// 案例2:使用 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)
}
四、ESMAScript 2016(ES7)
1.数组的includes 方法
-
Array.property.includes 方法
const arr = ['foo', 1, NaN, false] console.log(arr.includes('foo')) // 返回 Boolean 可以查找NaN
2.指数运算符
console.log(Math.pow(2, 10))
console.log(2 ** 10)