覆盖没有defineProperty的对象getter

时间:2015-04-02 22:17:42

标签: javascript ecmascript-5

我已使用Object.defineProperty使用getter定义了一个属性,我希望能够覆盖它而无需使用defineProperty来防止对将要使用它的同事造成任何混淆但可能不知道这不是一个“正常”的财产。我尝试了writeableconfigurable选项无济于事。

var fixture = {
  name: 'foo',
  color: 'green'
};

Object.defineProperty(fixture, 'email', {
  get: function () {
    return 'test+' + Date.now() + '@gmail.com';
  },
  configurable: true,
  writeable: true
});

console.log(fixture.email); // test+<date>@gmail.com
fixture.email = 'bob'; // consumer attempts to overwrite
console.log(fixture.email); // test+<date>@gmail.com T_T

// this _will_ modify the property
Object.defineProperty(fixture, 'email', {
  value: 'overwritten'
});
console.log(fixture.email);

2 个答案:

答案 0 :(得分:3)

  

我尝试过可写和可配置的选项无济于事。

writable仅适用于数据属性,即具有value非getter和setter的数据属性。

configurable仅允许删除和重新定义,而不是使属性可设置。

如果您希望fixture.email = 'bob';不抛出错误,则必须在属性对象上提供set方法。这个方法现在可以:

  • 忽略新值
  • 存储新值,例如在不同的属性或闭包变量中,以便getter可以在后续访问中产生它(@Icepickle在他的回答中有一些这样的例子)
  • 将访问者属性转换回“正常”属性

最后一个可能是最简单,最有价值的选择:

var fixture = {
  name: 'foo',
  color: 'green'
};

Object.defineProperty(fixture, 'email', {
  get: function() {
    return 'test+' + Date.now() + '@gmail.com';
  },
  set: function(val) {
    Object.defineProperty(this, 'email', {
      value: val,
      writable: true
    });
  },
  configurable: true,
  enumerable: true
});

console.log(fixture.email); // test+<date>@gmail.com
fixture.email = 'bob'; // consumer attempts to overwrite
console.log(fixture.email); // bob

答案 1 :(得分:1)

虽然,我不会亲自使用它,但我猜你可以解决这个问题,只有当价值特定于你的期望时才设置它,如果它不是正确的值,抛出某种错误?

缺点当然是,一旦他们看到代码就像你那样放入代码,他们也可以这样做,但我想他们已经不那么混淆了;)我实际上更喜欢第二种选择我在底部显示,它只是提供了一个额外的setEmail方法来进行设置,或者通过更新&#34; DataContainer&#34;

内部的props.email属性。

&#13;
&#13;
var data = {
  foo: 'foo',
  bar: 'bar'
};

Object.defineProperty(data, 'email', {
  get: function() {
    if (typeof this._props === 'undefined') {
      this._props = {};
    }
    return this._props.email;
  },
  set: function(val) {
    if (!val || !val.private) {
      throw 'no access exception';
    }
    if (this.email === val) {
      return;
    }
    this._props.email = val.email;
  },
  configurable: false
});

console.log(data.email);
data.email = {
  private: 1,
  email: 'hey.you@somewhere.hi'
};
console.log(data.email);
try {
  data.email = 'not allowed to set';
} finally {
  console.log(data.email);
}
&#13;
&#13;
&#13;

另一种方法可能只是将逻辑构建到类构造函数中,并在函数上添加一个potentail setter(或在变量内部添加)

&#13;
&#13;
function DataContainer(options) {

  var createProp = function(obj, propertyName, propertyHolder, isReadOnly) {
      Object.defineProperty(obj, propertyName, {
        get: function() {
          return propertyHolder[propertyName];
        },
        set: function(val) {
          if (isReadOnly) {
            return;
          }
          propertyHolder[propertyName] = val;
        },
        configurable: false
      });
    
      if (isReadOnly) {
        obj['set' + propertyName[0].toUpperCase() + propertyName.substr(1)] = function(val) {
           //internal setter
          propertyHolder[propertyName] = val;
        };
      }
    },
    prop = {},
    fieldProp, fieldVal, ro;

  if (options && options.fields) {
    for (fieldProp in options.fields) {
      if (options.fields.hasOwnProperty(fieldProp)) {
        fieldVal = options.fields[fieldProp];
        ro = false;
        if (typeof fieldVal === 'object' && fieldVal.value) {
          ro = fieldVal.readOnly || false;
          prop[fieldProp] = fieldVal.value;
        } else {
          prop[fieldProp] = fieldVal;
        }
        createProp(this, fieldProp, prop, ro);
      }
    }
  }
}

var data = new DataContainer({
  fields: {
    foo: 'foo',
    bar: 'bar',
    email: {
      value: 'test@something.com',
      readOnly: true
    }
  }
});

console.log(data);

data.email = 'test@test.com';
console.log(data.email);
data.setEmail('test@test.com');
console.log(data.email);
&#13;
&#13;
&#13;