写在前面的一些话,对于ES6之前一直都是用到哪里查哪里。一直也没有机会完全的读完阮一峰老师的书。刚好这段疫情时间有充足的时间用来学习,下面这篇文章可以带你了解es6新增属性、对象、数据结构等。如果真的有时间不妨读完阮一峰老师的书
let与块及作用域
es6以前只有全局作用域与函数作用域,es6中增加了块级作用域。
- es6之前
if(true){
var a = 1
console.log(a) //1
}
console.log(a) //1
- es6
if(true){
let a = 1
console.log(a) //1
}
console.log(a) //undefind
- const
声明后不可以修改内存地址
const test = 1234
test = 1 //报错
const test = {}
test.name = 1
console.log(test.name) //1
- 编程规范
- 不使用var
- 默认使用const
- 需要修改的变量通过let声明
数组解构
通过位置解构元素
- 以前的数组取值
const arr = [100,200,300]
const firtst = arr[0]
const two = arr[1]
const three = arr[2]
- 使用数组解构
const arr = [100,200,300]
const [firtst,two,three] = arr
console.log(firtst)//100
- 解构需要的变量
const arr = [100,200,300]
const [ , ,three] = arr
console.log(three)//300
- 解构当前成员后面的所有成员
只能在最后一个成员中使用
const arr = [100,200,300]
const [firtst, ...three] = arr
console.log(firtst) //100
console.log(three) //[200,300]
const arr = [100,200,300]
const [...firtst,three] = arr
console.log(firtst) //报错
- 解构设置默认值
const arr = [100]
const [firtst, two=200] = arr
console.log(two) //200
- 使用通过 ‘/’ 分割字符串,取第二个值
- es6之前
const str = 'foo/bar/baz' const tmp = str.split('/') const rootdir = tmp[1] console.log(rootdir)//bar
- es6
const str = 'foo/bar/baz' const [,rootdir] = str.split('/') console.log(rootdir)//bar
对象解构
通过变量名解构
const obj = {
name : 'abc',
age : 18
}
const { name } = obj
console.log(name) //abc
const obj = {
name : 'abc',
age : 18
}
const { ..._obj } = obj
console.log(_obj) //{ name: 'abc', age: 18 }
注意通过 ‘…’ 解构的对象中的引用类型为浅拷贝
const obj = {
name : 'abc',
age : 18,
tag : [
'php','js'
]
}
const { ..._obj } = obj
obj.name = 'zxc'
obj.tag[0] = 'java'
console.log(_obj.name) //abc
console.log(_obj.tag[0]) //java
关于块级作用域 变量名占用可以使用别名处理
const obj = {
name : 'abc',
age : 18
}
const name = 'zxc'
const { name } = obj //报错 Identifier 'name' has already been declared
const obj = {
name : 'abc',
age : 18
}
const name = 'zxc'
const { name : _name } = obj
console.log(_name) // abc
设置默认值值
const obj = {
// name : 'abc',
// age : 18
}
const name = 'zxc'
const {
name : _name = 456 ,
age = 17
} = obj
console.log(_name) // 456
console.log(age) // 17
模板字符串
- 新特性
- 支持多行字符串
- 通过差值表达式插入JavaScript语句
const name = 'abc'
const msg = `hi ${name}`
console.log(msg) // hi abc
const name = 'abc'
const msg = `hi ${1+2}`
console.log(msg) // hi 3
* 带标签的模板字符串
```js
const name = 'abc'
const job = 'php'
function tag(strings,name,job){
console.log(strings) // [ '', ' job is', '' ]
console.log(name,job) // abc php
return strings[0] + name +strings[1]+job
}
const str = tag`${name} job is ${job}`
console.log(str)//abc job is php
字符串扩展方法
- includes
是否包含传输参数
const err = 'Error: foo is not defined'
console.log(err.includes('foo')) // ture
- startsWith
以传入参数开头
const err = 'Error: foo is not defined'
console.log(err.startsWith('Error')) // ture
- endsWith
以传入参数结尾
const err = 'Error: foo is not defined'
console.log(err.endsWith('defined')) // ture
- repeat
重复原字符串n次
const str = '123='
console.log(str.repeat(2)) //123=123=
参数默认值
- 定义函数参数默认值
function foo (str='no'){
console.log(str)
}
foo() // no
foo('yes') //yes
带有默认值的参数一定要放到最后
function foo (start,str='no'){
console.log(start+str)
}
foo('=') // =no
foo('yes','no') //yesno
剩余参数获取
function test (name,...attributes){
console.log(`${name} ${attributes.join('-')}`)
}
test('zzy','php','js','java') //zzy php-js-java
展开运算符
const arr = [1,2,3,4,5]
console.log(...arr) //1 2 3 4 5
对象展开从上到下的顺序,如果存在相同变量,后面的会替换前面的
const obj = {
name:'zzy',
age:18
}
const _obj={
...obj,
job:'web',
age:19
}
console.log(_obj) //{ name: 'zzy', age: 19, job: 'web' }
箭头函数 (箭头函数中的this指向的是父级的环境)
定义一个函数
const inc = n => n+1
console.log(inc(10)) //11
下方代码等于上方代码
const inc = (n)=>{
return n+1
}
console.log(inc(10)) //11
返回数组中所有的偶数
const arr = [1,2,3,4,5,6]
const even_numbers=arr.filter(val=>val%2===0)
console.log(even_numbers)
箭头函数中的this 箭头函数不会改变this指向,在箭头函数的外面this 指向什么就是什么。一般函数的this只跟它的执行者有关,跟在哪定义和在哪执行无关,但是箭头函数就特殊了,它指向执行者的上下文,而不再是执行者了
const person = {
name : 'Tom',
say : function(){
console.log(`hi my name is ${this.name}`)
}
}
person.say() //hi my name is Tom
const person = {
name : 'Tom',
say :()=>{
console.log(`hi my name is ${this.name}`)
}
}
person.say() //hi my name is undefined
箭头函数实际应用 传统使用that or _this 的地方都可以使用箭头函数
const person = {
name: 'Tom',
say:function() {
setTimeout(() => {
console.log(`hi my name is ${this.name}`)
}, 1000)
}
}
person.say() //hi my name is Tom
对象字面量增强
const bar = '123'
const obj = {
bar : bar ,
say : function(){
}
}
const bar = '123'
const obj = {
bar,
say (){
}
}
动态key与计算属性
const obj = {
[Math.random()]:'abc'
}
console.log(obj) //{ '0.4094474269217101': 'abc' }
Object.assign 对象合并
用后面的对象属性覆盖第一个对象 返回第一个对象, 此方法为浅拷贝如果对象中包含引用类型依然会改变
const obj_a={
a:1,
}
const obj_b={
b:1,
}
const target = Object.assign(obj_a,obj_b)
console.log(target === obj_a) //true
console.log(target) // { a: 1, b: 1 }
Object.is 判断两个值是否相等
一般情况下不建议使用,而是使用 ‘’ or '=’
console.log(Object.is(1,1)) // true
console.log(Object.is(-0,0)) // false
console.log(Object.is(NaN,NaN)) //true
console.log(Object.is(null,null)) //true
console.log(Object.is(undefined,undefined)) //true
console.log(Object.is([1],[1])) //true
Proxy 代理器
const obj = {
name : 'abc',
age : 18
}
const objProxy = new Proxy(obj,{
get(target,property){
console.log(target,property)
//{ name: 'abc', age: 18 } name
return property in target ? target[property] : 'default'
},
set(target,property,value){
console.log(target,property,value)
// { name: 'abc', age: 18 } name 1
target[property]=value
}
})
console.log(objProxy.name) // abc
console.log(objProxy.job) // default
objProxy.name=1
Proxy vs Object.defineProperty
-
Object.defineProperty
- 对象读取
- 对象写入
-
Proxy
- 对象读取
- 对象写入
- 方法调用
- 非入侵式
- delete
delete
const obj = { name : 'abc', age : 18 } const objProxy = new Proxy(obj,{ get(target,property){ console.log(`get`) return property in target ? target[property] : 'default' }, set(target,property,value){ console.log(`set`) target[property]=value }, deleteProperty(target,property){ console.log(`delete`) delete target[property] } }) objProxy['name'] // get objProxy['name']=123 // set delete objProxy['name'] // delete
-
Proxy 可以监听到的方法
handler | 触发方式 |
---|---|
get | 读取某个属性 |
set | 写入某个属性 |
has | in操作符 |
deleteProperty | delete操作符 |
getPropertypeOf | Object.getPropertypeOf() |
setPropertypeOf | Object.setPropertypeOf() |
isExtensible | Object.isExtensible() |
preventExtensions | Object.preventExtensions() |
getOwnPropertyDescripttor | Object.getOwnPropertyDescripttor() |
defineProperty | Object.defineProperty() |
ownKeys | Object.getOwnPropertyNames() or Object.getOwnPropertySymbols() |
apply | 调用一个函数 |
construct | 用new 调用一个函数 |
Proxy监听数组方法
const list = []
const listProxy = new Proxy(list,{
set(target,property,value){
console.log('set',target,property,value)
target[property]=value
return true
}
})
listProxy.push(100)
listProxy.push(101)
Reflect.get()
Reflect 就是 Proxy 的默认实现,它提供了统一的一套操作对象的api
const obj = {
name: 'abc',
age: 18
}
const objProxy = new Proxy(obj, {
get(target, property) {
return Reflect.get(target, property)
},
set(target, property, value) {
target[property] = value
Reflect.set(target, property,value)
},
delete(target, property) {
Reflect.delete(target, property)
return true
}
})
promise
解决了传统异步编程中回调函数嵌套过深的问题,后面会单独写相关部分
class类(关键字)
原型链实现
function Person (name){
this.name = name
}
Person.prototype.say = function(){
console.log(`My name is ${this.name}`)
}
const person = new Person('Tom')
person.say() //My name is Tom
es6class实现
class Person {
constructor(name){
this.name=name
}
say(){
console.log(`My name is ${this.name}`)
}
}
const person = new Person('Tom')
person.say() //My name is Tom
静态方法,this不会挂载到实例对象
class Person {
constructor(name){
this.name=name
}
say(){
console.log(`My name is ${this.name}`)
}
static create (name){
return new Person(name)
}
}
const person = Person.create('Tom')
person.say() // My name is Tom
extent 类的继承
- super()必须在this之前
class Person {
constructor(name){
this.name=name
}
say(){
console.log(`My name is ${this.name}`)
}
static create (name){
return new Person(name)
}
}
class Student extends Person{
constructor(name,number){
super(name)
this.number = number
}
hello(){
super.say()
// 等同于 this.say()
console.log(`number is ${this.number}`)
}
}
const student = new Student('Tom',123)
student.hello()
// My name is Tom
// number is 123
Set(集合)全新的数据结构
const s = new Set()
s.add(1).add(2).add(3).add(1)
console.log(s) // 1 2 3
// 循环方式-自带的forEach方法
s.forEach(item=>console.log(item))
// 循环方式-es6 中的 for of 方法
for(let item of s){
console.log(item)
}
// 长度
console.log(s.size)//3
// has 是否存在指定的值 返回 true false
console.log(s.has(1))
// delete 删除指定值 返回 true false
console.log(s.delete(3))
console.log(s) // 1 2
Set 与 数组互转
const arr = [1,2,3,4]
// 数组转Set
const s1 = new Set(arr)
console.log(s1) // Set(4) { 1, 2, 3, 4 }
// Set 转数组
const a1 = Array.from(s1)
console.log(a1) // [ 1, 2, 3, 4 ]
// 结构方式
console.log([...s1]) // [ 1, 2, 3, 4 ]
数组去重—Set
const arr = [1,1,2,3,4,1]
const arr2 = [...new Set(arr)]
console.log(arr2) //[ 1, 2, 3, 4 ]
Map (类似对象数据结构)全新的数据结构
严格意义上的键值对集合,用来映射两个任意类型数据的对应关系
const m = new Map()
const tom = { name:'tom' }
m.set(tom,90)
console.log(m) // Map(1) { { name: 'tom' } => 90 }
console.log(m.get(tom))//90
// m.has()
// m.delete()
// m.clear()
// 遍历
m.forEach((val,index)=>console.log(val,index)) // 90 { name: 'tom' }
Symbol 全新的数据结构
const s = Symbol()
console.log(s) // Symbol()
console.log(typeof s) //symbol
作为对象key
const obj = {}
obj[Symbol()]=123
console.log(obj) //{ [Symbol()]: 123 }
实现私有成员 为对象增加独一无二的属性标识符
const name = Symbol()
const person = {
[name]:'Tom',
say(){
console.log(`name is ${this[name]}`)
}
}
person.say() //name is Tom
Symbol唯一性
console.log(Symbol('foo')===Symbol('foo')) // false
// Symbol.for 活
Symbol.for 接收字符串参数。相同的字符串就会返回相同的Symbol
console.log(Symbol.for('foo')===Symbol.for('foo')) // true
console.log(Symbol.for('true')===Symbol.for(true)) // true
Symbol提供了一些内置的对象,这些对象可以实现一些js的接口
- Symbol.iterator
- Symbol.hasInstance
尝试改写obj.toString() 方法
let obj = {
}
console.log(obj.toString()) // [object Object]
let obj1 = {
[Symbol.toStringTag]:'Tom',
key:'value'
}
console.log(obj1.toString()) // [object Tom]
对象中的Symbol 获取
// Object or Array常用的遍历或者获取方法都无法获得Symbol
for(let k in obj1){
console.log(k) // 只会输出 key
}
console.log(Object.keys(obj1)) // ['key']
// 如若需要获取可以使用 getOwnPropertySymbols只能获取 Symbol类型的属性名
console.log(Object.getOwnPropertySymbols(obj1)) //[ Symbol(Symbol.toStringTag) ]
for of 循环
// 数组遍历
const arr = [100, 200, 300]
for(val of arr){
console.log(val) //100 200 300
}
//Set遍历
const s = new Set(['foo','bar'])
for(val of s){
console.log(val) //foo bar
}
//Map遍历
const m = new Map()
m.set('foo',1)
m.set('bar',2)
for([k,v] of m){
console.log(k,v) //foo 1 bar 2
}
// 对象遍历 无法变量
const obj = {
a:1,
b:2
}
//TypeError: obj is not iterable
for(let val of obj){
console.log(val)
}
iterator 可迭代接口
const s = new Set([1,2,3])
const iterator = s[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 }
实现可迭代接口
- 伪代码
const obj = { // 实现可迭代接口 iterable [Symbol.iterator](){ return { // 实现迭代器接口 iterator next(){ // 实现了迭代结果接口 iterationResult return{ val:'aa', done:true } } } } }
实现for of 遍历对象
const obj = {
a: 1,
b: 2,
// 实现可迭代接口 iterable
[Symbol.iterator]() {
let index = 0
const keys = Object.keys(this)
return {
// 实现迭代器接口 iterator
next: () => {
// 实现了迭代结果接口 iterationResult
const key = keys[index]
const result = {
value: [this[key],key],
done: index >= keys.length
}
index++
return result
}
}
}
}
for (let [val,key] of obj) {
console.log('循环体',val,key)
// 循环体 1 a
// 循环体 2 b
}
设计模式—迭代器模式
- 传统实现
const todo = { life : [ '吃饭', '睡觉', '打豆豆' ], learn : [ '语文', '数学', '外语' ], // 传统遍历 each:function(callbacl){ for(const item of [...this.life,...this.learn]){ callbacl(item) } } } todo.each(val=>console.log(val)) //会遍历每一个属性
- iterator 实现
const todo = { life: ['吃饭', '睡觉', '打豆豆'], learn: ['语文', '数学', '外语'], [Symbol.iterator](){ const all = [...this.life,...this.learn] let index = 0 return { next(){ return { value : all[index], done : index++ >= all.length } } } } } for(let item of todo){ console.log(item) // 吃饭 睡觉 打豆豆 ... }
Generator 生成器函数
- 特点
- 自动返回 iterable
- 惰性执行 需调用next()
- 减少异步编程嵌套
基本语法
function * foo(){
console.log('100')
yield 100
console.log('200')
yield 200
console.log('300')
yield 300
}
const res = foo()
console.log(res) // Object [Generator] {}
console.log(res.next()) // 100 { value: 100, done: false }
console.log(res.next()) // 200 { value: 200, done: false }
console.log(res.next()) // 300 { value: 300, done: true }
Generator应用–发号器
function * careteIdMaker(){
let id = 1
while(true){
yield id++
}
}
const idMaker = careteIdMaker()
console.log(idMaker.next().value) // 1
console.log(idMaker.next().value) // 2
console.log(idMaker.next().value) // 3
Generator应用–实现iterator接口
const todo = {
life: ['吃饭', '睡觉', '打豆豆'],
learn: ['语文', '数学', '外语'],
[Symbol.iterator]:function * (){
const all = [...this.life,...this.learn]
for(const item of all){
yield item
}
}
}
for(let item of todo){
console.log(item) // 吃饭 睡觉 打豆豆 ...
}
ECMA 2016
- 数组 includes
const arr = [ 'foo', 1, NaN, false]
console.log(arr.includes('foo')) //true
console.log(arr.includes(1)) //true
console.log(arr.includes(NaN)) //true
console.log(arr.includes(false)) //true
- 指数运算符
console.log(Math.pow(2,10)) // 1024
console.log(2 ** 10) // 1024
ECMA 2017
- Object.values
const obj = {
a:1,
b:2
}
console.log(Object.values(obj)) // [ 1, 2 ]
- Object.entries
const obj = {
a:1,
b:2
}
console.log(Object.entries(obj)) // [ [ 'a', 1 ], [ 'b', 2 ] ]
通过for of 遍历对象
const obj = {
a:1,
b:2
}
for(const [ key, value ] of Object.entries(obj)){
console.log(key,value)
// a 1
// b 2
}
Object对象转Map对象
const obj = {
a:1,
b:2
}
console.log(new Map(Object.entries(obj))) // Map(2) { 'a' => 1, 'b' => 2 }
- Object.getOwnPropertyDescriptors (获取对象中属性的完整描述信息)
const p1 = {
firstName : 'Zhao',
lastName : 'Zhong',
get fullName(){
return `${ this.firstName }-${this.lastName}`
}
}
console.log(p1.fullName) // Zhao-Zhong
const p2 = Object.assign({},p1)
p2.firstName = 'zzy'
console.log(p2.fullName) // Zhao-Zhong
const p3 = Object.defineProperties({},Object.getOwnPropertyDescriptors(p1))
p3.firstName = 'wzz'
console.log(p3.fullName) // wzz-Zhong
- padStart、padEnd字符串填充方法
const obj = {
html : 5,
css : 16,
javascript : 128
}
for(const [name,count] of Object.entries(obj)){
console.log(name,count)
}
//html 5
//css 16
//javascript 128
for(const [name,count] of Object.entries(obj)){
console.log(`${name.padEnd(16,'-')}|${count.toString().padStart(3,'0')}`)
}
// html------------|005
// css-------------|016
// javascript------|128
- 允许函数参数中添加伪逗号
- Async/Await