在ECMAScript 5里面,可以通过(writable 和 configurable)内部属性把属性设置为不可修改和不可删除的,可以通过(Object.preventExtensions() )让对象不允许被拓展新的属性,可以通过(Object.freeze())让对象的所有属性变为只读和不可删除的,同时也不能添加新属性,或者(Object.seal() )等。
"use strict";
var person = {
name: "Nicholas"
};
Object.seal(person);
person.age = 20; // Error!
上面的代码会触发错误,因为对象被密封了,不允许添加新属性
但是在非strict模式下,不会出现错误提示,即使属性无法添加,也不会有任何提示(通常访问不存在的对象属性只会返回undefined),这就会在调试上带来麻烦
当读从对象读取一个属性值的时候,会触发一个内部的[[Get]]操作来获取属性值,这是一个内部的不可改变的行为,但是通过 proxies 可以拦截javascript engine对[[Get]]的调用,转而调用自定义的函数
var proxy = new Proxy({ name: "Nicholas" }, {
get: function(target, property) {
if (property in target) {
return target[property];
} else {
return 35;
}
}
});
console.log(proxy.time); // 35
console.log(proxy.name); // "Nicholas"
console.log(proxy.title); // 35
改造一下便可以成为一个让对象不存在属性被访问时触发错误的函数
function createDefensiveObject(target) {
return new Proxy(target, {
get: function(target, property) {
if (property in target) {
return target[property];
} else {
throw new ReferenceError("Property \"" + property + "\" does not exist.");
}
}
});
}
使用方式:
var person = {
name: "Nicholas"
};
var defensivePerson = createDefensiveObject(person);
console.log(defensivePerson.name); // "Nicholas"
console.log(defensivePerson.age); // Error!
其他常规操作不受影响
defensivePerson.age = 13;
console.log(defensivePerson.age); // 13
console.log("name" in defensivePerson); // true
console.log(defensivePerson.hasOwnProperty("name")); // true
通过对设置内部[[Set]],可以让属性添加的时候判断类型
function createTypeSafeObject(object) {
return new Proxy(object, {
set: function(target, property, value) {
var currentType = typeof target[property],
newType = typeof value;
if (property in target && currentType !== newType) {
throw new Error("Property " + property + " must be a " + currentType + ".");
} else {
target[property] = value;
}
}
});
}
上面两个方法都可以安全用到构造函数里面,因为proxies是透明的,不影响其他代码
function Person(name) {
this.name = name;
return createTypeSafeObject(this);
}
var person = new Person("Nicholas");
console.log(person instanceof Person); // true
console.log(person.name); // "Nicholas"