ES5 属性配置:writable,set/get,freeze,seal
ES5定义的Object.defineProperty()方法提供了面向对象实现中
『属性』的概念,类似于C#的属性(Property),或Java的访问器(Accessor)。
『属性』可以用来隐藏内部变量,实现写保护和读写钩子,从而加强对象封装。
ES5为此给出了一系列的对象属性管理方法,包括:Object.defineProperty
,
Object.preventExtensions
, Object.seal
, Object.freeze
等。
defineProperty参数
Object.defineProperty(obj, prop, descriptor)
用于在对象obj
上添加(或修改)
名为prop
的属性,该方法接受三个参数:
obj
:需要定义属性的对象prop
:属性名,字符串类型descriptor
:属性描述符,对象类型。
可以通过descriptor
可以精确地控制该属性的行为,
该描述符可以分为数据描述符和存取描述符两种。它们具有不同的属性,不可混用。
数据描述符类似于强类型编程语言中的属性修饰符,这种描述符可以有下列属性:
value
:属性值,默认为undefined
。writable
:为true
时才可被赋值运算符改变,默认为false
。
存取描述符类似于其他面向对象语言中的访问器,可以有下列属性:
get
:读访问器,返回值即为属性的值。默认为undefined
。set
:写访问器,该方法接受一个参数作为新的值,一般会将该值保存下来一共读访问器使用。默认为undefined
。
两种描述符都具有的属性包括:
configurable
:为true
时该属性才可被配置和删除,默认为false
。enumerable
:为true
时该属性才能出现在对象属性枚举中,默认为false
。
数据描述符示例
使用writable
属性可禁止属性的值被赋值运算符更改。
var post = {};
Object.defineProperty(post, "author", {
value : 'harttle',
writable : false
});
console.log(post.author); // harttle
post.author = 'another'; // 只在严格模式会抛出错误
console.log(post.author); // harttle,赋值不起作用
如果configurable
为true
,该属性还是可以被Object.defineProperty()
更改的:
var post = {};
Object.defineProperty(post, "author", {
value : 'harttle',
writable : false,
configurable: true
});
Object.defineProperty(post, "author", {
value : 'another',
});
post.author = 'another';
console.log(post.author); // another
enumerable
定义了对象属性是否可被枚举。值为false
时,for...in
将不会遍历到该属性。
存取描述符示例
该示例来自MDN:
function Archiver() {
var temperature = null;
var archive = [];
Object.defineProperty(this, 'temperature', {
get: function() {
console.log('get!');
return temperature;
},
set: function(value) {
temperature = value;
archive.push({ val: temperature });
}
});
this.getArchive = function() { return archive; };
}
var arc = new Archiver();
arc.temperature; // 'get!'
arc.temperature = 11;
arc.temperature = 13;
arc.getArchive(); // [{ val: 11 }, { val: 13 }]
禁止属性扩展
有些情况下我们希望禁止客户通过defineProperty()
进行属性扩展,
ES5提供了三级的限制:
Object.preventExtensions(obj)
: 禁止新的属性被添加到obj
中,直接的属性赋值也会失效。可通过Object.isExtensible(obj)
来检查。Object.seal(obj)
: 同上,属性也将不可被删除。等效于为所有属性设置configurable: false
。可通过Object.isSealed(obj)
来检查。Object.freeze(obj)
: 同上,属性也将不可被改变。等效于为所有属性设置writable: false
。可通过Object.isFrozen(obj)
来检查。
注意上述三个方法都是浅的(Shallow),即对象属性的
isExtensible
,isSealed
,isFrozen
不会发生变化。
参考阅读
- MDN-defineProperty: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
- MDN-seal: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal
- MDN-freeze: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
- MDN-preventExtensions: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/preventExtensions
- StackOverflow: http://stackoverflow.com/questions/18524652/how-to-use-javascript-object-defineproperty
本文采用 知识共享署名 4.0 国际许可协议(CC-BY 4.0)进行许可,转载注明来源即可: https://harttle.land/2016/07/26/es5-define-properties.html。如有疏漏、谬误、侵权请通过评论或 邮件 指出。