对动态对象属性使用 getter / setter

Using getter / setter for dynamic object properties

我有一个名为 status 的对象,我想在其中跟踪 class.
的任何状态 除了设置各种状态外,我还想跟踪这些状态的活跃时间。现在不是为每个状态定义第二个 属性 来跟踪时间,这听起来像是 getter / setter 的工作。

这就是我被困的地方。我如何使它们动态化,以便它们触发 status 中的每个 属性?

var Person = function(options) {
  this.name = options.name;

  var _statusChanged = {};
  var _status = {};

  // How to make this dynamic?
  var expr = "isOnfire";
  this.status = {
    get [expr]() {
      console.log(_statusChanged);
      return _status[expr];
    },
    set [expr](val) {
      _status[expr] = val;
      _statusChanged[expr] = new Date();
      return _status[expr];
    }
  };
};

var John = new Person({
  name: "John"
});

John.status.isOnfire = true;
John.status.hasPinkShirt = true;

console.log(John, John.status.isOnfire, John.status.hasPinkShirt);

如果您有这些列表,只需在循环中创建 getters/setters,例如:

this.status = {};
["isOnFire", "hasPinkShirt"].forEach((name) => {
  Object.defineProperty(status, name {
    get() {
      console.log(_statusChanged);
      return _status[name];
    },
    set(val) {
      _status[name] = val;
      _statusChanged[name] = new Date();
      return _status[name];
    }
  });
});

如果它们可以是 任何东西,那么您会想要使用 Proxy object。使用代理,您可以捕获所有 gets/sets 而无需提前知道 属性 名称:

this.status = new Proxy(_status, {
    get(target, propKey, receiver) {
        // handle get
        return _status[propKey];
    },
    set(target, propKey, value, receiver) {
        // handle set
        _status[propKey] = value;
        _statusChanged[propKey] = new Date();
        return true; // Tells the proxy the assignment worked
    }
});

(或者您可以使用 Reflect.get and Reflect.set,但 Firefox 还没有它们。)

Here's an article 更详细地介绍代理。

这是一个示例,但您需要在最新版本的 Firefox 中 运行 它,因为 support or Proxy in the wild 地面上仍然很薄,而且根据它们的性质,您不能shim/polyfill 个代理。

(function() {
  "use strict";

  var _status = {};
  var _statusChanged = {};
  var status = new Proxy(_status, {
    get(target, propKey, receiver) {
      snippet.log(propKey + " requested");
      return _status[propKey];
    },
    set(target, propKey, value, receiver) {
      snippet.log(propKey + " set to " + value);
      _status[propKey] = value;
      _statusChanged[propKey] = new Date();
      return true; // Tells the proxy the assignment worked
    }
  });
  
  status.foo = "bar";
  snippet.log("foo = " + status.foo);

})();
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

在您可以使用它们之前,您需要将设置状态设置为方法调用,而不是赋值。

您需要一个名为 ECMAScript 6 的对象 Proxy。在 Firefox 中,它们默认打开。在某一时刻,它们在 Chrome 中在“experimental JavaScript" but they seem to have been removed temporarily; see this ES6 compatibility table.

下实施

此代码在 Firefox 中有效:

var output = function(text) {
      var line = document.createElement('div');
      line.innerHTML = text;
      document.getElementById('output').appendChild(line);
  }

var Person = function(options) {
  this.name = options.name;

  var _status = {};
  var _statusChanged = {};
  
  this.status = new Proxy(_status,{
    get: function(target,property) {
      return target[property];
    },
    set: function(target,property,value) {
      _statusChanged[property] = new Date();
      output("set " + property + " to " + value + " at " + _statusChanged[property]);
      _status[property] = value;
    }
  });
  
  this.show = function(property) {
    output("Property " + property + " is " + _status[property] + " since " + _statusChanged[property]);
  }
};

var John = new Person({
  name: "John"
});

John.status.isOnfire = true;
John.status.hasPinkShirt = true;

John.show("isOnfire");
John.show("hasPinkShirt");
<div id="output"></div>

也许这对你有用

http://jsfiddle.net/oksbLyqf/16/

var Person = function (options) {
    this.name = options.name;

    var _statusChanged = {};
    var _status = {};

    var expr = '';

    var addStatusProperty = function (prop) {
        expr = prop;

        Object.defineProperty(otherStatus, expr, {
            get: function () {
                console.log(_statusChanged);
                return _status[expr];
            },
            set: function (val) {
                _status[expr] = val;
                _statusChanged[expr] = new Date();
                return _status[expr];
            }
        });
    };

    var setStatusProperty = function (prop, val) {
        expr = prop;

        if (_status[expr]) {
            otherStatus[expr] = val;
            return _status[expr];
        } else {
            addStatusProperty(expr);
            otherStatus[expr] = val;
            return _status[expr];
        }
    };

    var getStatusProperty = function (prop) {
        expr = prop;

        return _status[expr]
    };

    this.status = {
        addProperty: addStatusProperty,
        setProperty: setStatusProperty,
        getProperty: getStatusProperty
    };

    var otherStatus = this.status;
};

var John = new Person({
    name: "John"
});

John.status.setProperty('isOnfire', true);
John.status.setProperty('hasPinkShirt', true);

console.log(John, John.status.getProperty('isOnfire'), John.status.getProperty('hasPinkShirt'));