使用ES6 Proxies隐藏私有属性

时间:2015-10-12 07:51:59

标签: javascript ecmascript-6 es6-proxy

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

以下是我迄今为止所得到的内容(感谢Nicolas Bevacqua's great intro to proxies)。

现在我想知道:

  1. 我是否用以下代码覆盖所有基地?或者我错过了一个重要的代理陷阱,通过它可以访问对象?
  2. 这是将Reflect方法与代理结合使用的正确方法吗?我甚至在这里需要它们吗?
  3. 我为私有财产返回的值是否足够让人们认为该财产确实不存在?
  4. 到目前为止我的功能:

    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/

2 个答案:

答案 0 :(得分:1)

您的代码存在一些问题:

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

答案 1 :(得分:1)

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

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