使用ES6代理隐藏私有属性

4
我正在尝试创建一个函数,尽可能隐藏对象的私有属性。在此,我将以下划线开头的属性定义为私有属性,例如_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响应中可见。 - Blake Regalia
是的,没错,我无法完全隐藏它。我只是不希望他们意外地与第三方代码进行交互(例如,有人循环遍历我的代理对象)。 - nils
@just-boris 我返回的隐藏值是否是您期望接收的,如果该属性不存在?例如,如果属性不存在(不是undefinednull或其他任何内容),in运算符应该返回false - nils
只有在 throwError = false 时才会得到这个。一旦它被代理并带有 throw errors,您将在对私有属性进行 in 检查、获取和设置时得到 Exception - just-boris
2
我认为使用代理隐藏所有私有属性没有任何用处。你不仅仅是将它们从外部隐藏,还将它们从每个人的视线中隐藏 - 如果你想编写方法直接访问target(或o)变量,你本应该一开始就使用闭包来实现隐私保护。 - Bergi
显示剩余2条评论
2个回答

3
您需要了解“不变量”的概念。例如,如果对象是不可扩展的,则不允许通过代理隐藏其属性,且不可配置的属性不能被隐藏。您不能使用defineProperty定义不存在的属性。 getOwnPropertyDescriptor必须返回一个对象或undefined。不能删除非可配置属性。 set无法更改非可写入、非可配置属性。这些问题中的任何一个都可能导致代码在各种情况下失败(运行时抛出)。其他次要问题包括set应返回一个布尔值(成功/失败),尽管我不确定您返回的undefined会发生什么。

2
您的代码存在一些问题:
  • 在私有函数的参数中,throwError = throwError这一部分是多余的(事实上,在最新的Chrome浏览器中甚至无法工作),因为throwError将在函数内部可用。
  • getOwnPropertyDescriptor陷阱不应该返回false。对于不存在的属性,它应该返回undefined
  • 枚举陷阱已经过时了
  • proxied对象上调用Reflect.preventExtensions()会使其失效。您应该通过添加preventExtensions陷阱并在其中返回false来防止阻止对象扩展。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接