【问题标题】:Using ES6 Proxies to hide private properties使用 ES6 代理隐藏私有属性
【发布时间】:2015-10-12 07:51:59
【问题描述】:

我正在尝试创建一个尽可能隐藏对象私有属性的函数。我在这里将私有属性定义为以下划线开头的属性,例如。 _password.

以下是我目前所得到的(感谢Nicolas Bevacqua's great intro to proxies)。

现在我想知道:

  1. 我是否使用以下代码覆盖了所有基础?还是我错过了一个重要的代理陷阱,仍然可以通过它访问对象?
  2. 这是将Reflect 方法与代理结合使用的正确方法吗?我在这里还需要它们吗?
  3. 我为私有财产返回的值是否真实到足以让人们认为该财产真的不存在?

到目前为止我的功能:

function privatize(obj, prefix = '_', throwError = false) {
  const proxyHandler = {
    get(target, key) {
        return private(key, 'get') ? undefined : Reflect.get(target, key);
      },
      set(target, key, value) {
        return private(key, 'set') ? undefined : Reflect.set(target, key, value);
      },
      has(target, key) {
        return private(key, 'has') ? false : Reflect.has(target, key);
      },
      deleteProperty(target, key) {
        return private(key, 'delete') ? false : Reflect.deleteProperty(target, key);
      },
      defineProperty(target, key, descriptor) {
        return private(key, 'defineProperty') ? false : Reflect.defineProperty(target, key, descriptor);
      },
      enumerate(target) {
        return Object.keys().filter((key) => {
          return !private(key, null, false);
        })[Symbol.iterator]();
      },
      ownKeys(target) {
        return Reflect.ownKeys(target).filter((key) => {
          return !private(key, null, false);
        });
      },
      getOwnPropertyDescriptor(target, key) {
        return private(key, 'getOwnPropertyDescriptor') ? false : Reflect.getOwnPropertyDescriptor(target, key);
      }
  };

  function private(key, operationName) {
    if (key.indexOf(prefix) === 0) {
      if (throwError) {
        throw new Error(`Operation '${operationName}' is not allowed on private properties.`);
      }
      return true;
    }
  }

  return new Proxy(obj, proxyHandler);
}

var o = {
  first: 'should work',
  _second: 'should fail'
};

var proxied = privatize(o);

console.log(proxied);

PS:对于原生浏览器支持,您可能需要在 MS Edge 或 Firefox Dev Edition 中查看。

http://jsfiddle.net/bkd7mde7/1/

【问题讨论】:

  • 即使你很好地隐藏了属性,这些值不是主要从源脚本或 ajax 响应中可见吗?
  • 是的,没错,反正我也无法完全隐藏。我只是不希望他们意外与第三方代码交互(例如,有人循环通过我的代理对象)。
  • Are the values ... real enough 你这是什么意思?
  • @just-boris 是我为隐藏值返回的值,如果属性不存在,您希望收到这些值?例如。如果属性不存在(不是undefined、null 或其他任何内容),in 运算符应返回 false。
  • 我认为使用代理来隐藏所有私有属性没有任何用处。你不只是从外面隐藏它们,你对每个人都隐藏它们——如果你想编写你的方法来直接访问target(或o)变量,你可以……呃……*应该* 只是首先使用闭包来保护隐私。

标签: javascript ecmascript-6 es6-proxy


【解决方案1】:

您需要了解“不变量”的概念。例如,如果一个对象是不可扩展的,则不允许通过代理隐藏其属性,并且不可配置的属性可能不会被隐藏。你不能defineProperty 一个它还没有的属性。 getOwnPropertyDescriptor 必须返回一个对象或undefined。 deleteProperty 无法删除不可配置的属性。 set 无法更改不可写、不可配置的属性。任何或所有这些都可能导致您的代码在各种情况下失败(通过在运行时抛出)。

其他小问题包括 set 应该返回一个布尔值(成功/失败),尽管我不确定您返回的 undefined 会发生什么。

【讨论】:

    【解决方案2】:

    您的代码有几个问题:

    • private 函数的参数中的 throwError = throwError 部分是多余的(实际上甚至在最新的 Chrome 中都不起作用),因为无论如何throwError 在函数内部都是可用的。
    • getOwnPropertyDescriptor 陷阱永远不应该返回 false。对于不存在的属性,它应该返回 undefined。
    • The enumerate trap 已过时。
    • 在proxied 对象上调用Reflect.preventExtensions() 会破坏它。您应该通过添加 preventExtensions trap 并在其中返回 false 来防止对象的扩展。

    【讨论】:

      猜你喜欢
      • 2014-01-07
      • 1970-01-01
      • 2011-06-08
      • 2014-04-05
      • 1970-01-01
      • 1970-01-01
      • 2018-07-21
      相关资源
      最近更新 更多