如何禁止JavaScript对象重写?
翻译 | Fundebug
不要重写他人的对象
// 示例代码1
window.originalAlert = window.alert;
window.alert = function(msg) {
if (typeof msg === "string") {
return console.log(msg);
}
return window.originalAlert(msg);
};
alert('ooh so awesome'); // 参数为字符串时,打印到控制台
alert(3.14); // 参数为其他类型时,弹出对话框
在示例代码1中,我修改了windows.alert:参数为字符串时,打印到控制台;参数为其他类型时,弹出对话框。这样的修改显然会影响其他使用alert方法的开发者。
如果你修改的是DOM对象比如getElementById(),这会导致非常严重的后果。
如果你只是为对象添加新的方法,这也会导致问题。
// 示例代码2
Math.cube = function(n) {
return Math.pow(n, 3);
};
console.log(Math.cube(2)); // 8
这样做最大的问题是有可能在未来导致命名冲突。尽管Math对象目前并没有cube方法,下一个版本的JavaScript标准也许会增加cube方法(当然可能性不大),这就意味着我们会把原生cube方法给替代了。
有一个真实的案例,Prototype库定义了document.getElementsByClassName()方法,而这个方法后来被加入了JavaScript标准。
不幸的是,我们无法阻止其他开发者重写我们定义的对象,这时我们就需要本文介绍的这些方法了:
首先,我们不妨通过一个表格对比一下Object.preventExtensions()、Object.seal()和Object.freeze():
方法 | 禁止增加属性 | 禁止删除属性 | 禁止修改属性 |
---|---|---|---|
Object.preventExtensions() | 是 | 否 | 否 |
Object.seal() | 是 | 是 | 否 |
Object.freeze() | 是 | 是 | 是 |
Object.preventExtensions()
使用Object.preventExtensions(),可以禁止给对象添加新的方法或者属性。注意,修改或者删除对象已经存在的方法或者属性是没有问题的。使用Object.isExtensible()可以查看某个对象是否可以增加方法或者属性。
// 示例代码3
var song = {
title: 'Hope Leaves',
artist: 'Opeth'
};
console.log(Object.isExtensible(song)); //true
Object.preventExtensions(song);
console.log(Object.isExtensible(song)); //false
song.album = 'Damnation';
console.log(song.album); // undefined
song.play = function() {
console.log('ahh soo awesome');
};
song.play(); // TypeError: song.play is not a function
由示例代码3可知,执行Object.preventExtensions()之后,为song对象新增album以及play方法都失败了!
但是,当我们为song新增属性或者方法时,并没有报错。当我们使用了”use strict”采用严格模式时,情况就不一样了:
// 示例代码4
"use strict";
var song = {
title: 'Hope Leaves',
artist: 'Opeth'
};
Object.preventExtensions(song);
song.album = 'Damnation'; // Uncaught TypeError: Cannot add property album, object is not extensible
在严格模式下,给已经Object.preventExtensions的对象新增属性时,会立即报错。
Object.seal()
使用Object.seal(),可以禁止给对象添加属性或者方法(这一点与Object.preventExtension()的作用一致),同时禁止删除对象已经存在的属性或者方法。
// 示例代码5
"use strict"
var song = {
title: 'Hope Leaves',
artist: 'Opeth'
};
Object.seal(song);
console.log(Object.isExtensible(song)); //false
console.log(Object.isSealed(song)); //true
song.album = 'Damnation'; // Uncaught TypeError: Cannot add property album, object is not extensible
delete song.artist; // Uncaught TypeError: Cannot delete property 'artist' of #