多多读书
1298 字
6 分钟
JavaScript 魔法师:玩转代理与反射
2024-08-02

今天我们要聊一聊两个听起来很”高大上”的概念 - 代理(Proxy)和反射(Reflect)。别被这些术语吓到,其实它们就像是 JavaScript 世界里的魔法棒,让我们能够轻松地操控代码的行为。想象一下,你可以像变魔术一样改变对象的性质,或者像魔镜一样洞察代码的内部运作,是不是很酷?让我们一起深入这个神奇的世界吧!

代理(Proxy):你的私人助理#

什么是代理?#

想象一下,你有一个私人助理,他可以帮你处理各种琐事,比如接电话、安排日程等。在 JavaScript 中,代理就像这样一个助理,它可以帮我们拦截和自定义对象的各种操作。

代理的小把戏#

来看个简单的例子:

const handler = {
  get(target, prop, receiver) {
    console.log(`有人想知道 "${prop}" 的秘密!`);
    return Reflect.get(target, prop, receiver);
  },
  set(target, prop, value, receiver) {
    console.log(`有人想把 "${prop}" 改成 ${value}!`);
    return Reflect.set(target, prop, value, receiver);
  }
};

const obj = {
  name: "小明"
};

const proxyObj = new Proxy(obj, handler);

console.log(proxyObj.name); // 有人想知道 "name" 的秘密!
proxyObj.age = 18; // 有人想把 "age" 改成 18!

看到了吗?我们的”助理”(代理)帮我们监控了所有想要查看或修改对象属性的行为。这就像给对象装了个监控摄像头,任何小动作都逃不过我们的眼睛!

反射(Reflect):魔镜魔镜告诉我#

反射是什么?#

如果说代理是私人助理,那反射就像是一面魔镜,它能让我们看到并操作 JavaScript 世界的本质。Reflect 对象提供了一系列方法,让我们能够以一种统一的方式来执行 JavaScript 的内部操作。

反射的妙用#

来看看反射是如何工作的:

const obj = {
  name: "小红",
  age: 20
};

console.log(Reflect.has(obj, "name")); // true (魔镜告诉我们,obj 有 name 属性)
console.log(Reflect.get(obj, "age")); // 20 (魔镜帮我们取出了 age 的值)

const newObj = Reflect.construct(Object, []); 
console.log(newObj); // {} (魔镜变出了一个新对象!)

代理与反射:最佳拍档#

代理和反射就像是一对好搭档,它们经常一起出现。代理负责拦截各种操作,而反射则帮助我们优雅地完成这些操作。看看它们是如何携手合作的:

const handler = {
  get(target, prop, receiver) {
    console.log(`有人在偷看 "${prop}"`);
    return Reflect.get(target, prop, receiver);
  },
  set(target, prop, value, receiver) {
    console.log(`有人想悄悄改 "${prop}" 为 ${value}`);
    return Reflect.set(target, prop, value, receiver);
  }
};

const obj = {
  name: "小华"
};

const proxyObj = new Proxy(obj, handler);

console.log(proxyObj.name); // 有人在偷看 "name"
proxyObj.age = 22; // 有人想悄悄改 "age" 为 22

实际应用:代理反射二重奏#

1. 神奇的记忆术(缓存)#

const createCache = () => {
  const cache = new Map();

  return new Proxy({}, {
    get(target, prop, receiver) {
      if (!cache.has(prop)) {
        const result = expensiveOperation(); // 假设这是一个很耗时的操作
        cache.set(prop, result);
        return result;
      } else {
        return cache.get(prop);
      }
    }
  });
};

const cachedData = createCache();

console.log(cachedData.someValue); // 第一次会很慢
console.log(cachedData.someValue); // 第二次就飞快了!

这就像是给我们的代码装了个”记忆芯片”,第一次算出的结果会被记住,下次再问就能立即回答了。

2. 保密小能手(权限控制)#

const createSecuredObject = (userRole) => {
  const data = {
    secretInfo: "这是天机不可泄露"
  };

  const handler = {
    get(target, prop, receiver) {
      if (prop === "secretInfo" && userRole !== "boss") {
        throw new Error("你没资格知道!");
      }
      return Reflect.get(target, prop, receiver);
    },
    set(target, prop, value, receiver) {
      if (prop === "secretInfo" && userRole !== "boss") {
        throw new Error("你没资格改!");
      }
      return Reflect.set(target, prop, value, receiver);
    }
  };

  return new Proxy(data, handler);
};

const employeeObject = createSecuredObject("employee");
console.log(employeeObject.secretInfo); // 抛出错误:你没资格知道!

const bossObject = createSecuredObject("boss");
console.log(bossObject.secretInfo); // 成功获取秘密信息

这就像给数据上了把”智能锁”,只有拥有正确”钥匙”(权限)的人才能看到或修改某些信息。

3. 代码监工(面向切面编程)#

const createSupervisor = (fn, before, after) => {
  return new Proxy(fn, {
    apply(target, thisArg, args) {
      before();
      const result = Reflect.apply(target, thisArg, args);
      after();
      return result;
    }
  });
};

const logBefore = () => {
  console.log("工作要开始啦!");
};

const logAfter = () => {
  console.log("工作结束啦!");
};

const wrappedFunction = createSupervisor(
  () => console.log("我在认真工作"),
  logBefore,
  logAfter
);

wrappedFunction(); // 输出:工作要开始啦! 我在认真工作 工作结束啦!

这就像给我们的函数安排了一个”监工”,在函数工作前后都要汇报一下,方便我们掌握工作进度。

总结#

代理和反射就像是 JavaScript 世界里的魔法道具,它们让我们能够以前所未有的方式操控代码。通过代理,我们可以像变魔术一样改变对象的行为;而反射则让我们能够深入了解并操作 JavaScript 的内部机制。

灵活运用这两个”魔法道具”,我们就能写出更智能、更安全、更高效的代码。无论是实现缓存、控制访问权限,还是进行代码监控,代理和反射都能帮上大忙。

所以,准备好成为 JavaScript 魔法师了吗?拿起你的代理魔杖和反射魔镜,让我们一起在 JavaScript 的魔法世界里大展身手吧!

JavaScript 魔法师:玩转代理与反射
https://fuwari.vercel.app/posts/20240803/
作者
我也困了
发布于
2024-08-02
许可协议
CC BY-NC-SA 4.0