ES6基础知识八:你是怎么理解ES6中Proxy的?使用场景?

news2024/12/29 3:49:09

在这里插入图片描述
一、介绍

定义: 用于定义基本操作的自定义行为

本质: 修改的是程序默认形为,就形同于在编程语言层面上做修改,属于元编程(meta programming)

元编程(Metaprogramming,又译超编程,是指某类计算机程序的编写,这类计算机程序编写或者操纵其它程序(或者自身)作为它们的数据,或者在运行时完成部分本应在编译时完成的工作

一段代码来理解

#!/bin/bash
# metaprogram
echo '#!/bin/bash' >program
for ((I=1; I<=1024; I++)) do
    echo "echo $I" >>program
done
chmod +x program

这段程序每执行一次能帮我们生成一个名为program的文件,文件内容为1024行echo,如果我们手动来写1024行代码,效率显然低效

元编程优点:与手工编写全部代码相比,程序员可以获得更高的工作效率,或者给与程序更大的灵活度去处理新的情形而无需重新编译

Proxy 亦是如此,用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)

二、用法

Proxy为 构造函数,用来生成 Proxy实例

var proxy = new Proxy(target, handler)

参数

  • target表示所要拦截的目标对象(任何类型的对象,包括原生数组,函数,甚至另一个代理))
  • handler通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为

handler解析

关于handler拦截属性,有如下:

  • get(target,propKey,receiver):拦截对象属性的读取
  • set(target,propKey,value,receiver):拦截对象属性的设置
  • has(target,propKey):拦截propKey in proxy的操作,返回一个布尔值
  • deleteProperty(target,propKey):拦截delete proxy[propKey]的操作,返回一个布尔值
  • ownKeys(target):拦截Object.keys(proxy)、for…in等循环,返回一个数组
  • getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象
  • defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc),返回一个布尔值
  • preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值
  • getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象
  • isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值
  • setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值
  • apply(target, object, args):拦截 Proxy 实例作为函数调用的操作
  • construct(target, args):拦截 Proxy 实例作为构造函数调用的操作

Reflect

若需要在Proxy内部调用对象的默认行为,建议使用Reflect,其是ES6中操作对象而提供的新 API

基本特点:

  • 只要Proxy对象具有的代理方法,Reflect对象全部具有,以静态方法的形式存在
  • 修改某些Object方法的返回结果,让其变得更合理(定义不存在属性行为的时候不报错而是返回false)
  • 让Object操作都变成函数行为

下面我们介绍proxy几种用法:

get()

get接受三个参数,依次为目标对象、属性名和 proxy 实例本身,最后一个参数可选

var person = {
  name: "张三"
};

var proxy = new Proxy(person, {
  get: function(target, propKey) {
    return Reflect.get(target,propKey)
  }
});

proxy.name // "张三"

get能够对数组增删改查进行拦截,下面是试下你数组读取负数的索引

function createArray(...elements) {
  let handler = {
    get(target, propKey, receiver) {
      let index = Number(propKey);
      if (index < 0) {
        propKey = String(target.length + index);
      }
      return Reflect.get(target, propKey, receiver);
    }
  };

  let target = [];
  target.push(...elements);
  return new Proxy(target, handler);
}

let arr = createArray('a', 'b', 'c');
arr[-1] // c

注意:如果一个属性不可配置(configurable)且不可写(writable),则 Proxy 不能修改该属性,否则会报错

const target = Object.defineProperties({}, {
  foo: {
    value: 123,
    writable: false,
    configurable: false
  },
});

const handler = {
  get(target, propKey) {
    return 'abc';
  }
};

const proxy = new Proxy(target, handler);

proxy.foo
// TypeError: Invariant check failed

set()

set方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身

假定Person对象有一个age属性,该属性应该是一个不大于 200 的整数,那么可以使用Proxy保证age的属性值符合要求

let validator = {
  set: function(obj, prop, value) {
    if (prop === 'age') {
      if (!Number.isInteger(value)) {
        throw new TypeError('The age is not an integer');
      }
      if (value > 200) {
        throw new RangeError('The age seems invalid');
      }
    }

    // 对于满足条件的 age 属性以及其他属性,直接保存
    obj[prop] = value;
  }
};

let person = new Proxy({}, validator);

person.age = 100;

person.age // 100
person.age = 'young' // 报错
person.age = 300 // 报错

如果目标对象自身的某个属性,不可写且不可配置,那么set方法将不起作用

const obj = {};
Object.defineProperty(obj, 'foo', {
  value: 'bar',
  writable: false,
});

const handler = {
  set: function(obj, prop, value, receiver) {
    obj[prop] = 'baz';
  }
};

const proxy = new Proxy(obj, handler);
proxy.foo = 'baz';
proxy.foo // "bar"

注意,严格模式下,set代理如果没有返回true,就会报错

'use strict';
const handler = {
  set: function(obj, prop, value, receiver) {
    obj[prop] = receiver;
    // 无论有没有下面这一行,都会报错
    return false;
  }
};
const proxy = new Proxy({}, handler);
proxy.foo = 'bar';
// TypeError: 'set' on proxy: trap returned falsish for property 'foo'

deleteProperty()

deleteProperty方法用于拦截delete操作,如果这个方法抛出错误或者返回false,当前属性就无法被delete命令删除

var handler = {
  deleteProperty (target, key) {
    invariant(key, 'delete');
    Reflect.deleteProperty(target,key)
    return true;
  }
};
function invariant (key, action) {
  if (key[0] === '_') {
    throw new Error(`无法删除私有属性`);
  }
}

var target = { _prop: 'foo' };
var proxy = new Proxy(target, handler);
delete proxy._prop
// Error: 无法删除私有属性

注意,目标对象自身的不可配置(configurable)的属性,不能被deleteProperty方法删除,否则报错

取消代理

Proxy.revocable(target, handler);

三、使用场景

Proxy其功能非常类似于设计模式中的代理模式,常用功能如下:

  • 拦截和监视外部对对象的访问
  • 降低函数或类的复杂度
  • 在复杂操作前对操作进行校验或对所需资源进行管理

使用 Proxy 保障数据类型的准确性

let numericDataStore = { count: 0, amount: 1234, total: 14 };
numericDataStore = new Proxy(numericDataStore, {
    set(target, key, value, proxy) {
        if (typeof value !== 'number') {
            throw Error("属性只能是number类型");
        }
        return Reflect.set(target, key, value, proxy);
    }
});

numericDataStore.count = "foo"
// Error: 属性只能是number类型

numericDataStore.count = 333
// 赋值成功

声明了一个私有的 apiKey,便于 api 这个对象内部的方法调用,但不希望从外部也能够访问 api._apiKey

let api = {
    _apiKey: '123abc456def',
    getUsers: function(){ },
    getUser: function(userId){ },
    setUser: function(userId, config){ }
};
const RESTRICTED = ['_apiKey'];
api = new Proxy(api, {
    get(target, key, proxy) {
        if(RESTRICTED.indexOf(key) > -1) {
            throw Error(`${key} 不可访问.`);
        } return Reflect.get(target, key, proxy);
    },
    set(target, key, value, proxy) {
        if(RESTRICTED.indexOf(key) > -1) {
            throw Error(`${key} 不可修改`);
        } return Reflect.get(target, key, value, proxy);
    }
});

console.log(api._apiKey)
api._apiKey = '987654321'
// 上述都抛出错误

还能通过使用Proxy实现观察者模式

观察者模式(Observer mode)指的是函数自动观察数据对象,一旦对象有变化,函数就会自动执行

observable函数返回一个原始对象的 Proxy 代理,拦截赋值操作,触发充当观察者的各个函数

const queuedObservers = new Set();

const observe = fn => queuedObservers.add(fn);
const observable = obj => new Proxy(obj, {set});

function set(target, key, value, receiver) {
  const result = Reflect.set(target, key, value, receiver);
  queuedObservers.forEach(observer => observer());
  return result;
}

观察者函数都放进Set集合,当修改obj的值,在会set函数中拦截,自动执行Set所有的观察者

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/787805.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Java练习题汇总】《第一行代码JAVA》异常处理篇,汇总Java练习题——异常的概念及处理标准格式(try、catch、finally、throws、throw)、断言机制 Assertion ~

