Proxy和Reflect(反射)
方法一模一样
Coca-cola供货商(压缩汽水)->包装(Proxy),兑水(读get),吐唾沫(has),偷喝(deleteProperty),添加各种口味(操作set)->出货
{
let obj={
time:'2017-03-11',
name:'net',
_r:123
};
let monitor=new Proxy(obj,{
// 拦截对象属性的读取
get(target,key){
return target[key].replace('2017','2018')
},
// 拦截对象设置属性
set(target,key,value){
if(key==='name'){
return target[key]=value;
}else{
return target[key];
}
},
// 拦截key in object操作
has(target,key){
if(key==='name'){
return target[key]
}else{
return false;
}
},
// 拦截delete
deleteProperty(target,key){
if(key.indexOf('_')>-1){
delete target[key];
return true;
}else{
return target[key]
}
},
// 拦截Object.keys,Object.getOwnPropertySymbols,Object.getOwnPropertyNames
ownKeys(target){
return Object.keys(target).filter(item=>item!='time')
}
});
console.log('get',monitor.time);
monitor.time='2018';
monitor.name='mukewang';
console.log('set',monitor.time,monitor);
console.log('has','name' in monitor,'time' in monitor);
// delete monitor.time;
// console.log('delete',monitor);
//
// delete monitor._r;
// console.log('delete',monitor);
console.log('ownKeys',Object.keys(monitor));
}
Reflect
{
let obj={
time:'2017-03-11',
name:'net',
_r:123
};
console.log('Reflect get',Reflect.get(obj,'time'));
Reflect.set(obj,'name','mukewang');
console.log(obj);
console.log('has',Reflect.has(obj,'name'));
}
Proxy和Reflect使用场景
数据校验
传统方式:在赋值时进行判断
把业务逻辑和对象本身隔离开
{
function validator(target,validator){
return new Proxy(target,{
_validator:validator,
set(target,key,value,proxy){
// 判断是否有KEY
if(target.hasOwnProperty(key)){
let va = this._validator[key];
if(!!va(value)){
return Reflect.set(target,key,value,proxy)
}else{
throw Error(`不能设置${key}到${value}`)
}
}else{
throw Error(`${key} 不存在`)
}
}
})
}
// 过滤选项
const personValidators={
name(val){
return typeof val === 'string'
},
age(val){
return typeof val === 'number' && val > 18
}
}
// 构造函数
class Person{
constructor(name,age){
this.name = name ;
this.age = age;
return validator(this,personValidators)
}
}
// 修改数据
const person = new Person('wangchunlong',30);
console.info(person);
person.name="wangwangwang";
console.info(person);
}
类和对象
- 基本语法
- 类的继承
- 静态方法
- 静态属性
- getter
- setter
基本语法
{
// 基本定义和生成实例
class Parent{
constructor(name='mukewang'){
this.name=name;
}
}
let v_parent=new Parent('v');
console.log('构造函数和实例',v_parent);
}
类的继承
{
// 继承
class Parent{
constructor(name='mukewang'){
this.name=name;
}
}
class Child extends Parent{
}
console.log('继承',new Child());
}
子类继承的覆盖
{
// 继承传递参数
class Parent{
constructor(name='mukewang'){
this.name=name;
}
}
class Child extends Parent{
constructor(name='child'){
super(name);
this.type='child';
}
}
console.log('继承传递参数',new Child('hello'));
}
getter和setter(属性,不是方法)
{
// getter,setter
class Parent{
constructor(name='mukewang'){
this.name=name;
}
get longName(){
return 'mk'+this.name
}
set longName(value){
this.name=value;
}
}
let v=new Parent();
console.log('getter',v.longName);
v.longName='hello';
console.log('setter',v.longName);
}
静态方法
{
// 静态方法
class Parent{
constructor(name='mukewang'){
this.name=name;
}
static tell(){
console.log('tell');
}
}
Parent.tell();
}
静态属性
{
// 静态属性
class Parent{
constructor(name='mukewang'){
this.name=name;
}
}
Parent.type='test';
console.log('静态属性',Parent.type);
}
Promise(异步操作)
实现异步的两种方式(ES5):回调,事件触发
“同步模式”就是上一段的模式,后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的;”异步模式”则完全不同,每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。
ES5回调异步
{
// 基本定义
let ajax=function(callback){
console.log('执行');
setTimeout(function () {
callback&&callback.call()
}, 1000);
};
ajax(function(){
console.log('timeout1');
})
}
很难阅读,代码复杂,可维护性差
Promise
{
let ajax=function(){
console.log('执行2');
return new Promise(function(resolve,reject){
// 具有了一个then的方法
// resolve 执行下一步的操作
// reject 中断当前的操作
setTimeout(function () {
resolve()
}, 1000);
})
};
ajax().then(function(){
console.log('promise','timeout2');
})
}
{
let ajax=function(){
console.log('执行3');
return new Promise(function(resolve,reject){
setTimeout(function () {
resolve()
}, 1000);
})
};
ajax()
.then(function(){
return new Promise(function(resolve,reject){
setTimeout(function () {
resolve()
}, 2000);
});
})
.then(function(){
console.log('timeout3');
})
}
Promise捕获异常
{
let ajax=function(num){
console.log('执行4');
return new Promise(function(resolve,reject){
if(num>5){
resolve()
}else{
throw new Error('出错了')
}
})
}
ajax(6).then(function(){
console.log('log',6);
}).catch(function(err){
console.log('catch',err);
});
ajax(3).then(function(){
console.log('log',3);
}).catch(function(err){
console.log('catch',err);
});
}
Promise实用场景
今日头条首页推荐流(图片全部加载完,才呈现在页面上,提高用户体验)
{
// 加载图片
function loadImg(src){
return new Promise((resolve,reject)=>{
let img = document.createElement('img');
img.src = src;
img.onload = function(){
resolve(img);
}
img.onerror=function(err){
reject(err);
}
})
}
// 添加图片
function showImgs(imgs){
imgs.forEach(function(img){
document.body.appendChild(img);
})
}
// Promise.all 把多个Promise实例当作一个Promise实例
Promise.all([
loadImg('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1498142965451&di=846cd76e25cb0d159b91c57ab615290f&imgtype=jpg&src=http%3A%2F%2Fimg3.imgtn.bdimg.com%2Fit%2Fu%3D1823136043%2C1469760399%26fm%3D214%26gp%3D0.jpg'),
loadImg('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1498142965451&di=846cd76e25cb0d159b91c57ab615290f&imgtype=jpg&src=http%3A%2F%2Fimg3.imgtn.bdimg.com%2Fit%2Fu%3D1823136043%2C1469760399%26fm%3D214%26gp%3D0.jpg'),
loadImg('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1498142965451&di=846cd76e25cb0d159b91c57ab615290f&imgtype=jpg&src=http%3A%2F%2Fimg3.imgtn.bdimg.com%2Fit%2Fu%3D1823136043%2C1469760399%26fm%3D214%26gp%3D0.jpg')
]).then(showImgs)
}
* 场景:选取最先加载完的图片*
{
// 加载图片
function loadImg(src){
return new Promise((resolve,reject)=>{
let img = document.createElement('img');
img.src = src;
img.onload = function(){
resolve(img);
}
img.onerror=function(err){
reject(err);
}
})
}
// 添加图片
function showImgs(img){
document.body.appendChild(img);
}
// Promise.race 有一个状态改变,race的状态也会跟着改变,其他不再响应
Promise.race([
loadImg('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1498142965451&di=846cd76e25cb0d159b91c57ab615290f&imgtype=jpg&src=http%3A%2F%2Fimg3.imgtn.bdimg.com%2Fit%2Fu%3D1823136043%2C1469760399%26fm%3D214%26gp%3D0.jpg'),
loadImg('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1498142965451&di=846cd76e25cb0d159b91c57ab615290f&imgtype=jpg&src=http%3A%2F%2Fimg3.imgtn.bdimg.com%2Fit%2Fu%3D1823136043%2C1469760399%26fm%3D214%26gp%3D0.jpg'),
loadImg('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1498142965451&di=846cd76e25cb0d159b91c57ab615290f&imgtype=jpg&src=http%3A%2F%2Fimg3.imgtn.bdimg.com%2Fit%2Fu%3D1823136043%2C1469760399%26fm%3D214%26gp%3D0.jpg')
]).then(showImgs)
}
Lterator
用法:不同数据结构的集合如何统一读取
for of循环的过程就是不断调用Lterator接口的过程
{
let arr=['hello','world'];
let map=arr[Symbol.iterator]();
console.log(map.next());
console.log(map.next());
console.log(map.next());
}
自定义Lterator
{
let obj={
start:[1,3,2],
end:[7,9,8],
[Symbol.iterator](){
let self=this;
let index=0;
let arr=self.start.concat(self.end);
let len=arr.length;
return {
next(){
if(index<len){
return {
value:arr[index++],
done:false
}
}else{
return {
value:arr[index++],
done:true
}
}
}
}
}
}
for(let key of obj){
console.log(key);
}
}
for of 遍历(数组的时候讲过哦)
{
let arr=['hello','world'];
for(let value of arr){
console.log('value',value);
}
}
Genertor(异步编程,高级)
基本定义
返回的也是Lterator对象
yield 停车
next 开车
{
// genertaor基本定义
let tell=function* (){
yield 'a';
yield 'b';
return 'c'
};
let k=tell();
console.log(k.next());
console.log(k.next());
console.log(k.next());
console.log(k.next());
}
Genertor和Lterator的关系(Genertor新应用)
Genertor是一个遍历器生成函数
{
let obj={};
obj[Symbol.iterator]=function* (){
yield 1;
yield 2;
yield 3;
}
for(let value of obj){
console.log('value',value);
}
}
状态机(在几个状态中循环)
{
let state=function* (){
while(1){
yield 'A';
yield 'B';
yield 'C';
}
}
let status=state();
console.log(status.next());
console.log(status.next());
console.log(status.next());
console.log(status.next());
console.log(status.next());
}
async语法糖(第三种写法)
{
let state=async function (){
while(1){
await 'A';
await 'B';
await 'C';
}
}
let status=state();
console.log(status.next());
console.log(status.next());
console.log(status.next());
console.log(status.next());
console.log(status.next());
}
实例
抽奖
尽量少把变量放在全局上,因为影响性能
一般做法:把次数变量放在全局上。但是,次数的安全性
{
let draw = function(count){
// 具体抽奖逻辑
console.info(`剩余${count}次`)
}
let residue = function* (count){
while(count>0){
count --;
yield draw(count);
}
}
let star = residue(5);
let btn = document.createElement('button');
btn.id = 'start';
btn.textContent='抽奖';
document.body.appendChild(btn);
document.getElementById('start').addEventListener('click',function(){
star.next();
},false)
}
长轮询
一个数据状态定期变化,我们需要定时去取状态(长轮询,web Socket)
长轮询:客户端不停的向服务器发送请求以获取最新的数据信息。这里的“不停”其实是有停止的,只是我们人眼无法分辨是否停止,它只是一种快速的停下然后又立即开始连接而已。
// 长轮询
{
// 轮询接口
let ajax = function*(){
yield new Promise(function(resolve,reject){
// 模拟请求的耗时
setTimeout(function(){
resolve({code:10})
},200);
})
}
// 轮询的过程
let pull = function(){
let genertaor = ajax();
let step = genertaor.next();
step.value.then(function(d){
if(d.code!=0){
setTimeout(function(){
console.log('wait');
pull()
},1000);
}else{
console.info(d);
}
})
}
pull();
}
Decorators(修饰器)
- 是一个函数,可以用来修改类的行为
- 需要插件babel-plugin-transform-decorators-legacy
- 第三方库修饰器的js库:core-decorators
只读修饰器的添加
{
let readonly=function(target,name,descriptor){
descriptor.writable=false;
return descriptor
};
class Test{
@readonly
time(){
return '2017-03-11'
}
}
let test=new Test();
// test.time=function(){
// console.log('reset time');
// };
console.log(test.time());
}
写在类的上面
{
let typename=function(target,name,descriptor){
target.myname='hello';
}
@typename
class Test{
}
console.log('类修饰符',Test.myname);
// 第三方库修饰器的js库:core-decorators; npm install core-decorators
}
埋点,日志统计
优点:代码可复用性,业务代码和埋点代码分离
{
let log=(type)=>{
return function(target,name,descriptor){
let src_method = descriptor.value;
descriptor.value = (...arg) =>{
src_method.apply(target,arg);
// 埋点函数 new 后执行
console.log('log ${type}');
}
}
}
class AD{
@log('show')
show(){
console.info('ad is show')
}
@log('click')
click(){
console.log('ad is click');
}
let ad = new AD();
ad.show();
ad.click();
}
}
模块化
export 导出
export let A=123;
export function test(){
console.log('test');
}
export class Hello{
test(){
console.log('class');
}
}
第二种写法:
let A=123;
let test=function(){
console.log('test');
}
class Hello{
test(){
console.log('class');
}
}
export default {
A,
test,
Hello
}
import 导入
入口文件
import {a,test,Hello} from './模块文件名'
升级
import * as mon from './模块文件名'
部分内容摘自阮一峰老师的博客