在Javascript里配置对象属性是可以实现的,比如将一个参数设为伪私有或者只读。这个特性从ECMAScript 5.1开始就可以使用了,因此近来的浏览器都是支持的。 要实现这些功能,你需要使用Object的原型方法defineProperty,像这样:

var a = {};
Object.defineProperty(a, 'readonly', {
  value: 15,
  writable: false
});

a.readonly = 20;
console.log(a.readonly); // 15

语法如下:

Object.defineProperty(dest, propName, options);

或者定义多个:

Object.defineProperties(dest, {
  propA: optionsA,
  propB: optionsB, //...
});

options包含如下的属性:

  • value: 如果参数不是getter(请看下文),value是必须的。{a: 12} === Object.defineProperty(obj, 'a', {value: 12})
  • writable: 将参数设为只读。需要注意的是如果参数是嵌套对象,它的元素仍是能可修改的。
  • enumerable: 将参数设为隐藏。这意味着for ... of循环和stringify的结果里不会包含这些参数,但是这个参数还是存在的。提示:这并不意味着参数是私有的!他依旧可以从外界访问,只是意味着不会被打印。
  • configurable: 将属性设置为不能更改,比如:防止参数被删除或重新定义。如果此对象是一个嵌套对象,他的参数依旧是可配置的。

所以如果想创建私有静态变量,你可以这样定义:

Object.defineProperty(obj, 'myPrivateProp', {value: val, enumerable: false, writable: false, configurable: false});

除了配置属性,由于defineProperty第二个参数是字符串,所以允许我们定义动态变量(defineProperty)。例如,我们可以说我们要根据一些外部配置创建一个属性:


var obj = {
  getTypeFromExternal(): true // illegal in ES5.1
};

Object.defineProperty(obj, getTypeFromExternal(), {value: true}); // ok

// For the example sake, ES6 introduced a new syntax:
var obj = {
  [getTypeFromExternal()]: true
};

还没有结束!高级特性允许我们创建gettersetter,就像其他面向对象(OOP)语言!这种情况下,我们不能使用writableenumerableconfigurable参数,而是:

function Foobar () {
  var _foo; //  true private property

  Object.defineProperty(obj, 'foo', {
    get: function () { return _foo; }
    set: function (value) { _foo = value }
  });

};

var foobar = new Foobar();
foobar.foo; // 15
foobar.foo = 20; // _foo = 20

除了封装与先进的访问器这些明显的优点,你还发现我们并没有“调用”getter,而是不需要使用小括号直接“取得”了属性!这太棒了!例如,我们可以想象我们有一个多层嵌套对象,像这样:

var obj = {a: {b: {c: [{d: 10}, {d: 20}] } } };

现在我们不需要调用a.b.c[0].d(其中某个属性可能是undefined且抛出错误),我们可以创建一个别名:

Object.defineProperty(obj, 'firstD', {
  get: function () { return a && a.b && a.b.c && a.b.c[0] && a.b.c[0].d }
});

console.log(obj.firstD); // 10

提示

如果你定义了getter而没有定义setter却仍要给它赋值,你将会得到一个错误。这在使用像$.extend_.merge这样的辅助方法时是尤为重要的。要小心!

链接