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

这篇具有很好参考价值的文章主要介绍了ES6基础知识八:你是怎么理解ES6中Proxy的?使用场景?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

ES6基础知识八:你是怎么理解ES6中Proxy的?使用场景?,es6,es6,前端,ecmascript
一、介绍

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

本质: 修改的是程序默认形为,就形同于在编程语言层面上做修改,属于元编程(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所有的观察者文章来源地址https://www.toymoban.com/news/detail-608251.html

到了这里,关于ES6基础知识八:你是怎么理解ES6中Proxy的?使用场景?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 你是怎么理解ES6中Module的?使用场景?

    模块,(Module),是能够单独命名并独立地完成一定功能的程序语句的集合(即程序代码和数据结构的集合体)。 两个基本的特征:外部特征和内部特征 外部特征是指模块跟外部环境联系的接口(即其他模块或程序调用该模块的方式,包括有输入输出参数、引用的全局变量

    2024年03月12日
    浏览(39)
  • 你是怎么理解ES6中 Decorator 的?使用场景?

    Decorator,即装饰器,从名字上很容易让我们联想到装饰者模式 简单来讲,装饰者模式就是一种在不改变原类和使用继承的情况下,动态地扩展对象功能的设计理论。 ES6 中 Decorator 功能亦如此,其本质也不是什么高大上的结构,就是一个普通的函数,用于扩展类属性和类方法

    2024年03月11日
    浏览(43)
  • 你是怎么理解ES6中 Promise的?使用场景?

    Promise ,译为承诺,是异步编程的一种解决方案,比传统的解决方案(回调函数)更加合理和更加强大 在以往我们如果处理多层异步操作,我们往往会像下面那样编写我们的代码 阅读上面代码,是不是很难受,上述形成了经典的回调地狱 现在通过 Promise 的改写上面的代码 瞬

    2024年03月15日
    浏览(56)
  • 你是怎么理解ES6中 Generator的?使用场景?

    Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同 回顾下上文提到的解决异步的手段: 回调函数 promise 那么,上文我们提到 promsie 已经是一种比较流行的解决异步方案,那么为什么还出现 Generator ?甚至 async/await 呢? 该问题我们留在后面再进行

    2024年03月14日
    浏览(43)
  • ES6基础知识二:ES6中数组新增了哪些扩展?

    一、扩展运算符的应用 ES6通过扩展元素符…,好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列 主要用于函数调用的时候,将一个数组变为参数序列 可以将某些数据结构转为数组 能够更简单实现数组复制 const a1 = [1, 2]; const […a2] = a1; // [1,2] 数组的合并也更为

    2024年02月16日
    浏览(38)
  • ES6基础知识三:对象新增了哪些扩展?

    一、属性的简写 ES6中,当对象键名与对应值名相等的时候,可以进行简写 方法也能够进行简写 在函数内作为返回值,也会变得方便很多 注意:简写的对象方法不能用作构造函数,否则会报错 二、属性名表达式 ES6 允许字面量定义对象时,将表达式放在括号内 表达式还可以

    2024年02月15日
    浏览(37)
  • JS 怎么理解ES6新增Set、Map两种数据结构?

    目录 一、前言 二、Set 1.Set数据结构定义 2.Set数据结构的特性 3.Set数据结构的基本使用 4.Set遍历数据 5.Set 的使用场景 6.WeakSet的使用 7.垃圾回收机制 三、Map 1.Map数据结构定义 2.Map数据结构的特性 3.Map数据结构的基本使用  4.Map遍历数据 5.Map的使用场景 6.WeakMap的使用 7.垃圾回收

    2024年02月08日
    浏览(36)
  • 【ES6】阮一峰ES6学习(六) Proxy

    es6 中全新设计了一个叫 Proxy 的类型, Proxy 这个词的原意是代理,用在这里表示由它来”代理“某些操作,可以译为”代理器“,可以这样理解: 在目标对象之前架设一层\\\"拦截\\\",外界对该对象的访问,都必须先通过这层拦截 。 举个简单的例子 比如茅台酒的代理,有了这个

    2023年04月16日
    浏览(38)
  • 音视频基础知识——素材理解

    素材是媒体内容生产中一切生产资料的集合,包括不限于视频、音频、图片、字幕等形式。 素材通过统一的协议把原始的数据有序组织起来,便于编辑与管理。比如一般的素材是由一个物理文件及其各类属性构成,在对素材进行剪辑处理时,不用改变源文件中的任何内容,通

    2024年02月01日
    浏览(54)
  • OpenGL 和 OpenGL ES基础知识

    当今许多视觉应用程序,从简单的游戏到高级工程领域,都使用OpenGL(Open Graphics Library)和OpenGL ES(OpenGL for Embedded Systems)作为其图形渲染API。这些API提供了一种跨平台、可移植且高性能的图形编程解决方案,支持大量不同类型的设备和操作系统。 在本篇博客中,我们将深入

    2024年02月08日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包