如何禁止JavaScript对象重写?

译者按: 使用Object.preventExtensions()、Object.seal()和Object.freeze(),能够禁止重写JavaScript对象。javascript

因为JavaScript的灵活性,咱们能够轻易地重写(override)一些于其余人定义的对象(object)。换句话说,任何人均可以重写咱们所定义的对象。这是一个很是强大的特性,许多开发者都有兴趣试试,来拓展或者修改某些对象的行为。例如,DOM方法document.getElementById()均可以被重写。通常来说,咱们应该避免这样作,由于这会致使代码很难维护,而且会留下一些难于发现的BUG。ECMAScript 5引入了一些方法,容许开发者限制对象重写。若是你在开发一些工具库好比jQuery, fundebug等, 或者你的开发团队很是大,本文介绍的这些方法将很是有用。html

不要重写他人的对象

不要重写他人的对象,这是JavaScript的黄金法则。好比,当你重写了一个方法,则极可能这会影响依赖于该方法的库,这会让其余开发者很是困惑。java

// 示例代码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(),这会致使很是严重的后果。jquery

若是你只是为对象添加新的方法,这也会致使问题。windows

// 示例代码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标准。浏览器

不幸的是,咱们没法阻止其余开发者重写咱们定义的对象,这时咱们就须要本文介绍的这些方法了:bash

首先,咱们不妨经过一个表格对比一下Object.preventExtensions()、Object.seal()和Object.freeze():ide

方法 禁止增长属性 禁止删除属性 禁止修改属性
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方法都失败了!ui

可是,当咱们为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的对象新增属性时,会当即报错。广告:若是你但愿实时监控应用中相似的错误,欢迎免费试用Fundebug

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 #<Object>
复制代码

Object.freeze()

使用Object.freeze(),能够禁止为对象增长属性或者方法(这一点与Object.preventExtension()的做用一致),同时禁止删除对象已经存在的属性或者方法(这一点与Object.seal()的做用一致),另外还禁止修改已经存在的属性或者方法。

// 示例代码6
"use strict"
var song = {
    title: 'Hope Leaves',
    artist: 'Opeth',
    getLongTitle: function() {
        return this.artist + " - " + this.title;
    }
};

Object.freeze(song);

console.log(Object.isExtensible(song)); // false 
console.log(Object.isSealed(song)); // true 
console.log(Object.isFrozen(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 #<Object> 
song.getLongTitle = function() // Uncaught TypeError: Cannot assign to read only property 'getLongTitle' of object '#<Object>' {
    return "foobar";
};
复制代码

主流浏览器的最新版本都支持这些方法:

  • IE 9+
  • Firefox 4+
  • Safari 5.1+
  • Chrome 7+
  • Opera 12+
相关文章
相关标签/搜索