ES6相关知识
由于很多开发者并没有理解语言和平台之间的关系,并且市面上ES相关的资料比较零散,并且我们开发者对ES的理解和掌握程度不尽相同,后续大前端的趋势和高阶课程都必须用到这些内容,所以学习ES很重要。 后续面试中面试闻到ES6新特性请参考以下知识。 具体知识如下:
- ECMAScript概述: JS是ECMAScript的扩展,实现了它的标准,并进行扩展,保证真实项目中能使用。 在浏览器环境:js相当于ES+WebAPI(DOM+BOM) 在node环境下:js相当于ES+node API(fs+net+etc..)
- ES2015
- a. 解决原有语法的问题和不足
- b. 对原有语法进行增强
- c. 全新的对象,方法,功能
- d. 全新的数据类型和数据结构
- let与块级作用域
// let 声明的成员只会在所声明的块中生效 -------------------------------------------
if (true) {
// var foo = 'zce'
let foo = 'zce'
console.log(foo)
}
// let 在 for 循环中的表现 ---------------------------------------------------
for (var i = 0; i < 3; i++) {
for (var i = 0; i < 3; i++) {
console.log(i)
}
console.log('内层结束 i = ' + i)
}
for (var i = 0; i < 3; i++) {
for (let i = 0; i < 3; i++) {
console.log(i)
}
console.log('内层结束 i = ' + i)
}
// let 应用场景:循环绑定事件,事件处理函数中获取正确索引 -----------------------------------------------------
var elements = [{}, {}, {}]
for (var i = 0; i < elements.length; i++) {
elements[i].onclick = function () {
console.log(i)
}
}
elements[2].onclick()
var elements = [{}, {}, {}]
for (var i = 0; i < elements.length; i++) {
elements[i].onclick = (function (i) {
return function () {
console.log(i)
}
})(i)
}
elements[0].onclick()
var elements = [{}, {}, {}]
for (let i = 0; i < elements.length; i++) {
elements[i].onclick = function () {
console.log(i)
}
}
elements[0].onclick()
// for 循环会产生两层作用域 ----------------------------------
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++
// let 修复了变量声明提升现象 --------------------------------------------
console.log(foo)
var foo = 'zce'
console.log(foo)
let foo = 'zce'
- a. 花括号包含起来的范围就叫做块级作用域
- b. 通过let声明的变量就是块级作用域的变量,外部是无法访问这个变量的
- c. let并不会出现声明提升的问题
- const
- a. const声明后不可被修改
const name = 'zce'
// 恒量声明过后不允许重新赋值
name = 'jack'
// 恒量要求声明同时赋值
const name
name = 'zce'
// 恒量只是要求内层指向不允许被修改
const obj = {}
// 对于数据成员的修改是没有问题的
obj.name = 'zce'
obj = {}
最佳实践:不用var,主用const,配合let 5. 数组的解构
// 数组的解构
const arr = [100, 200, 300]
const foo = arr[0]
const bar = arr[1]
const baz = arr[2]
console.log(foo, bar, baz)
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 = 123, more = 'default value'] = arr
console.log(bar, more)
const path = '/foo/bar/baz'
// const tmp = path.split('/')
// const rootdir = tmp[1]
const [, rootdir] = path.split('/')
console.log(rootdir)
- b. 通过[...rest]能获取当前元素后面的所有值
- 对象的解构
- a. 通过这种方式const { name } = obj可以直接获取name的值
- b. 其他的用法和数组相同
// 对象的解构
const obj = { name: 'zce', age: 18 }
const { name } = obj
console.log(name)
const name = 'tom'
const { name: objName } = obj
console.log(objName)
const name = 'tom'
const { name: objName = 'jack' } = obj
console.log(objName)
const { log } = console
log('foo')
log('bar')
log('123')
- 模板字符串
- a. 使用反引号定义
- b. 通过
hey, ${name}这种方式可以将name变量直接添加到字符串中 - c. 前面可以定义标签函数,可以对模板字符串进行加工
// 模板字符串
// 反引号包裹
const str = `hello es2015, this is a string`
// 允许换行
const str = `hello es2015,
this is a \`string\``
console.log(str)
const name = 'tom'
// 可以通过 ${} 插入表达式,表达式的执行结果将会输出到对应位置
const msg = `hey, ${name} --- ${1 + 2} ---- ${Math.random()}`
console.log(msg)
// 带标签的模板字符串
// 模板字符串的标签就是一个特殊的函数,
// 使用这个标签就是调用这个函数
// const str = console.log`hello world`
const name = 'tom'
const gender = false
function myTagFunc (strings, name, gender) {
// 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)
- 字符串的扩展方法
- a. startsWith() 判断字符串是以什么开头的
- b. incluses() 判断是否包含什么
- c. endsWith() 判断是以什么结尾的
// 字符串的扩展方法
const message = 'Error: foo is not defined.'
console.log(
// message.startsWith('Error')
// message.endsWith('.')
message.includes('foo')
)
- 参数默认值
- a. 直接实参后面 = 默认值,带默认值的参数放在最后
// 函数参数的默认值
function foo (enable) {
// 短路运算很多情况下是不适合判断默认参数的,例如 0 '' false null
// enable = enable || true
enable = enable === undefined ? true : enable
console.log('foo invoked - enable: ')
console.log(enable)
}
// 默认参数一定是在形参列表的最后
function foo (enable = true) {
console.log('foo invoked - enable: ')
console.log(enable)
}
foo(false)
- 剩余参数
- a. 之前使用的arguments,es6中可以直接使用...args这种方式定义。只能在最后一位,只能使用一次。
// 剩余参数
// function foo () {
// console.log(arguments)
// }
function foo (first, ...args) {
console.log(args)
}
foo(1, 2, 3, 4)
- 数组展开
- a. 通过apply传递第一个参数是this的指向,第二个就是要展开的数组
- b. 也可以通过...的方式直接展开
// 展开数组参数
const arr = ['foo', 'bar', 'baz']
console.log(
arr[0],
arr[1],
arr[2],
)
console.log.apply(console, arr)
console.log(...arr)
- 箭头函数
- a. 使用=>来创建函数
// 箭头函数
// function inc (number) {
// return number + 1
// }
// 最简方式
// const inc = n => n + 1
// 完整参数列表,函数体多条语句,返回值仍需 return
const inc = (n, m) => {
console.log('inc invoked')
return n + 1
}
console.log(inc(100))
const arr = [1, 2, 3, 4, 5, 6, 7]
// arr.filter(function (item) {
// return item % 2
// })
// 常用场景,回调函数
arr.filter(i => i % 2)
- b. 箭头函数不会改变this的指向
- c. 箭头函数中的this指向的是当前作用域里面的this
// 箭头函数与 this
// 箭头函数不会改变 this 指向
const person = {
name: 'tom',
// sayHi: function () {
// console.log(`hi, my name is ${this.name}`)
// }
sayHi: () => {
console.log(`hi, my name is ${this.name}`)
},
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()
- 对象字面量的增强
- a. 计算属性名是使用[]包起来
// 对象字面量
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()
- 对象新增方法
- a. assign方法是用后面对象中的属性去覆盖目标对象中的属性
- b. is判断是否相等
- c. definePropProperty
// 应用场景
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)
- proxy
- a. 代理相当于门卫,监听对象的访问读取和设置
- b. 其中包含get和set方法
// Proxy 对象
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)
}
})
c. 16. Reflect
- a. 静态类,封装了一系列对对象的底层操作
// Reflect 对象
const obj = {
foo: '123',
bar: '456'
}
const proxy = new Proxy(obj, {
get (target, property) {
console.log('watch logic~')
return Reflect.get(target, property)
}
})
console.log(proxy.foo)
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))
- Promise
- a. 全新的异步编程解决方案
- class
- a. 独立声明类型的语法
class Person {
constructor (name) {
this.name = name
}
say () {
console.log(`hi, my name is ${this.name}`)
}
}
const p = new Person('tom')
p.say()
- 类的继承
// extends 继承
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()
- 新增的set数据结构
- a. 和数组非常类似,但是set内部的成员是不允许重复的
- b. 其中包含has方法,判断集合中是否存在某个值
- Map数据结构
-
- a. 和对象很像,严格意义上的键值对集合,可以用任意类型作为键值
- Symbol
- a. 全新的数据类型,定义一个唯一的值
- b. 用在对象的键上,避免对象的键重复
- c. 用在对象的私有成员上,避免外部访问这个成员
- d. 可以通过Symbol.for('')传进一个字符串标识,两个相同标识的Symbol的值是相同的。但是传进去的值都会自动转成字符串,传进去boolean的true和传进去字符串的true返回的值也是相同的
- for of
- a. 可以通过break终止循环
- b. 可以遍历set
- c. 遍历map之后得到的是包含两个成员的数组
// for...of 循环
const arr = [100, 200, 300, 400]
for (const item of arr) {
console.log(item)
}
// for...of 循环可以替代 数组对象的 forEach 方法
arr.forEach(item => {
console.log(item)
})
for (const item of arr) {
console.log(item)
if (item > 100) {
break
}
}
- 可迭代接口
- a. 是for of遍历的前提
- b. 所有能被for of遍历的数据类型都需要实现这个Iterable,内部都会挂载一个iterator的方法
// 迭代器(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())
while (true) {
const current = iterator.next()
if (current.done) {
break // 迭代已经结束了,没必要继续了
}
console.log(current.value)
}
- 生成器
- a. yeild函数不会直接执行,通过调用next方法会执行yeild
- b. 会自动返回生成器对象,遇到yeild值会停止执行
- c. 再次调用next会继续执行
- d. 惰性执行
- 生成器函数案例
- a. 发号器
// Generator 应用
// 案例1:发号器
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)
}
- b. 实现iterator方法
- ES Modules
- ES2016
- a. 新增includes方法
- b. 指数运算符
- ES2017
- a. Object的扩展方法
- b. 函数参数末尾可以添加逗号
- c. Async/Await