Set
set是es6新增的一种数据结构,能够自动去重,可以使用在数组去重里
let set = new Set([1,2,3,4,5, 5])
let arr = [...set] // [1,2,3,4,5]
set可以循环访问数据
let set = new Set([1,2,3,4,5, 5])
set.forEach((item) => {
console.log(item)
})
set的add, has
let set = new Set([1,2,3,4,5, 5])
set.add(7) // 添加数据
set.has(7) // true 判断是元素是否存在
set实现数组的并/交/差
let arr1 = [1,2,3]
let arr2 = [2,3,4]
let res = []
// -----并集-------
res = [...new Set([...arr1, ...arr2])] // [1,2,3,4]
// -----交集-------
let set1 = new Set(arr1)
res = arr2.filter((item) => {
return set1.has(item)
})
// -----差集-------
let set2 = new Set(arr2)
res = arr1.filter((item) => {
return !set2.has(item)
})
Map
Map是es6新增加的数据结构,类似对象,但是Map可以设置任意的key,es5里面的对象,在设置key的时候,如果把一个对象作为另一个对象的key,这个对象key会自动调用toString()变成一个字符串,就会有key值覆盖的问题。
Map和Object的区别
es6对象设置为key,值相同的对象会覆盖
let obj = {}
let obj1 = {}
let obj2 = {}
obj[obj1] = 'qq'
obj[obj2] = 'ss'
console.log(obj) // { '[object Object]': 'ss' }
let obj = new Map()
let obj1 = {}
let obj2 = {}
obj.set(obj1, 'qq')
obj.set(obj2, 'ss')
console.log(obj) // Map(2) { {} => 'qq', {} => 'ss' }
Map的方法
set设置属性get获取属性has判断属性是否存在delete删除属性keys遍历keyvalues遍历值entries遍历键值对let arr = [ ['name','章三'], ['age', 12], ['sex', '男'] ] let map = new Map(arr) map.get('name') map.set('like', '看书') map.has('age') map.delete('like') //----------------------- // keys() 返回一个引用的迭代器对象 // values() 返回一个新的迭代器对象 // entries() 返回一个新的迭代器对象 let keyObj = map.keys() // [Map Iterator] { 'name', 'age', 'sex' } let valObj = map.values() // [Map Iterator] { '章三', 12, '男' } let keyValObj = map.entries()
Map的参数
Map 可以接受数组作为参数,但是数组里面的元素,必须是以键值对数组形式存在
let items = [
['name', 'zhangsan'],
[{'name': 'lisi'}, 'wangwu']
]
let map = new Map(items)
console.log(map.get('name')) // zhangsan
console.log(map.get({'name': 'lisi'})) // undefined
上面map.get({'name': 'lisi'})是undefined应为在使用map.get的时候,已经重新定义了一个对象这个对象和原来数组里面的对象,已经不是一个地址了
let obj = {'name': 'lisi'}
let items = [
['name', 'zhangsan'],
[obj, 'wangwu']
]
let map = new Map(items)
console.log(map.get('name')) // zhangsan
console.log(map.get(obj)) // wangwu
Map接受数组的本质
let obj = {'name': 'lisi'}
let items = [
['name', 'zhangsan'],
[obj, 'wangwu']
]
let map = new Map()
items.forEach(([key,val]) => {
map.set(key, val)
})
Map 的参数,只要符合参数的数据结构有Iterator接口,并且数组的每一个元素都是双元素的
let set = new Set([
['name', '章三'],
['age', 12]
])
let map = new Map(set)
console.log(map.get('name')) // 章三
console.log(map.get('age')) // 12
Interator和for...of
所有能使用for...of的数据结构,都具有Interator迭代器。Interator是为了能够为不同的数据结构提供统一的访问接口。
- Interator迭代器是一个函数
- 函数会返回指针对象
- 对象含有一个next属性
- next也是一个函数
- next函数返回一个对象{value: 'xx', done: true/false}
- done为false迭代继续
- done为true的时候迭代结束
function makeInterator(arr) {
let index = 0
return {
next: function() {
if (index < arr.length) {
return {
value: arr[index++],
done: false
}
} else {
return {
value: undefined,
done: true
}
}
}
}
}
let it = makeInterator(['a', 'b', 'c'])
console.log(it.next()) // { value: 'a', done: false }
console.log(it.next()) // { value: 'b', done: false }
console.log(it.next()) // { value: 'c', done: false }
console.log(it.next()) // { value: undefined, done: true }
Object对象的属性上没有迭代器,自已可以改造一个,让对象使用for...of
let obj = {
data: ['hello', 'world']
}
obj[Symbol.iterator] = function() {
let data = this.data;
let index = 0;
return {
next: function() {
if (index < data.length) {
return {
value: data[index++],
done: false
}
} else {
return {
value: undefined,
done: true
}
}
}
}
}
for(let item of obj) {
console.log(item)
}
console.log(...obj) // hello world
let obj = {
data: ['hello', 'world'],
data1: ['1', '2']
}
obj[Symbol.iterator] = function() {
let keys = Object.keys(this)
let index = 0;
let _this = this
return {
next: function() {
if (index < keys.length) {
let key = keys[index]
index++
return {
value: _this[key],
done: false
}
} else {
return {
value: undefined,
done: true
}
}
}
}
}
console.log(...obj) // [ 'hello', 'world' ] [ '1', '2' ]
for in和for of的区别
- for in 用来遍历对象的key,数组的key就是下标索引,如果给数组增加一个.a的属性,for in也是会遍历出来的,这样就不是期望的了,所以for in 不适合遍历数组
- for of上面已经详细的讲过了,他很适合遍历数组
箭头函数和this
箭头函数和普通函数的区别
-
箭头函数没有arguments,可以通过rest来获取参数
function fun1() { console.log(arguments) } const fun = (...arg) => { console.log(...arg) } fun1(1,2,3) fun(4,5,6) -
箭头函数自己没有this,内部使用的this是从作用域链的上一层继承来的,箭头函数的this一直指向函数定义的地方
-
var name = '里斯' let obj = { name: '张三', fun: function() { return { fn1: () => { console.log(this.name) } } }, fn2: () => { console.log(this.name) } } obj.fun().fn1() // 张三 obj.fn2() // 里斯
- 箭头函数不能使用call,apply,bind来改变this指向
```js
let obj = {
name: '张三',
}
var name = '顽固'
function fn1() {
console.log(this.name)
}
const fn2 = () => {
console.log(this.name)
}
fn1.call(obj) // 张三
fn2.call(obj) // 顽固
- 箭头函数不能作为构造函数
function fn1() {
}
const fn2 = () => {
}
const fn1Obj = new fn1()
const fn2Obj = new fn2() // n2 is not a constructor
console.log(fn1Obj)
-
箭头函数没有原型
const fn2 = () => {} console.log(fn2.prototype) // undefined -
箭头函数不能使用yeild,不能作为Generator函数
一个小题
var name = 'window'
var obj = {
name: 'obj',
methods: () => {
console.log(this.name)
},
fn: function (cb) {
cb()
}
}
obj.fn1 = function () {
obj.fn(() => { console.log(this.name) })
}
var fn1 = obj.fn1 // 普通函数
/**
* obj.methods() 是一个箭头函数,他要去作用域链的上一层查找this,obj对象是一个对象,作用域只有全局和函数2中,对象自己并没有作用域,所以obj.methods()的this指向全局
*/
obj.methods() // window
/**
* obj.fn(() => { console.log(this.name) }) 中obj.fn接受一个函数作为参数,这个参数函数其实是在全局里面定义的,不是在obj.fn()里面定义的,箭头函数的this永远指向函数定义的地方,所以this指向window
*/
obj.fn(() => { console.log(this.name) }) // window
/**
* var fn1 = obj.fn1 这个代码已经把obj上的fn1赋值给了fn1,fn1是一个在全局中的函数,this已经指向window,已经和obj没有关系了
*/
fn1() // window
/**
* 这里调用obj.fn1(), fn1函数是普通函数,所以fn1的this指向obj,在obj.fn1()里面调用箭头函数,箭头函数的上一级自然是obj.fn1(),所以this自然是obj
*/
obj.fn1() // obj
promise
promise有3种状态,状态一旦改变就不能更改了,promise的返回值也是一个promise
-
pedding初始状态
-
fulfilled成功
-
rejected失败
new Promise((resolve, reject) => {
resolve(1)
reject(2)
}).then((res) => {
console.log(res)
}).catch((err) => {
console.log(err)
})
then()接收成功值,catch()接收失败值
promise的常用方法
- Promise.resolve(1) 返回一个状态为fulfilled的promise对象
- Promise.reject() 返回一个状态为rejected的promise对象
- Promise.all() 所有的promise都成功,才能返回结果
Promise.all([
new Promise((resolve, reject) => {
resolve(1)
}),
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2)
}, 1000)
})
]).then((res) => {
console.log(res) // [1,2]
})
Promise.all([
new Promise((resolve, reject) => {
resolve(1)
}),
new Promise((resolve, reject) => {
setTimeout(() => {
reject(2)
}, 1000)
})
]).then((res) => {
console.log(res)
}).catch((err) => {
console.log(err) // 2
})
- Promise.any()返回最先成功的那个promise结果,所有的都失败了就失败
Promise.any([
new Promise((resolve, reject) => {
setTimeout(() => {
reject(2)
}, 1000)
}),
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('')
}, 3000)
}),
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('1-1')
}, 2000)
}),
]).then((res) => {
console.log('res',res)
}).catch((err) => {
console.log('err',err)
})
- Promise.race()永远输出提前执行完毕的结果,不管成功与否,结果不是数组
Promise.race([
new Promise((resolve, reject) => {
setTimeout(() => {
reject(2)
}, 1000)
}),
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('')
}, 3000)
}),
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('1-1')
}, 2000)
}),
]).then((res) => {
console.log('res',res)
}).catch((err) => {
console.log('err',err)
})
// err 2
- Promise.allsettled返回所有promise的状态,结果是数组
Promise.allSettled([
new Promise((resolve, reject) => {
setTimeout(() => {
reject(2)
}, 1000)
}),
new Promise((resolve, reject) => {
setTimeout(() => {
reject('3')
}, 3000)
}),
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('1-1')
}, 2000)
}),
]).then((res) => {
console.log('res',res)
}).catch((err) => {
console.log('err',err)
})
/**
res [
{ status: 'rejected', reason: 2 },
{ status: 'rejected', reason: '3' },
{ status: 'fulfilled', value: '1-1' }
]
*/
async/await
能够更简单的描写promise的异步行为 async的返回值是一个promise。
- async的函数内部会返回一个promise,如果不是会被默认转化
async function fn() {
return '1'
}
console.log(fn()) // Promise { '1' }
- await接收的promise状态改变之后的值,如果改变之后不是promise,await会把改值变成正常处理的promise(resolve处理的值)
async function fn() {
await 1
}
console.log(fn())
- await和面的promise的状态是reject的话,await后面的代码不会执行,async的函数会返回状态为rejected的promise
- async函数内部如果存在await,await表达式会暂停整个async函数的执行,等当前位置promise状态改变后才能恢复
小题练习
function fn () {
return new Promise((resolve) => {
console.log('Promise1')
fn1()
setTimeout(() => {
console.log('Promise2')
resolve()
console.log('Promise3')
}, 0);
})
}
async function fn1() {
var p = Promise.resolve().then(() => {
console.log('Promise6')
})
await p.then(() => {
console.log('Promise7')
})
console.log('end')
}
console.log('script')
setTimeout(() => {
console.log('setTimeout')
}, 0)
fn().then(() => {
console.log('Promise4')
})
// 结果
// script
// Promise1
// Promise6
// Promise7
// end
// setTimeout
// Promise2
// Promise3
// Promise4
代码执行的时候顺序
console.log('script')得到 scriptsetTimeout(() => { console.log('setTimeout') }, 0)放入宏任务队列 -fn()调用函数new Promise((resolve) => { console.log('Promise1') fn1() setTimeout(() => { console.log('Promise2') resolve() console.log('Promise3') }, 0); })是在函数调用的内部代码,要同步执行console.log('Promise1')fn1()调用fn1var p = Promise.resolve().then(() => { console.log('Promise6') }) await p.then(() => { console.log('Promise7') }) console.log('end')p = Promise.resolve()是一个微任务,resolve()会触发.then(),所以console.log('Promise6')放入微任务await p.then(() => { console.log('Promise7') })``await的方法必须执行完,后面的代码才能执行,所以这是时候** script, Promise1, Promise6,Promise7,end**都打印出来- 微任务执行完毕了,要去执行宏任务了,打印setTimeout, Promise2
resolve()触发fn().then(),fn().then()被放入微任务- 继续打印
Promise3 - 最后打印
Promise4
async function fn () {
setTimeout(function () {
console.log(1)
}, 0)
Promise.resolve().then(() => console.log(4))
await setTimeout(function () {
console.log(5)
}, 0)
await Promise.resolve().then(() => console.log(6))
Promise.resolve().then(() => console.log(7))
console.log(3)
}
fn()
/**
* 4
* 6
* 3
* 7
* 1
* 5
*/
proxy
proxy可以为一个对象设置代理,可以实现拦截是自定义方法(赋值,查找,枚举,函数调用...)
proxy语法
- 对象代理
const data = {}
const handler = {
get(target, property, receiver) {
}
}
const p = new Proxy(data, handler)
- 可以撤销的对象代理
const data = {}
const handler = {
get(target, property, revicer) {
}
}
const p = Proxy.revocable(data, handler)
proxy的get会拦截目标对象的以下操作
- obj.xxx获取对象属性
- Object.cerate(p).xxx用
Object.cerate复制proxy代理的结果,并访问属性 - Reflect.get() proxy的set会拦截目标对象的以下操作
- obj.xxx = 'yyy'
- Object.cerate(p).xxx = 'yyy'
- Reflect.set()
proxy使用案例
var data = [
{ name: 'Firefox' , type: 'browser' },
{ name: 'SeaMonkey' , type: 'browser' },
{ name: 'Thunderbird', type: 'mailer' }
]
// 需要实现的功能
// 1. 通过索引返回对应数据 proxy[0]
// 2. 通过number属性返回数组长度 proxy.number
// 3. 通过name获取对应的数据 proxy['Firefox']
// 4. 通过type返回对应的数据 proxy['browser']
// 5. 通过types返回data中的type products.types
let data = [
{ name: 'Firefox' , type: 'browser' },
{ name: 'SeaMonkey' , type: 'browser' },
{ name: 'Thunderbird', type: 'mailer' }
]
let p = new Proxy(data, {
get(target, prop) {
if(prop in target) {
return target[prop]
}
if(prop === 'number') {
return target.length
}
let res
let types = {}
for(let item of target) {
if (item.name === prop) {
res = item
}
if (types[item.type]) {
types[item.type].push(item)
} else {
types[item.type] = [item]
}
}
if (res) return res
if(prop in types) {
return types[prop]
}
if (prop === 'types') {
return Object.keys(types)
}
}
})
console.log(p[0])
console.log(p.number)
console.log(p['Firefox'])
console.log(p['browser'])
console.log(p['types'])
class
class是构造函数的语法糖,需要使用extends来实现继承
class的特点
- class的数据类型是一个函数
class A {}
console.log(typeof A) // function
- class原型的constructor指向class
class A {}
console.log(A.prototype.constructor === A) // true
- new声明的实例的constructor指向当前的class
class A {}
let a = new A;
console.log(a.constructor === A) // true
- class内部的方法都是在类的原型上
class A {
fn() {
console.log(1)
}
fn1() {}
}
let a = new A();
console.log(a)
- 通过类创建对象的本质都是在调用constructor,如果没有就在使用时会默认添加
class A {
}
class A {
constructor() {
}
}
// 这2种写法完全是等价的
- class不能直接调用,需要使用new关键字
class A {
}
A()
- class内部使用严格模式,内部使用this的时候,指向的就是class的实例
class Logger {
printName(name = 'world') {
this.print(`Hello ${name}`);
}
print(text) {
console.log(text);
}
}
const logger = new Logger();
const { printName } = logger;
printName()
this的指向被改变了,但是是严格模式,所以this为undefined,如果想正确的访问this,可以使用箭头函数实现
class的用法
- class有getter和setter
class A {
get name() {
return 'name'
}
set name(value) {
console.log('set', value)
}
}
const a = new A()
a.name = 1 // 'set' 1
console.log(a.name) // 'name'
- 使用static关键字,声明静态方法 静态方法里面的this,指向类,而不是实例
- 定义实例的属性
class A {
a = 1
b = 'SUCCESS'
fn() {}
}
const a = new A()
console.log(a)
- 子类中的constructor必须使用super,否则报错
class A {
a = 1
b = 'SUCCESS'
fn() {}
}
class B extends A {
constructor() {
// super()
}
}
const b = new B()
console.log(b)
super关键字,会触发父类的constructor,并且传递参数
- 子类继承父类的时候,属性都是在实例上的,但是方法都会放在原型里面
class A {
a = 1
b = 'SUCCESS'
fn() {}
}
class B extends A {
constructor() {
super()
}
}
const b = new B()
console.log(b)