简体   繁体   English

ES6:如何对对象及其属性进行吸气/设置

[英]ES6: How to have a getter/setter for an object AND its properties

Using JS getters and setters, I am aiming to create the following API case: 我旨在使用JS getter和setters创建以下API案例:

// the user should be able to write this to update thing

thing = {
   x: 1,
   y: 2
}

// OR they should be able to write this

thing.x = 1
thing.y = 2

Right now I am using code like this: 现在我正在使用这样的代码:

 get thing() {
   return {
     x: this._thing.x,
     y: this._thing.y
   };
 }

 set thing(value) {
   this._thing.x = value.x,
   this._thing.y = value.y
 }

This supports the first case, but not the second case. 这支持第一种情况,但不支持第二种情况。

Can this be done in any reasonably simple way? 可以用任何合理的简单方法来完成吗?

EDIT: I will type up an example, but a use case for this might be that thing.x and thing.y should always round to an integer using Math.round() . 编辑:我将键入一个示例,但为此的用例可能是thing.xthing.y应该始终使用Math.round()舍入为整数。

If you have to use getters and setters, a relatively simple solution would be define x and y as getters and setters as well. 如果必须使用getter和setter,则相对简单的解决方案是将xy也定义为getter和setter。

get thing() {
  var self = this;
  return {
    get x() {
      return self._thing.x;
    }
    set x(value) {
      self._thing.x = value;
    }
    // same for y
  };
}

But you have to be aware that every time thing is accessed for reading , a new object will be created. 但是您必须意识到,每次访问thing进行读取时 ,都会创建一个新对象。 Though you could avoid this by caching that object and reuse it. 尽管您可以通过缓存该对象并重用它来避免这种情况。

In fact, I probably wouldn't have _thing at all. 实际上,我可能_thing I'd just store _x and _y and generate an object once on demand: 我只存储_x_y并按需生成一个对象:

class Foo {
  get thing() {
    if (this._thing) {
      return this._thing;
    }

    var self = this;
    return this._thing = {
      get x() {
        return self._x;
      }

      set x(value) {
        self._x = value;
      }
    };
  }

  set thing(value) {
    this._x = value.x;
    this._y = value.y;
  }
}

...a use case for this might be that thing.x and thing.y should always round to an integer using Math.round() . ...一个用例可能是thing.xthing.y应该始终使用Math.round()舍入为整数。

Consider using a Proxy 考虑使用代理

 class Parent { constructor() { this.thing = {x: 15.6, y: 2.1}; } set thing(value) { this._thing = new Proxy(value, { get: function(target, property, receiver) { // Catch all get access to properties on the object if (['x', 'y'].includes(property)) { // for x and y, return the real value, rounded return Math.round(target[property]); } } }); } get thing() { return this._thing; } } var parent = new Parent(); console.log(parent.thing.x); // 16 parent.thing.x = 13.2; console.log(parent.thing.x); // 13 parent.thing = {x: 10.1, y: 5.4}; console.log(parent.thing.y); // 5 // This works for the Jacque Goupil's conundrum var anotherRef = parent.thing; anotherRef.x = 5.8; console.log(parent.thing.x); // 6 // In fact, if you wanted to dance with the devil... var plainObj = {x: 10.1, y: 5.4}; parent.thing = plainObj; plainObj.x = 7.2; console.log(parent.thing.x); // 7 parent.thing.x = 22.2; console.log(plainObj.x); // 22.2 

The proxy allows you to catch a get or set operation on a property. 代理允许您捕获属性的获取或设置操作。

Caveat: IE doesn't natively support proxies for the moment. 警告: IE目前暂时不支持代理。 If I had a CDN for Google's Proxy polyfill I would add it to the snippet, but I don't. 如果我有一个用于Google Proxy polyfill的CDN,我会将其添加到摘要中,但我没有。 Also if you use Babel , there's babel-plugin-proxy 另外,如果您使用Babel ,则还有babel-plugin-proxy

I don't think you should use this pattern, and I think you'll have trouble making it work. 我认为您不应该使用这种模式,并且我认为您将无法使其正常工作。 Consider the following cases: 考虑以下情况:

// calls the position setter on foo
foo.position = {x: 10, y: 20};

// calls the position getter on foo, obtaining a simple {x:10,y:20} object
var pos = foo.position;

// calls the position getter on foo and then the x getter on position
var xx = foo.position.x;

So far, everything makes sense. 到目前为止,一切都有意义。 Then we get to this situation: 然后我们进入这种情况:

// calls the position getter on foo and then the x setter on position
foo.position.x = 7;

Since we returned a simple map with the position getter, position.x simply assigns to the returned copy and doesn't modify foo's actual position. 由于我们返回了一个带有位置获取器的简单地图,所以position.x只是分配给返回的副本,而不会修改foo的实际位置。 One way to fix this would be to have the position getter return a smarter object that has a reference to the foo instance and proper getter/setters. 解决此问题的一种方法是让位置获取器返回一个更智能的对象,该对象具有对foo实例和适当的获取器/设置器的引用。 This would allow doing: 这将允许执行以下操作:

foo.position = bar.position;
bar.x += 10;

The bar.position getter would return an object that merely acts as a view for bar 'sx/y properties. 所述bar.position吸气剂都将返回一个仅作为一个视图中的对象bar “SX / y属性。 Then, the foo.position setter would copy bar 'sx/y properties into its private storage. 然后, foo.position设置程序会将bar的sx / y属性复制到其专用存储中。 Makes sense. 说得通。 bar.x += 10 ads only to bar 's position. bar.x += 10广告仅针对bar的位置。

However, the following situation would be very confusing: 但是,以下情况将非常令人困惑:

var temp = foo.position;
foo.position = bar.position;
bar.position = temp;

This creates the illusion that temp is a backup copy of foo.position but it isn't. 这产生了一种幻觉,认为temp是foo.position的备份副本,但事实并非如此。 It's a view. 这是一个观点。 As soon as foo's position changes, we lose the data for good. 一旦foo的位置发生变化,我们就会永远丢失数据。 There's no easy way to copy the position. 没有简单的方法可以复制该职位。

You could try making your position objects actually store both a copy of the data as well as a reference to the original, so that you can do both set and get... It's an endless problem. 您可以尝试使position对象实际存储数据的副本以及原始数据的引用,以便既可以设置又可以获取...这是一个无休止的问题。

But at this point, your sugar syntax is making everything much harder to maintain as well as much less efficient. 但是在这一点上,您的糖语法使所有内容的维护变得更加困难,效率也大大降低。

So instead of doing all this, I'd actually make is so that the x and y getter/setters are on the foo/bar objects themselves. 因此,实际上我没有做所有这些事情,而是使x和y getter / setters位于foo / bar对象本身上。 I'd also make any code inside the foo/bar object treat positions as if they were immutable. 我还要使foo / bar对象内的任何代码都将位置视为不变。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM