0Pricing
JavaScript Academy · 课时

Proxy 的实际应用场景

构建验证、日志记录和响应式对象。

Proxy 的实际应用场景 是 CoddyKit 上的免费 JavaScript Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 JavaScript Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 JavaScript Academy 课程共包含 4 节课。

代理对象实践

现在您将结合代理对象和 Reflect 来解决实际问题:验证、日志记录、默认值等。每种模式都会包装一个普通对象,而不修改它。

验证包装器

强制执行模式:拒绝任何不符合规则的写入。由于错误数据永远不会写入,target 可以保持整洁。

function validated(obj, rules) {
  return new Proxy(obj, {
    set(target, prop, value, receiver) {
      const rule = rules[prop];
      if (rule && !rule(value)) {
        throw new TypeError('invalid value for ' + prop);
      }
      return Reflect.set(target, prop, value, receiver);
    }
  });
}

const user = validated({}, { age: v => v >= 0 });
user.age = 25;
console.log(user.age);
try { user.age = -1; } catch (e) { console.log(e.message); }

日志记录包装器

在记录日志后通过 Reflect 转发操作,以跟踪每次读取和写入,便于调试。

function logged(obj) {
  return new Proxy(obj, {
    get(t, p, r) { console.log('GET ' + p); return Reflect.get(t, p, r); },
    set(t, p, v, r) { console.log('SET ' + p + '=' + v); return Reflect.set(t, p, v, r); }
  });
}

const data = logged({ count: 0 });
data.count = 5;
console.log(data.count);

默认值

为任何缺失属性返回 fallback,因此读取结果不会是 undefined。

function withDefault(obj, fallback) {
  return new Proxy(obj, {
    get(t, p, r) {
      return p in t ? Reflect.get(t, p, r) : fallback;
    }
  });
}

const scores = withDefault({ ada: 90 }, 0);
console.log(scores.ada);
console.log(scores.unknown);

数组负索引

通过在 get 拦截器中转换索引,让数组支持类似 Python 的负索引。

function negIndex(arr) {
  return new Proxy(arr, {
    get(t, p, r) {
      const i = Number(p);
      if (Number.isInteger(i) && i < 0) {
        return Reflect.get(t, t.length + i, r);
      }
      return Reflect.get(t, p, r);
    }
  });
}

const list = negIndex(['a', 'b', 'c']);
console.log(list[-1]);
console.log(list[0]);

只读视图

提供一个可以读取但永远不能写入的对象,可用于保护共享的 config。

function readonly(obj) {
  return new Proxy(obj, {
    set() { console.log('blocked write'); return false; },
    deleteProperty() { console.log('blocked delete'); return false; }
  });
}

const config = readonly({ apiUrl: '/api' });
config.apiUrl = '/hacked';
console.log(config.apiUrl);

统计访问次数

将 counts 存储在 target 外部,以跟踪每个属性被读取的次数。

function counted(obj) {
  const counts = {};
  const proxy = new Proxy(obj, {
    get(t, p, r) {
      counts[p] = (counts[p] || 0) + 1;
      return Reflect.get(t, p, r);
    }
  });
  return { proxy, counts };
}

const { proxy, counts } = counted({ x: 1 });
proxy.x; proxy.x; proxy.x;
console.log(counts.x);

计算虚拟属性

公开未被存储的派生值,并在每次读取时重新计算。

const rect = new Proxy({ width: 4, height: 3 }, {
  get(t, p, r) {
    if (p === 'area') return t.width * t.height;
    return Reflect.get(t, p, r);
  }
});

console.log(rect.area);
rect.width = 10;
console.log(rect.area);

读取未知属性时抛出异常

当代码读取不存在的属性时抛出异常,从而尽早发现拼写错误。

function strict(obj) {
  return new Proxy(obj, {
    get(t, p, r) {
      if (typeof p === 'string' && !(p in t)) {
        throw new ReferenceError('unknown property: ' + p);
      }
      return Reflect.get(t, p, r);
    }
  });
}

const o = strict({ name: 'Ada' });
console.log(o.name);
try { console.log(o.naem); } catch (e) { console.log(e.message); }

组合多个关注点

实际的包装器通常会在一个处理程序中结合验证和日志记录,并让每个拦截器都通过 Reflect 转发操作。

const account = new Proxy({ balance: 0 }, {
  set(t, p, v, r) {
    if (p === 'balance' && v < 0) throw new RangeError('no overdraft');
    console.log(p + ' set to ' + v);
    return Reflect.set(t, p, v, r);
  }
});

account.balance = 100;
console.log(account.balance);
try { account.balance = -50; } catch (e) { console.log(e.message); }

这些模式为何重要

代理对象可以添加横切行为、验证、日志记录、默认值和访问控制,而不会污染原始对象或其调用方。这正是响应式框架观察 state 变化的方式。当您需要透明地拦截操作时,可以使用它们。

const observed = new Proxy({ n: 0 }, {
  set(t, p, v, r) {
    const ok = Reflect.set(t, p, v, r);
    console.log(p + ' is now ' + t[p]);
    return ok;
  }
});
observed.n = 1;
observed.n = 2;

快速检查

如果要在无效值被存储之前拒绝它们,应使用哪个代理拦截器?

回顾:代理对象的实际使用场景

您将代理对象应用到了实际问题中:

  • set 拦截器中的验证会拒绝错误数据。
  • 日志记录通过 Reflect 跟踪读取和写入。
  • 默认值和虚拟属性位于 get 拦截器中。
  • 只读视图、访问计数和严格的拼写错误检查都可以透明地包装对象。

常见问题解答

「Proxy 的实际应用场景」课时是免费的吗?

是的 — 「Proxy 的实际应用场景」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 JavaScript Academy 课程的其余内容,请升级到 CoddyKit PRO。 JavaScript Academy 课程共包含 4 节课。

「Proxy 的实际应用场景」这节课中我会学到什么?

构建验证、日志记录和响应式对象。 你通过在浏览器中直接运行的动手代码来练习 JavaScript Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 JavaScript Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 JavaScript Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「Proxy 的实际应用场景」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 JavaScript Academy 课中编写并运行代码吗?

能。每节 JavaScript Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 创建 Proxy
  2. get 和 set 陷阱
  3. Reflect API
  4. Proxy 的实际应用场景
← 返回 JavaScript Academy