默认的情况下,js的所有对象都是可以扩展的,也就是说,任何时候都可以向对象中添加属性和方法。在开发过程中,很可能会意外的修改别人的代码,因此,需要将不让修改的对象保护起来。
保护对象: 阻止程序对一个对象执行不合理的修改
两个层面的保护:
一. 保护单个属性: 阻止对对象中某个属性执行不合常理的修改
1、ES3和ES5中主要通过闭包来对属性进行保护
function () {
var data = {
name : '张三',
sex: 'man',
age: 18
}
// 向外部暴露可以访问内部属性的方法
this.get = function (key) {
return data[key]
}
this.set = function (key, value) {
if(key != 'sex') { // 对需要保护的属性设置不可更改
data[key] = value
}
}
}
2、ES5还可以通过Object.defineProperty来通过修改属性默认特性来保护属性
Object.defineProperty(Person, 'sex', {
value: //实际存储属性值
writable: true/false, //是否可以修改 默认是true
enumerable: true/false, //是否可被for in遍历 仅仅只能控制 for in 无法控制访问
configurable: true/false, //是否可修改其他属性特征 是否可删除该属性
}
3、ES6可以通过 Proxy 来保护对象
let Person = {
name: 'ES6',
age: 15,
sex: 'male'
}
// 使用代理对原对象进行封装,生成特殊的代理对象
// 代理对象成为了代码交互的主要对象,而实际目标对象处于被保护的状态
let person = new Proxy(Person, {
get (target, key) {
return target[key]
},
set (target, key, value) {
if(key != 'sex') {
target[key] = value
}
}
})
二. 保护对象结构: 阻止乱添加删除属性
1、不可扩展,即禁止给对象添加新属性和方法:Object.preventExtensions(对象);
var student = { name: "王五" };
Object.preventExtensions(student);
student.age = 26;
alert(student.age); //undefined
在非严格模式下,给对象添加新成员会导致静默失败,因此student.age 将是 undefined。
在严格模式下,尝试给不可扩展的对象添加新成员会导致抛出错误。虽然不能给对象添加新成员,但已有的成员则丝毫不受影响。已有的成员仍然可以修改和删除。使用 Object.istExtensible()方法可以确定对象是否可以扩展。
var student = { name: "王五" };
alert(Object.isExtensible(student)); //true
Object.preventExtensions(student);
alert(Object.isExtensible(student)); //false
2、禁止添加、删除属性和方法,属性值可以修改(密封对象):使用Object.seal()方法。
var student = { name: "王五" };
Object.seal(student);
student.age = 26;
alert(student.age); //undefined
delete student.name;
alert(student.name); //"王五"
上面的代码执行过程中,添加 age 属性的行为被忽略了。而删除 name 属性的操作也被忽略了,因此这个属性没有受任何影响。这是在非严格模式下的行为。在严格模式下,尝试添加或删除对象成员都会导致抛出错误。
使用 Object.isSealed()方法可以确定对象是否被密封了。因为被密封的对象不可扩展,所以用Object.isExtensible()检测密封的对象也会返回 false。
3、不可拓展、密封且不可修改(冻结对象),ES5的Object.freeze()方法可以用来冻结对象。
冻结对象数据属性的Writable特性会被设置为 false。由于访问器属性没有writable,所以访问器属性还是可以用的。
var student = { name: "王五" };
Object.freeze(student);
student.age = 26;
alert(student.age); //undefined
delete student.name;
alert(student.name); //"王五"
student.name = "李四";
alert(student.name); //"王五"
与不可拓展和密封一样,在非严格模式下对冻结的对象执行非法操作会被忽略,而在严格模式下会抛出错误。
可以使用 Object.isFrozen()方法用于检测冻结对象。因为冻结对象既是密封的又是不可扩展的,所以用 Object.isExtensible()和 Object.isSealed()检测冻结对象将分别返回 false和 true。
var student = { name: "王五" };
alert(Object.isExtensible(student)); //true
alert(Object.isSealed(student)); //false
alert(Object.isFrozen(student)); //false
Object.freeze(student);
alert(Object.isExtensible(student)); //false
alert(Object.isSealed(student)); //true
alert(Object.isFrozen(student)); //true