Proxy代理对象
如果想要监视某个对象中的属性的变化可以使用es5中的Object.defineProperty,在es6中增加了Proxy,专门用来为对象设置访问代理器。
const person = {
name: 'qsn',
age: 20
};
const personProxy = new Proxy(person, {
get(target, property) {
return property in target ? target[property] : 'default';
},
set(target, property, value) {
if (property == 'age') {
if (!Number.isInteger(value)) {
throw new TypeError(`${value} is not an int`);
}
}
target[property] = value;
}
});
personProxy.age = 18;
console.log(personProxy);
console.log(personProxy.xxx);
Set的三个入参:代理目标对象,要写入的名称,要写入的属性值
Proxy对比defineProperty
defineProperty只能监视对象属性的读写,Proxy能够监视到更多对象操作,如delete操作
const person = {
name: 'qsn',
age: 20
};
const personProxy = new Proxy(person, {
deleteProperty(target, property) {
console.log('delete', property);
delete target[property];
}
});
delete personProxy.name;
console.log(personProxy);
Proxy更好的支持数组对象的监视,以住通过Object. defineProperty监视数组的操作最常见的就是重写数组的操作方法,也是vue.js中使用的方式,大体的思路是通过自定义的方法覆盖掉原型对象上的Push,shift等方法,以此来劫持这个方法调用的过程。
使用Proxy对象监视数组
const list = []
const listProxy = new Proxy(list, {
set(target, property, value) {
console.log("set", property, value);
target[property] = value;
return true; //表示添加成功
}
});
listProxy.push(100);
console.log(listProxy);
Proxy是以非侵入的方式监管了对象的读写,意思是已经定义好的对象,不需要对对象进行任何的操作,就可以监视到内部成员的读写 ,而Object.defineProperty必须要通过特定的方式单独去设定属性去监听不同的操作
Reflect 统一的对象操作API
Reflect属于一个静态类,不能通过new的方式建一个实例对象,只能通过这个静态类去调用静态方式。和js中的Math对象类似,Reflect内部封装了一系列对对象的底层操作(14个方法,其中一个废弃)。
Reflect成员方法就是Proxy处理对象的默认实现
const obj = {
name: "qsn",
age: 18
};
const objProxy = new Proxy(obj, {
get(target, property) {
console.log("watch logic");
return Reflect.get(target, property);
}
});
console.log(objProxy.name);
Reflect最大的作用就是统一提供一套用于操作对象的API
const obj = {
name: "qsn",
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
一种更优的异步编程解决文案,解决了传统异步编程中回调函数嵌套过深的问题.
具体可以查看我之前发布的文章
js异步编程二:promise链式调用,异常处理,静态方法,并行和执行时序
Class类
//原始方法
// function Person(name) {
// this.name = name;
// }
// Person.prototype.say = function() {
// console.log(`hi,my name is ${this.name}`);
// }
// new Person("qsn").say();
class Person {
constructor(name) {
this.name = name;
}
say() {
console.log(`hi,my name is ${this.name}`);
}
}
const p = new Person("qsn1");
p.say();
静态方法static
方法分为实例方法和静态方法,实例方法通过这个类型构造的实例对象去调用,而静态方法则是直接通过类型本身去调用,
以前实现静态方法直接在构造函数对象上挂载方法来实现,因为在js中函数也是对象,也可以增加一些方法成员。
Es6中新增添加静态成员的static着关键词
class Person {
constructor(name) {
this.name = name;
}
say() {
console.log(`hi,my name is ${this.name}`);
}
static create(name) {
return new Person(name);
}
}
const p = Person.create("qsn");
p.say();
类的继承extends
class Person {
constructor(name) {
this.name = name;
}
say() {
console.log(`hi,my name is ${this.name}`);
}
}
class Status extends Person {
constructor(name, number) {
super(name);
this.number = number;
}
hello() {
super.say();
console.log(`my school number is ${this.number}`);
}
}
const p = new Status("qsn", 12);
p.hello();
Super是访问父成员
Set数据结构
Set集合和数组非常类似,不过set内部的成员是不允许重复的
const s = new Set();
s.add(1).add(2).add(3).add(2);
console.log(s);
因为返回的值是这个对象的本身,所以可以链式的去调用,当添加了重复的值后会被忽略
const s = new Set();
s.add(1).add(2).add(3).add(2);
// s.forEach(i => console.log(i));
for (let i of s) {
console.log(i);
}
console.log(s.size);
可以用of去遍历set,size是长度
s.has(100);
s.delete(3);
s.clear();
Has方法是判断是否存在某个值
删除,返回的是true或false,clear清空所有数据
Set最常见的场景就是用来给数组去重,去重再转成数组
const arr = [1, 2, 3, 2, 2, 5];
const newarr = [...new Set(arr)];
console.log(newarr)
Map数据结构
和对象比较像,但是对象的键只能是字符串类型、map可以是任意类型.
普通的对象:
const obj = {};
obj[true] = 1;
obj[123] = 2;
obj[{ "a": 1 }] = 3;
console.log(Object.keys(obj));
都被传成了字符串,map可以是任意的类型
const obj = new Map();
const tom = {
"a": 1
};
obj.set(tom, 1);
obj.forEach((value, key) => console.log(key, value));
Symbol一种全新的原始数据类型
表示一个独一无二的值,避免对象属性名重复,传入字符串表示描述文本
console.log(
Symbol() === Symbol()
);
console.log(Symbol('foo'));
console.log(Symbol('bar'));
console.log(Symbol('baz'));
const obj = {};
obj[Symbol()] = '123';
obj[Symbol()] = '456';
console.log(obj);
const obj1 = {
[Symbol()]: 123
};
console.log(obj1);
也可以借助这个类型的特点模拟实现对象的私有成员
const name = Symbol();
const person = {
[name]: 'zce',
say() {
console.log(this[name]);
}
}
person.say();
因为没办法得到一样的symbol值,所以name只能在内部被访问到,
最主要的作用就是为对象添加独一无二的属性名。
const s1 = Symbol.for('foo');
const s2 = Symbol.for('foo');
console.log(s1 === s2);
Symbol.for传入的值是一样的,返回的symbol值也是一样的,这个方法内部维护了一个全局的注册表,为字符串和值提供了一一对应的关系,需要注意的是在这个方法内部维护的是字符串和symbol之间的对应关系,如果传的不是字符串会自动转换成字符串
const obj = {
[Symbol()]: 'symbol value',
foo: 'normal value'
};
console.log(Object.getOwnPropertySymbols(obj));
For…of循环
作为遍历所有数据结构的统一方式
const arr = [100, 200, 300, 400];
for (const item of arr) {
console.log(item);
if (item > 100) {
break;
}
}
const m = new Map();
m.set('foo', 123);
m.set('bar', '345');
for (const [key, value] of m) {
console.log(key, value);
}
可迭代接口
Es中能够表示有结构的数据类型越来越多,为了给各种各样的数据结构提供统一遍历方式,es2015提供了Iterable接口,实现Ierable接口就是for…of的前提
// 迭代器(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)
}
实现可迭代接口
const obj = {
store: ['foo', 'bar', 'baz'],
[Symbol.iterator]: function() {
let index = 0
const self = this
return {
next: function() {
const result = {
value: self.store[index],
done: index >= self.store.length
}
index++
return result
}
}
}
}
for (const item of obj) {
console.log('循环体', item)
}
迭代器模式
// 迭代器设计模式
// 场景:你我协同开发一个任务清单应用
// 我的代码 ===============================
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
}
}
}
}
}
// 你的代码 ===============================
// for (const item of todos.life) {
// console.log(item)
// }
// for (const item of todos.learn) {
// console.log(item)
// }
// for (const item of todos.work) {
// console.log(item)
// }
todos.each(function(item) {
console.log(item)
})
console.log('-------------------------------')
for (const item of todos) {
console.log(item)
}
当你知道当前的数据结构时可以这样去遍历,在todos里再增加一个数据时,遍历又要增加
对外提供统一的遍历接口
生成器Generator
避免在异步编程中回调嵌套过深,提供更好的异步编程解决方案
// Generator 函数
// 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
最大的特点是惰性执行,执行一次next执行到yield停一下。
生成器应用
// 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)
}
Es2016概述,增加了includes和pow
// ECMAScript 2016
// Array.prototype.includes -----------------------------------
// const arr = ['foo', 1, NaN, false]
// 找到返回元素下标
// console.log(arr.indexOf('foo'))
// 找不到返回 -1
// console.log(arr.indexOf('bar'))
// 无法找到数组中的 NaN
// console.log(arr.indexOf(NaN))
// 直接返回是否存在指定元素
console.log(arr.includes('foo'))
// 能够查找 NaN
console.log(arr.includes(NaN))
// 指数运算符 ---------------------------------------------------
console.log(Math.pow(2, 10))//2的10次方
Es2017概述
增加了values,entries,等方法
// ECMAScript 2017
const obj = {
foo: 'value1',
bar: 'value2'
}
// Object.values -----------------------------------------------------------
console.log(Object.values(obj))
// Object.entries ----------------------------------------------------------
console.log(Object.entries(obj))
for (const [key, value] of Object.entries(obj)) {
console.log(key, value)
}
console.log(new Map(Object.entries(obj)))
// Object.getOwnPropertyDescriptors ----------------------------------------
const p1 = {
firstName: 'Lei',
lastName: 'Wang',
get fullName() {
return this.firstName + ' ' + this.lastName
}
}
// console.log(p1.fullName)
// const p2 = Object.assign({}, p1)
// p2.firstName = 'zce'
// console.log(p2)
const descriptors = Object.getOwnPropertyDescriptors(p1)
// console.log(descriptors)
const p2 = Object.defineProperties({}, descriptors)
p2.firstName = 'zce'
console.log(p2.fullName)
// String.prototype.padStart / String.prototype.padEnd --------------------
const books = {
html: 5,
css: 16,
javascript: 128
}
// for (const [name, count] of Object.entries(books)) {
// console.log(name, count)
// }
for (const [name, count] of Object.entries(books)) {
console.log(`${name.padEnd(16, '-')}|${count.toString().padStart(3, '0')}`)
}
// 在函数参数中添加尾逗号 -----------------------------------------------------
function foo(
bar,
baz,
) {
}
const arr = [
100,
200,
300,
]
const arr = [
100,
200,
300,
400,
]
const arr = [
100,
200,
300
]
const arr = [
100,
200,
300,
400
]