Java练习题 异常处理篇 1️⃣ 异常处理篇 1️⃣ 异常处理篇 一、填空题 Throwable 下的两个子类是______________ 、______________ 。ArthmeticException 类表示______________ 异常&#xff0c; ArraysIndexOutOfBoundsException 表示______________ 异常。一个 try 代码后…

Linux内核提权漏洞(Dirty-pipe)复现

前言&#xff1a;CVE-2022-0847 是存在于 Linux内核 5.8 及之后版本中的本地提权漏洞。攻击者通过利用此漏洞&#xff0c;可覆盖重写任意可读文件中的数据&#xff0c;从而可将普通权限的用户提升到特权 root。 CVE-2022-0847 的漏洞原理类似于 CVE-2016-5195 脏牛漏洞&#xf…

Vue入门项目——WebApi

Vue入门——WebApi vue3项目搭建组合式API响应式APIreactive()ref() 生命周期钩子computed计算属性函数watch监听函数父子通信模板引用组合选项 vue3项目搭建 简单看下Vue3的优势吧 下载安装npm及node.js16.0以上版本&#xff08;确保安装成功可用如下代码检查版本&#xff0…

YOLOX-PAI 论文学习

1. 解决了什么问题&#xff1f; 对 YOLOX 做加速&#xff0c;在单张 Tesla V100 上取得了 42.8 42.8 42.8mAP&#xff0c;推理速度为 1 毫秒。 2. 提出了什么方法&#xff1f; 2.1 主干网络 YOLOv6 和 PP-YOLOE 都将主干网络从 CSPNet 切换到了 RepVGG。RepVGG 在推理时&a…

PHP在线相册--【强撸项目】

强撸项目系列总目录在000集 PHP要怎么学–【思维导图知识范围】 文章目录 本系列校训本项目使用技术 上效果图phpStudy 设置导数据库项目目录如图&#xff1a;代码部分&#xff1a;主页 配套资源作业&#xff1a; 本系列校训 用免费公开视频&#xff0c;卷飞培训班哈人&…

【深度学习】【Image Inpainting】Free-Form Image Inpainting with Gated Convolution

模型&#xff1a;DeepFillv2 (CVPR’2019) 论文&#xff1a;https://arxiv.org/abs/1806.03589 代码&#xff1a;https://github.com/JiahuiYu/generative_inpainting 文章目录 效果AbstractIntroductionRelated WorkAutomatic Image InpaintingGuided Image Inpainting and Sy…

KaiwuDB 资深解决方案专家周幸骏:打造核心时序引擎,释放数据新价值

以下为浪潮 KaiwuDB 资深解决方案专家周幸骏受邀于7月4日在京举行的可信数据库发展大会发表演讲的实录摘要&#xff0c;欢迎大家点赞、收藏、关注&#xff01; 01 聚焦“时序” 据 IDC 等权威数据统计&#xff0c;预估 2025 年物联网设备产生的数据可达到 79.4ZB &#xff1b;…

ubuntu与windows之间的文件共享

最近在做项目&#xff0c;需要用到ubuntu进行代码编译生成可执行文件&#xff0c;但是我个人阅读和编写代码喜欢用source insight这个软件IDE。安利一下这个软件&#xff0c;阅读代码和编辑代码真的很棒啊&#xff0c;谁用谁知道吧&#xff01; 由于在ubuntu中安装source insig…

ShardingJDBC - 学习笔记

一、简介 官网&#xff1a;Apache ShardingSphere 下载地址&#xff1a;下载 :: ShardingSphere 快速入门&#xff1a;ShardingSphere-JDBC :: ShardingSphere 分库分表框架 ShardingSphere包括Sharding-JDBC、Sharding-Proxy、Sharding-Sidecar三个开源分布式数据库中间件解…

每月被攻击4000万次,全球最不安全的关键基础设施实锤了

