只要維護代碼是你的責任,那麼你就擁有這些對象。數組
若是你的代碼沒有建立這些對象,不要修改它們,包括:瀏覽器
原生對象(Object、Array 等)安全
DOM 對象(例如,document)app
瀏覽器對象模型(BOM)對象(例如,window)函數
類庫的對象
工具
把已存在的 JavaScript 對象如一個實用工具函數庫同樣來對待。this
不覆蓋方法spa
不新增方法prototype
不刪除方法插件
// 很差的寫法 document._orginalGetElementById = document.getElementById; document.getElementById = function(id) { if (id == "window") { return window; } else { return document._originalGetElementById(id); } };
在一個大型的項目中,一個此類問題會致使浪費大量時間和金錢。
爲非本身擁有的對象增長方法,會致使命名衝突。由於一個對象此刻沒有某個方法不表明它將來也沒有。若是未來原生的方法和你的方法行爲不一致,你將陷入一場代碼維護的噩夢。
大多數 JavaScript 庫代碼有一個插件機制,容許爲代碼庫安全地新增一些功能,這是最佳最可維護的途徑。
最簡單地刪除一個方法的方式就是將其賦值爲 null。
// 很差的寫法 - 刪除了 DOM 方法 document.getElementById = null;
也能夠用 delete 操做符來刪除對象的屬性或方法,但在 prototype 的屬性或方法上是不起做用的。
var person = { name: "Nicholas" }; delete person.name; console.log(person.name); // undefined
刪除一個已存在對象的方法是糟糕的實踐。
在 JavaScript 中有兩種基本的繼承方式:基於對象的繼承和基於類型的繼承。
在 JavaScript 中,繼承仍然有一些很大的限制:
不能從 DOM 或 BOM 對象繼承
繼承 Array 不能正常工做
也叫原型繼承。ECMAScript5 的 Object.create() 方法是實現這種繼承的最簡單的方式。
var person = { name: "Nicholas", sayName: function() { alert(this.name); } }; var myPerson = Object.create(person); myPerson.sayName(); // 彈出 "Nicholas"
從新定義 myPerson.sayName() 會自動切斷對 person.sayName() 的訪問。
Object.create() 方法能夠指定第二個參數,爲新對象添加新的屬性和方法:
var myPerson = Object.create(person, { name: { value: "Greg" } }); myPerson.sayName(); // 彈出 "Greg" person.sayName(); // 彈出 "Nicholas"
新對象能夠隨意修改。
基於類型的繼承是經過構造函數實現的,而非對象。
function MyError(message) { this.message = message; } MyError.prototype = new Error(); var error = new MyError("Something bad happened."); console.log(error instanceof Error); // true console.log(error instanceof MyError); // true
門面模式爲一個已存在的對象建立一個新的接口。門面有時也叫包裝器。
jQuery 和 YUI 的 DOM 接口都使用了門面。
function DOMWrapper(element) { this.element = element; } DOMWrapper.prototype.addClass = function(className) { element.className += " " + className; }; DOMWrapper.prototype.remove = function() { this.element.parentNode.removeChild(this.element); }; // 用法 var wrapper = new DOMWrapper(document.getElementById("my-div")); // 添加一個 className wrapper.addClass("selected"); // 刪除元素 wrapper.remove();
門面和適配器惟一的不一樣是前者建立新接口,後者實現已存在的接口。
polyfill 是對某種功能的模擬,這些功能在新版本的瀏覽器中有完整的定義和原生實現。例如 ECMAScript5 爲數組增長了 forEach() 函數。該方法在 ECMAScript3 中有模擬實現,這樣就能夠在老版本瀏覽器上使用這個方法了。
polyfills 的關鍵在於它們的模擬實現要與瀏覽器原生實現保持徹底兼容。爲了達到這個目的,polyfills 常常會給非本身擁有的對象新增一些方法。
從最佳的可維護性角度而言,避免使用 polyfills。
ECMAScript5 引入了幾個防止對象修改的方法。有三種鎖定修改的級別:
防止擴展:禁止爲對象「添加」屬性和方法,但已存在屬性和方法能夠被修改或刪除
密封:在防止擴展的基礎上,進一步禁止爲對象「刪除」已存在屬性和方法
凍結:在密封基礎上,進一步禁止爲對象「修改」已存在屬性和方法(全部字段均只讀)
var person = { name: "Nicholas" }; // 鎖定對象 Object.preventExtension(person); console.log(Object.isExtensible(person)); // false person.age = 25; // 正常狀況悄悄地失敗,除非在嚴格模式下拋出錯誤
// 密封對象 Object.seal(person); console.log(Object.isExtensible(person)); // false console.log(Object.isSealed(person)); // true delete person.name; // 正常狀況悄悄地失敗,除非在嚴格模式下拋出錯誤 person.age = 25; // 同上
// 凍結對象 Object.freeze(person); console.log(Object.isExtensible(person)); // false console.log(Object.isSealed(person)); // true console.log(Object.isFrozen(person)); // true person.name = "Greg"; // 正常悄悄地失敗,除非在嚴格模式下拋出錯誤 person.age = 25; // 同上 delete person.name; // 同上
若是決定將你的對象鎖定修改,強烈建議使用嚴格模式。
未來,原生 JavaScript 對象和 DOM 對象頗有可能都將統一內置使用 ECMAScript5 的鎖定修改的保護功能。