ES6中Proxy的使用

news/2024/7/5 4:26:26

1、概述

Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”

var obj = new Proxy({}, {
  get: function (target, propKey, receiver) {
    console.log(`getting ${propKey}!`);
    return Reflect.get(target, propKey, receiver);
  },
  set: function (target, propKey, value, receiver) {
    console.log(`setting ${propKey}!`);
    return Reflect.set(target, propKey, value, receiver);
  }
});

上面代码对一个空对象架设了一层拦截,重定义了属性的读取(get)和设置(set)行为。这里暂时先不解释具体的语法,只看运行结果。对设置了拦截行为的对象obj,去读写它的属性,就会得到下面的结果。

obj.count = 1
//  setting count!
++obj.count
//  getting count!
//  setting count!
//  2

下面是另一个拦截读取属性行为的例子。

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

proxy.time // 35
proxy.name // 35
proxy.title // 35

如果handler没有设置任何拦截,那就等同于直接通向原对象。

var target = {};
var handler = {};
var proxy = new Proxy(target, handler);
proxy.a = 'b';
target.a // "b"

2、Proxy 实例的方法 get()

get方法的用法,上文已经有一个例子,下面是另一个拦截读取操作的例子。

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

var proxy = new Proxy(person, {
  get: function(target, propKey) {
    if (propKey in target) {
      return target[propKey];
    } else {
      throw new ReferenceError("Prop name \"" + propKey + "\" does not exist.");
    }
  }
});

proxy.name // "张三"
proxy.age // 抛出一个错误

下面的例子使用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

利用 Proxy,可以将读取属性的操作(get),转变为执行某个函数,从而实现属性的链式操作。


var pipe = function (value) {
  var funcStack = [];
  var oproxy = new Proxy({} , {
    get : function (pipeObject, fnName) {
      if (fnName === 'get') {
        return funcStack.reduce(function (val, fn) {
          return fn(val);
        },value);
      }
      funcStack.push(window[fnName]);
      return oproxy;
    }
  });

  return oproxy;
}

var double = n => n * 2;
var pow    = n => n * n;
var reverseInt = n => n.toString().split("").reverse().join("") | 0;

pipe(3).double.pow.reverseInt.get; // 63

下面的例子则是利用get拦截,实现一个生成各种 DOM 节点的通用函数dom。

const dom = new Proxy({}, {
  get(target, property) {
    return function(attrs = {}, ...children) {
      const el = document.createElement(property);
      for (let prop of Object.keys(attrs)) {
        el.setAttribute(prop, attrs[prop]);
      }
      for (let child of children) {
        if (typeof child === 'string') {
          child = document.createTextNode(child);
        }
        el.appendChild(child);
      }
      return el;
    }
  }
});

const el = dom.div({},
  'Hello, my name is ',
  dom.a({href: '//example.com'}, 'Mark'),
  '. I like:',
  dom.ul({},
    dom.li({}, 'The web'),
    dom.li({}, 'Food'),
    dom.li({}, '…actually that\'s it')
  )
);

document.body.appendChild(el);

下面是一个get方法的第三个参数的例子,它总是指向原始的读操作所在的那个对象,一般情况下就是 Proxy 实例。

const proxy = new Proxy({}, {
  get: function(target, key, receiver) {
    return receiver;
  }
});
proxy.getReceiver === proxy // true

3、Proxy 实例的方法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;
    return true;
  }
};

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

person.age = 100;

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

有时,我们会在对象上面设置内部属性,属性名的第一个字符使用下划线开头,表示这些属性不应该被外部使用。结合get和set方法,就可以做到防止这些内部属性被外部读写。

const handler = {
  get (target, key) {
    invariant(key, 'get');
    return target[key];
  },
  set (target, key, value) {
    invariant(key, 'set');
    target[key] = value;
    return true;
  }
};
function invariant (key, action) {
  if (key[0] === '_') {
    throw new Error(`Invalid attempt to ${action} private "${key}" property`);
  }
}
const target = {};
const proxy = new Proxy(target, handler);
proxy._prop
// Error: Invalid attempt to get private "_prop" property
proxy._prop = 'c'
// Error: Invalid attempt to set private "_prop" property

http://lihuaxi.xjx100.cn/news/1149517.html

相关文章

文件删了怎么找回来

在电脑使用过程中&#xff0c;我们难免会误删一些重要的文件。当我们发现误删后&#xff0c;有时会感到非常焦虑&#xff0c;不知道文件删了怎么找回来?可以在这里看看&#xff0c;本文将介绍一些常见的找回文件的方法&#xff0c;帮助大家解决这一问题。 一、使用系统自带的回…

Java的枚举类型用法介绍

1 背景 在java语言中还没有引入枚举类型之前&#xff0c;表示枚举类型的常用模式是声明一组具有int常量。之前我们通常利用public final static 方法定义的代码如下&#xff0c;分别用1 表示春天&#xff0c;2表示夏天&#xff0c;3表示秋天&#xff0c;4表示冬天。 public c…

刚测完Bug,就被开除了····

我曾在一家软件公司担任功能测试工程师&#xff0c;经历了三年的工作。在这段时间里&#xff0c;我积累了丰富的测试经验和技能&#xff0c;在团队中也有着不错的表现。然而&#xff0c;最终我却被公司辞退了。 在我入职时&#xff0c;公司还没有建立完善的测试流程和标准。我的…

Git宝典

版本管理工具介绍 现在比较流行的版本管理工具是git&#xff0c;但是实际上git是近几年才发展起来的&#xff0c;可能有一些老的项目&#xff0c;还在用一些老的软件&#xff0c;如svn 版本管理发展简史 SVN&#xff08;SubVersion&#xff09; 工作流程 SVN是集中式版本控…

前端面试整理5

目录 1.父子组件生命周期执行顺序&#xff1f; 2.localstorage.sessionstorage,cookie的区别&#xff1f; 3.js截取字符串方案&#xff1f; 4.Webpack的优化流程&#xff1f; 5.协商缓存和强缓存&#xff1f; 6.静态资源是强缓存&#xff0c;会不会向服务器发请求&#x…

深入探究Android线程:理解与应用

在Android应用程序中&#xff0c;线程是关键的概念之一。本文将详细介绍Android线程的概念、多线程编程的必要性以及在应用程序中正确使用线程的方法。我们将深入探讨Android线程模型、主线程和后台线程的区别&#xff0c;以及如何处理线程间通信和线程安全性的问题&#xff0c…

在已有VPC中创建EKS集群

1. 美东1 默认配置 创建在master-vcp中节点放在两上Public Subnet上,便于SSH登录维护Attach上默认安全组sg-071f18562f41b5804,打通各种常规的网络访问规则cat << EOF > master-eks-cluster.yaml apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata:name…

如何进行大数据测试

大数据解决方案 大数据解决方案包括一系列工具和技术&#xff0c;用于收集、存储、处理和分析大量的数据。以下是一些常用的大数据解决方案&#xff1a; Apache Hadoop&#xff1a;Hadoop是一个开源的大数据处理框架&#xff0c;可以在商用硬件上处理大规模数据集。它包括HDFS…