5

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

以下是我到目前为止所得到的(感谢Nicolas Bevacqua 对代理的精彩介绍)。

现在我想知道:

  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/

4

2 回答 2

3

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

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

于 2016-12-02T02:51:23.293 回答
2

您的代码有几个问题:

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