只要维护代码是你的责任,那么你就拥有这些对象。数组
若是你的代码没有建立这些对象,不要修改它们,包括:浏览器
原生对象(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 的锁定修改的保护功能。