JS重温基础

【重温基础】14.元编程

2019-01-07  本文已影响5人  pingan8787

本文是 重温基础 系列文章的第十四篇。
这是第一个基础系列的最后一篇,后面会开始复习一些中级的知识了,欢迎持续关注呀!
接下来会统一整理到我的【Cute-JavaScript】JavaScript基础系列中。

今日感受:独乐乐不如众乐乐。

系列目录:

本章节复习的是JS中的元编程,涉及的更多的是ES6的新特性。

1. 概述

元编程,其实我是这么理解的:让代码自动写代码,可以更改源码底层的功能
元,是指程序本身。
有理解不到位,还请指点,具体详细的介绍,可以查看维基百科 元编程
从ES6开始,JavaScrip添加了对ProxyReflect对象的支持,允许我们连接并定义基本语言操作的自定义行为(如属性查找,赋值,枚举和函数调用等),从而实现JavaScrip的元级别编程。

参考资料

名称 地址
Reflect MDN Reflect
Proxy MDN Proxy
元编程 MDN 元编程

本文主要从Proxy介绍,还会有几个案例,实际看下怎么使用。

2. Proxy介绍

proxy 用于修改某些操作的默认行为,可以理解为一种拦截外界对目标对象访问的一种机制,从而对外界的访问进行过滤和修改,即代理某些操作,也称“代理器”。

2.1 基础使用

基本语法:

let p = new Proxy(target, handler);

proxy实例化需要传入两个参数,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。

let p = new Proxy({}, {
    get: function (target, handler){
        return 'leo';
    }
})
p.name; // leo
p.age;  // leo
p.abcd; // leo

上述a实例中,在第二个参数中定义了get方法,来拦截外界访问,并且get方法接收两个参数,分别是目标对象所要访问的属性,所以不管外部访问对象中任何属性都会执行get方法返回leo

注意

let target = {};
let handler = {};
let p = new Proxy(target, handler);
p.a = 'leo'; 
target.a;  // 'leo'

同个拦截器函数,设置多个拦截操作

let p = new Proxy(function(a, b){
    return a + b;
},{
    get:function(){
        return 'get方法';
    },
    apply:function(){
        return 'apply方法';
    }
})

这里还有一个简单的案例:

let handler = {
    get : function (target, name){
        return name in target ? target[name] : 16;
    }
}

let p = new Proxy ({}, handler);
p.a = 1;
console.log(p.a , p.b);
// 1   16

这里因为 p.a = 1 定义了p中的a属性,值为1,而没有定义b属性,所以p.a会得到1,而p.b会得到undefined从而使用name in target ? target[name] : 16;返回的默认值16

Proxy支持的13种拦截操作
13种拦截操作的详细介绍:打开阮一峰老师的链接

2.2 取消Proxy实例

使用Proxy.revocable方法取消Proxy实例。

let a = {};
let b = {
    get: function(target, name) {
        return "[[" + name + "]]";
    }
};
let revoke = Proxy.revocable(a, b);
let proxy = revoke.proxy;

proxy.age;           // "[[age]]"
revoke.revoke();
proxy.age;           // Uncaught TypeError: Cannot perform 'get' on a proxy that has been revoked

proxy.age = 10;      // Uncaught TypeError: Cannot perform 'set' on a proxy that has been revoked
delete proxy.age;    // Uncaught TypeError: Cannot perform 'deleteProperty' on a proxy that has been revoked
typeof proxy;        // "object"

2.3 实现 Web服务的客户端

const service = createWebService('http://le.com/data');
service.employees().than(json =>{
    const employees = JSON.parse(json);
})

function createWebService(url){
    return new Proxy({}, {
        get(target, propKey, receiver{
            return () => httpGet(url+'/'+propKey);
        })
    })
}

3. Proxy实践

3.1 数据拦截验证

通过Proxy代理对象的setget方法来进行拦截数据,像Vue就是用数据拦截来实现数据绑定。

let handler = {
    // 拦截并处理get方法
    // 可以理解为设置get方法返回的默认值
    get : function (target, key){
        return key in target ? target[key] : 30;
    },
    
    // 拦截并处理set方法
    // 可以理解为设置set方法的默认行为
    set : function (target, key, value){
        if(key === "age"){
            if (!Number.isInteger(value)){
                throw new TypeError("age不是一个整数!");
            }
            if (value > 200){
                throw new TypeError("age不能大于200!");
            }
        }
        // 保存默认行为
        target[key] = value;
    }
}

let p = new Proxy({}, handler);
p.a = 10;         // p.a   => 10
p.b = undefined;  // p.b   => undefined
p.c;              // 默认值 30
p.age = 100;      // p.age => 100
p.age = 300;      // Uncaught TypeError: age不能大于200!
p.age = "leo";    // Uncaught TypeError: age不是一个整数!

3.2 函数节流

通过拦截handler.apply()方法的调用,实现函数只能在1秒之后才能再次被调用,经常可以用在防止重复事件的触发。

let p = (fun, time) => {
    // 获取最后点击时间
    let last = Date.now() - time;
    return new Proxy (fun, {
        apply(target, context, args){
            if(Date.now() - last >= time){
                fun.bind(target)(args);
                // 重复设置当前时间
                last = Date.now();
            }
        }
    })
}

let p1 = () => {
    console.log("点击触发");
}
let time = 1000; // 设置时间
let proxyObj = p(p1, time);
// 监听滚动事件
document.addEventListener('scroll', proxyObj);

3.3 实现单例模式

通过拦截construct方法,让不同实例指向相同的constructer,实现单例模式。

let p = function(fun){
    let instance;
    let handler = {
        // 拦截construct方法
        construct: function(targer, args){
            if(!instance){
                instance = new fun();
            }
            return instance;
        }
    }
    return new Proxy(fun, handler);
}

// 创建一个construct案例
function Cons (){
    this.value = 0;
}

// 创建实例
let p1 = new Cons();
let p2 = new Cons();

// 操作
p1.value = 100; 
// p1.value => 100 , p2.value => 0
// 因为不是相同实例

// 通过Proxy实现单例
let singleton = p(Cons);
let p3 = new singleton();
let p4 = new singleton();
p3.value = 130; 
// p1.value => 130 , p2.value => 130
// 现在是相同实例

参考资料

1. MDN 元编程
2. ES6中的元编程-Proxy & Reflect
本部分内容到这结束

Author 王平安
E-mail pingan8787@qq.com
博 客 www.pingan8787.com
微 信 pingan8787
每日文章推荐 https://github.com/pingan8787/Leo_Reading/issues
JS小册 js.pingan8787.com
微信公众号【前端自习课】
上一篇下一篇

猜你喜欢

热点阅读