文章内容输出来源:拉勾教育前端高薪训练营
1.ES6+的一些新特性
1.1 let、const、var
- 变量提升:var会产生变量提升效果,使用undefined初始化变量值,可以在声明前访问,在声明前访问输出undefined;let和const仅在自身作用域内提升,并且不会挂载到window上,但是会有TDZ暂时性死区,在声明前访问会报错,只能在声明之后访问
- 作用域:由于var会产生变量提升,所以会有全局作用域;let和const的作用域仅限于自身声明的代码块内
- 初始化:var和let都可以仅声明,不进行初始化值;const必须在声明的同时初始化值
- 重新定义:var在第一次声明定义后,可以重新进行多次声明定义;let和const在同一作用域下只能被声明定义一次
- 多次赋值:var和let可以进行多次赋值;const基础数据类型不能多次赋值,引用数据类型可以改变其内部的属性值
1.2 数组和对象的解构
/* 数组解构 */
const arr = [100, 200, 300]
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) // undefined
const [foo, bar, baz = 123, more = 'default'] = arr
console.log(baz, more)
/* 对象解构 */
const obj = { name: 'zce', age: 18 }
const { name } = obj
console.log(name)
const age = 28
const { age: objAge } = obj
// const { age: objAge = 18 } = obj
1.3 模板字符串
- 模板字符串支持换行操作
const str = `hello
world
`
// hello
// world
- 支持变量,插值操作
${name} ${1 + 2}
console.log(`${1 + 2}`) // 3
- 支持标签函数
// 标签函数
const name1 = 'tom'
const gender = false
function myTagFunc (strings, name, gender) {
const sex = gender ? 'man' : 'woman'
return strings[0] + name + strings[1] + sex + strings[2]
}
const r = myTagFunc`hey, ${name1} is a ${gender}`
console.log(r) // hey, tom is a woman
1.4 字符串扩展方法
const msg = 'Error: foo is not defined.'
console.log(msg.startsWith('Error')) // true,字符串以Error开始
console.log(msg.endsWith('Error')) // false,字符串以Error结束
console.log(msg.includes('foo')) // true,字符串包含foo
1.5 参数默认值,剩余参数
// es5参数默认值
function foo1 (enable) {
// enable = enable || true // 短路运算符,在传入false时会出问题,此时会变成true
enable = enable === undefined ? true : enable // 三元运算替代短路运算
console.log(enable)
}
// es6参数默认值
function foo2 (enable = true) {
console.log(enable)
}
// es5剩余参数,arguments伪数组
function foo3 () {
console.log(arguments)
}
// es6剩余参数,只能出现在型参最后一位,并只能使用一次
function foo4 (...args) {
console.log(args)
}
1.6 展开数组
const array = [1, 2, 3, 4, 5, 6]
console.log(...array) // 1 2 3 4 5 6
2.箭头函数
箭头函数this根据外层(函数或者全局)上下文来决定,指向为外层this的指向。
const obj = {
name: '123',
a () {
console.log(this)
},
b: () => {
console.log(this)
},
c () {
setTimeout(() => {
console.log(this)
setTimeout(() => {
console.log(this)
}, 1000);
}, 1000);
},
d: () => {
setTimeout(() => {
console.log(this)
}, 1000);
}
}
obj.a() // obj
obj.b() // window
obj.c() // obj obj
obj.d() // window
3.对象的一些应用
3.1 对象字面
const bar = '123'
const o = {
foo: '123',
bar, // 对象字面量
method1 () {
console.log(this)
},
[Math.random()]: '123' // 计算属性名
}
3.2 object.assign
拷贝对象,但是只能拷贝一层数据,深层数据还是同一内存地址。
const assignA = {
a: 123,
b: 456,
c: {
a: 1,
}
}
function f (obj) {
const o = Object.assign({}, obj)
o.c = 'aaa'
console.log(o) // {a: 123, b: 456, c: "aaa"}
}
f(assignA)
console.log(assignA) // 不会被改变
const assignA = {
a: 123,
b: 456,
c: {
a: 1,
}
}
function f (obj) {
const o = Object.assign({}, obj)
o.c.a = 'aaa'
console.log(o) // {a: 123, b: 456, c: { a: aaa }}
}
f(assignA)
console.log(assignA) // c.a会被改变成aaa
3.3 object.is
判断两个值是否相等
console.log(0 == false) // 两等自动转换数据类型,输出true
console.log(0 === false) // 三等不会自动转换数据类型输出false,但是不能判断正负,会出现 -0 === +0 为true
console.log(NaN === NaN) // false
Object.is(NaN, NaN) // true
4.Proxy代理
和defineProperty比较,defineProperty只能监视属性的读写,Proxy可以监视到更多操作,例如delete和对象方法调用等,Proxy是非侵入的方式监管对象
Proxy的一些方法(handler方法 => 触发方式)
- get => 读取某个属性
- et => 写入某个属性
- as => in 操作符
- eleteProperty => delete 操作符
- etPrototypeOf => Object.getPrototypeOf()
- etPrototypeOf => Object.setPrototypeOf()
- sExtensible => Object.isExtensible()
- erventExtensions => Object.perventExtensions()
- etOwnPropertydescriptor => Object.getOwnPropertydescriptor()
- efineProperty => Object.defineProperty()
- wnKeys => Object.getOwnPropertyNames()、Object.getOwnPropertySymbols()
- pply => 调用一个函数
- onstruct => 用 new 调用一个函数
const obj = {
a: 123,
b: 456,
c: {
a: 1,
}
}
const proxyObj = new Proxy(obj, {
get (target, property) {
console.log(target, property)
return property in target ? target[property] : 'defaultValue'
},
set (target, property, value) {
if (property === 'a' && !Number.isInteger(value)) {
throw new TypeError(`${value} is not an int`)
}
target[property] = value
},
deleteProperty (target, property) {
console.log(target, property)
if (property === 'a') {
throw new Error(`${property} can not be deleted`)
}
delete target[property]
}
})
delete proxyObj.b
console.log(proxyObj)
5.Reflect
Reflect 统一的对象操作API
- 是一个静态类,不能用new Reflect()
- Reflect成员方法就是Proxy处理对象的默认实现
- 可以用来统一对象属性操作的风格
const obj = {
a: 123,
b: 456,
c: {
a: 1,
}
}
const proxyObj = new Proxy(obj, {
get (target, property) {
console.log(target, property)
return Reflect.get(target, property)
}
})
console.log(proxyObj.a)
// console.log('a' in obj)
console.log(Reflect.has(obj, 'a'))
// console.log(delete obj['a'])
console.log(Reflect.deleteProperty(obj, 'a'))
// console.log(Object.keys(obj))
console.log(Reflect.ownKeys(obj))
6.class 类
- 实例方法是通过类构造的实例对象去调用
- 静态方法是类本身调用的方法:static静态方法,static方法中的this不会指向实例,而是指向类型
- 类的继承使用extends
// es5中的类
function Person (name) {
this.name = name
}
Person.prototype.say = function () {
console.log(`hi, ${this.name}`)
}
// es6中的类
class Person {
constructor (name) {
this.name = name
}
say () {
console.log(`hi, ${this.name}`)
}
static create (name) {
// console.log(this) // this指向类型本身
return new Person(name)
}
}
class Student extends Person {
constructor (name, number) {
super(name) // super方法指向父类,调用即调用父类构造函数
this.number = number
}
hello () {
super.say() // super调用父类的方法
console.log(`my number is ${this.number}`)
}
}
const p = new Person('mike')
const p1 = Person.create('tom')
const s = new Student('jack', 10)
p.say()
p1.say()
s.hello()
7.set和map
/*
Set集合,里面不能存在重复的数据
*/
const s = new Set()
s.add(1).add(2).add(3).add(1)
console.log(s) // Set(3) {1, 2, 3}
s.forEach(i => console.log(i)) // 1 => 2 => 3
for (let i of s) {
console.log(i) // 1 => 2 => 3
}
console.log(s.size) // 长度3
console.log(s.has(1)) // true
s.delete(3)
console.log(s) // Set(2) {1, 2}
s.clear()
console.log(s) // Set(0) {}
const arr = ['a', 'b', 'c', 'a']
const r = [ ...new Set(arr) ]
console.log(r) // ["a", "b", "c"]
/*
Map对象,可以使用任意数据类型作为键,普通对象只能使用字符串作为键
*/
// 普通对象
const obj = {}
obj[true] = 'true'
obj[12] = '12'
obj[{ a: 1 }] = 'aaa'
console.log(Reflect.ownKeys(obj)) // ["12", "true", "[object Object]"]
// Map对象
const m = new Map()
const tom = { name: 'tom' }
m.set(tom, 90)
m.set(true, true)
m.set(12, 12)
m.forEach((val, key) => {
console.log(val, key) // 90 {name: "tom"} => true true => 12 12
})
console.log(m) // Map(3) {{…} => 90, true => true, 12 => 12}
console.log(m.get(tom)) // 90
console.log(m.has(tom)) // true
m.delete(tom)
console.log(m) // Map(2) {true => true, 12 => 12}
m.clear()
console.log(m) // Map(0) {}
8.Symbol
Symbol数据类型,为对象添加独一无二的属性名。
在Symbol出现前,对象属性名都是字符串,这容易造成属性名的冲突,比如他人写了一个对象,你在使用此对象时,想为这个对象添加一个新的方法名称,就可能与现有方法名称产生冲突。
而Symbol的出现恰恰解决了这种冲突,就算声明两个同样的变量,除非使用Symbol.for,不然也不会相等
const id = Symbol()
const name = Symbol('name')
const _name = Symbol('name')
const age = Symbol('age')
const obj = {
[name]: 'jimmy',
say () {
console.log(this[name]) // jimmy
},
s: 's'
}
obj[_name] = 'jim'
obj[id] = 1
obj[age] = 20
console.log(obj) // {s: "s", Symbol(name): "jimmy", Symbol(name): "jim", Symbol(): 1, say: ƒ, …}
obj.say()
// for in 循环无法获取,Object.keys,jSON.stringify无法获取对象的Symbol属性
for (const key in obj) {
console.log(key) // say => s
}
console.log(Object.keys(obj)) // ["say", "s"]
console.log(JSON.stringify(obj)) // {"s":"s"}
// 获取对象的Symbol属性
console.log(Reflect.ownKeys(obj)) // ["say", "s", Symbol(name), Symbol(name), Symbol(), Symbol(age)]
console.log(Object.getOwnPropertySymbols(obj)) // [Symbol(name), Symbol(name), Symbol(), Symbol(age)]
// 同样的Symbol声明不相等
console.log(Symbol() === Symbol()) // false
// 可以使用Symbol.for来相等,会将传入的属性值转为字符串类型
const s1 = Symbol.for('s')
const s2 = Symbol.for('s')
console.log(s1 === s2) // true
console.log(Symbol.for(true) === Symbol.for('true')) // true
9.for of
for of 遍历所有数据结构的统一方式,被遍历的数据需要具备Symbol.iterator属性
const arr = [1, 2, 3, 4, 5, 6]
const s = new Map()
s.set('foo', 'foo')
s.set('bar', 'bar')
for (const item of arr) {
console.log(item) // 1 => 2 => 3
if (item === 3) break
}
for (const [key, value] of s) {
console.log(key, value) // foo foo => bar bar
}
10.iterator迭代器
const arr = [1, 2, 3]
const iterator = arr[Symbol.iterator]() // 可遍历数据都有一个Symbol[iterator]属性
console.log(iterator.next()) // {value: 1, done: false}
console.log(iterator.next()) // {value: 2, done: false}
console.log(iterator.next()) // {value: 3, done: false}
console.log(iterator.next()) // {value: undefined, done: true}
// 实现迭代器让for...of来遍历对象
const obj = {
life: ['打游戏', '看书', '喝茶'],
learn: ['js', 'css', 'html'],
work: ['study']
}
obj[Symbol.iterator] = function () {
const keys = Object.keys(this)
let count = 0
return {
next () {
return { value: [keys[count], obj[keys[count]]], done: count++ >= keys.length }
}
}
}
const o = obj[Symbol.iterator]()
for (const [key, val] of obj) {
console.log(key, val) // life (3) ["打游戏", "看书", "喝茶"] => learn (3) ["js", "css", "html"] => work ["study"]
}
11.generator生成器
- 避免异步编程中回调嵌套过深
- 惰性执行,yield之后需要调用next()来继续执行
// 生成器模拟发号器
function * createMaker () {
let id = 1
while (true) {
yield id++
}
}
const idMaker = createMaker()
console.log(idMaker.next().value) // 1
console.log(idMaker.next().value) // 2
console.log(idMaker.next().value) // 3
// 实现生成器让for...of来遍历对象
const obj = {
life: ['打游戏', '看书', '喝茶'],
learn: ['js', 'css', 'html'],
work: ['study']
}
obj[Symbol.iterator] = function * () {
const keys = Object.keys(this)
for (const item of keys) {
yield [item, obj[item]]
}
}
for (const [key, val] of obj) {
console.log(key, val) // life (3) ["打游戏", "看书", "喝茶"] => learn (3) ["js", "css", "html"] => work ["study"]
}
12.es2016和es2017新增属性
/*
es2016新增两个新属性
* Array.prototype.includes 判断数组是否包含某个值
* 指数运算符 x ** y
*/
const arr = ['f', 1, NaN, false]
console.log(arr.indexOf('f'))
console.log(arr.indexOf(1))
console.log(arr.indexOf(NaN)) // indexOf不能识别
console.log(arr.indexOf(false))
console.log(arr.includes(NaN)) // includes可以识别
console.log(Math.pow(2, 10)) // Math的指数运算
console.log(2 ** 10) // es2016的指数运算
/*
es2017新增五个新功能
* Object.values,遍历对象的所有值
* Object.entries,将数组的键值对以数组形式返回,从而可以用for of来遍历
* Object.getOwnPropertyDescriptors,可以复制Object.assign不能复制的对象中的getter和setter,Object.assign
* String.prototype.padStart / String.prototype.padEnd 字符串填充
* 在函数的参数中添加尾逗号
*/
// Object.values Object.entries
const obj = {
a: 1,
b: 2,
get sum () {
return this.a + this.b
}
}
console.log(Object.values(obj))
console.log(Object.entries(obj))
console.log(new Map(Object.entries(obj)))
// Object.getOwnPropertyDescriptors
const obj1 = Object.assign({}, obj)
obj1.a = 3
console.log(obj1.sum)
const desc = Object.getOwnPropertyDescriptors(obj)
const obj2 = Object.defineProperties({}, desc)
obj2.a = 3
console.log(obj2.sum)
// String.prototype.padStart / String.prototype.padEnd
const books = {
html: 5,
css: 16,
javascript: 128
}
for (const [key, val] of Object.entries(books)) {
console.log(`${key.padEnd(16, '-')}|${val.toString().padStart(3, '0')}`)
}
// 在函数的参数中添加尾逗号
function foo (
a,
b,
) {}