在当今不确定的地缘政治环境中&#xff0c;无论是个人还是企业都高度关注潜在的网络攻击。而这其中最令人担忧的是对关键基础设施和工业资产的攻击。 关键基础设施中尤其是电力、交通、通信、金融等系统均在现代社会中发挥着至关重要的作用&#xff0c;因此关键基础设施一直是…

程序员如何向老板提加薪?

今天的问题不仅适用于程序员&#xff0c;对于其他职业同样适用。如果你认为自己所做的工作应该得到更多的报酬&#xff0c;并且想为此做点什么&#xff0c;你有两个选择&#xff1a;找一个新的高薪工作或要求加薪。 这两种选择都会带来新的焦虑&#xff0c;但它们都会带来新的…

【框架篇】Spring Boot 配置文件(详细教程)

Spring Boot 配置文件 一&#xff0c;配置文件的用途 配置文件在Spring Boot项目中的地位是比较重要&#xff0c;其功能可主要归纳为以下几个&#xff1a; 进行配置程序属性&#xff1a;配置文件允许设置应用程序的各种属性&#xff0c;如数据库连接、端口号、日志级别、缓存…

多态及其原理

文章目录 构成多态的条件虚函数作用&#xff1a;完成重写 重写重载 重写 隐藏为什么析构函数要搞成符合多态?原理预热对于基类指针或引用指向父类或者子类的成员函数是如何调用不同的函数呢&#xff1f; 一个类如果是基类&#xff0c;它的析构函数最好加上virtual 构成多态的条…

【【51单片机的LCD1602 最简单的一集】】

最简单的一集&#xff0c;明白运算显示就没问题 这一节相对简单 其实只要明白显示行列就可以了 剩下来的取什么基本上就是遍历 然后读取到\0停止 下面是基础的LCD1602的功能 #include <REGX52.H> sbit LCD_RSP2^6; sbit LCD_RWP2^5; sbit LCD_EP2^7; #define LCD_Data…

ES6基础知识七:你是怎么理解ES6中 Generator的?使用场景?

一、介绍 Generator 函数是 ES6 提供的一种异步编程解决方案&#xff0c;语法行为与传统函数完全不同 回顾下上文提到的解决异步的手段&#xff1a; 回调函数promise 那么&#xff0c;上文我们提到promsie已经是一种比较流行的解决异步方案&#xff0c;那么为什么还出现Gen…

归并排序法解释

什么是归并排序法 归并排序是一种常见的排序算法&#xff0c;它基于分治策略&#xff0c;将一个大问题分解为小问题来解决。归并排序的主要思想是将待排序的数组分成两个子数组&#xff0c;分别对这两个子数组进行排序&#xff0c;最后将两个有序的子数组合并成一个有序的数组…

Openlayers入门,Openlayers调整中心点坐标、Openlayers调整缩放级别、Openlayers调整地图可视角度和地图复位

专栏目录: OpenLayers入门教程汇总目录 前言 本章介绍一下Openlayers最基础的调整中心点坐标方式、调整缩放级别、调整地图可视角度和地图复位的小功能示例,非常简单,可直接上手。 二、依赖和使用 "ol": "^6.15.1"使用npm安装依赖npm install ol@6…

重学C++系列之继承

一、什么是继承 继承是面向对象三大特性之一&#xff0c;C中&#xff0c;被继承的类称为基类&#xff08;父类&#xff09;&#xff0c;继承别的类的类成为派生类&#xff08;子类&#xff09;&#xff0c;继承除了基类的构造函数和析构函数不继承外&#xff0c;其余成员全部继…

【安卓】视频播放器实现过程,超详细注释,自定义视频进度条,打开本地文件播放视频等功能。

一、实现效果 废话不多说&#xff0c;直接上代码&#xff0c;里面有详细注释&#xff0c;不清楚的评论区留言。 二、布局代码 <?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res…

Android安卓实战项目(3)—一个炫酷的健身APP界面

Android安卓实战项目&#xff08;3&#xff09;—一个炫酷的健身APP界面 一.项目运行介绍 1.大致浏览 2.功能介绍 &#xff08;1&#xff09;功能一 上导航条 &#xff08;2&#xff09;功能二 下导航条 二.具体实现 MainActivity.java package com.rckdeveloper.fitene…