从 ES6 类构造函数中返回 ES6 Proxy

29
我希望用户只能为一个对象设置特定属性,但同时该对象应该由自定义类构建。
例如:
var row = new Row({
  name : 'John Doe',
  email : 'uhiwarale@gmail.com'
}, Schema);

row 可以有方法。但是当用户尝试设置 row.password 时,将不被允许。

一种解决方法是使用 new Proxy 代替 new Row,但那样我们将失去在 Row 类中所做的所有有趣事情。我想要 new Row 返回一个代理对象,并将 this 引用作为代理的目标。

有人有这方面的任何想法吗?如果你了解 mongoose,那么它是如何做到的呢?


2
你能更详细地描述一下你试图解决的问题吗?你似乎在描述一些可能的解决方案(使用代理),但并没有真正描述你想要实现什么。 - jfriend00
这非常有帮助:https://exploringjs.com/es6/ch_proxies.html - Andrew
3个回答

43

如果代理对你来说是必然发生的,一种限制集合功能的可能解决方案是返回一个ES6代理实例。

默认情况下,javascript中的构造函数自动返回this对象,但通过在this上实例化代理作为目标,您可以定义和返回自定义行为。请记住,代理中的set方法应返回布尔值。

MDN:set方法应返回布尔值。返回true表示分配成功。如果set方法返回false,并且分配发生在严格模式代码中,则会抛出TypeError。

class Row {
  constructor(entry) {
    // some stuff

    return new Proxy(this, {
      set(target, name, value) {
        let setables = ['name', 'email'];
        if (!setables.includes(name)) {
          throw new Error(`Cannot set the ${name} property`);
        } else {
          target[name] = value;
          return true;
        }
      }
    });
  }

  get name() {
    return this._name;
  }
  set name(name) {
    this._name = name.trim();
  }
  get email() {
    return this._email;
  }
  set email(email) {
    this._email = email.trim();
  }
}

现在,您不能根据代理设置不可设置的属性。

let row = new Row({
  name : 'John Doe',
  email : 'john@doe.com'
});

row.password = 'blahblahblah'; // Error: Cannot set the password property

对于get方法也可以有自定义的行为。

但是要注意不要覆盖返回给调用上下文的引用,需要小心处理。

注意:示例代码已在 Node v8.1.3 和现代浏览器上进行了测试。


错误: "Unexpected token new"?! 我认为你不能在构造函数中使用'new',而且它也不喜欢"return Proxy"? - Master James
1
哇,看起来这个工作了?不错。在其他尝试中似乎构造函数没有返回新的代理,所以感谢您提供的这个示例。 - Master James
@MasterJames 不用担心。是的,它在Node v8.1.3上对我有效。 - Vahid Hallaji
3
从构造函数返回代理的一个潜在问题是,这会以与用户受限制相同的方式限制您的代码,即业务逻辑不能在实例构造后设置或更新其他属性。解决此问题的一种方法是使用 handler.construct 方法代理类本身,以包装和限制返回给用户的实例,但仍允许您的代码访问底层实例。 - Joshua Skrzypek
4
为什么MDN关于类构造函数的页面(https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Classes/constructor)没有提到类构造函数可以返回对象?在哪里可以找到这个层次的信息? - junvar
为了使代码正常工作,您需要删除Schema和schema的提及,并将“set name(email){”修复为“set email(email){”。 - timkay

18

您可以完全不使用代理来完成此操作。

在您的类构造函数中,您可以像这样定义密码属性:

constructor(options, schema) {
    this.name = options.name;
    this.email = options.email;
    Object.defineProperty(this, 'password', {
        configurable: false, // no re-configuring this.password
        enumerable: true, // this.password should show up in Object.keys(this)
        value: options.password, // set the value to options.password
        writable: false // no changing the value with this.password = ...
    });
    // whatever else you want to do with the Schema
}
您可以在MDN的Object.defineProperty()页面上找到更多有关如何使用它的信息。

1
我的愿望是允许观察对象和数组属性操作符。这意味着任何未知名称的所有内容。在构造函数中返回代理使其成为可能,通过名称定义属性则不行。 - Master James
1
使用这种方法,新定义的属性可以立即在类构造函数或方法中使用。另一方面,如果在类构造函数中返回一个代理对象,则代理的属性/方法仅在对象完全实例化后才可用。这是一个非常显著的限制,因为在我看来,类构造函数或方法引用自身或将“this”关键字作为参数链接是非常常见的。 - Keo Strife

0

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