如何禁止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+
相關文章
相關標籤/搜索