第十一章 不是你的对象不要动
JS独特之处就是任何东西的都不是神圣不可侵犯的,默认情况下,你可以修改任何你能触及到的对象。那么,在一个大型项目之中,对象的修改变成了一个大问题。
11.1 什么是你的
当你的代码创建了这些对象,那么你拥有这些对象。维护是你的责任。牢记,如果你的代码没有创建这些对象,那么不要修改它们。
1. 原生对象。 2. DOM对象 3. 浏览器对象模式对象(BOM)。 4. 类库对象。
11.2 原则
在JS中,我们将已经存在的对象视为一种背景,在此之上开发代码,应该将JS对象当做一个实用的工具函数来看待。
不覆盖方法,不增加方法,不删除方法。
11.2.1 不覆盖方法
JS有史以来最糟糕的实践就是 覆盖一个非自己拥有的对象。(作者说在 Yahoo 便遇到了很多此类问题) 。
神圣的 document.getElementById 都可以被轻易覆盖。
11.2.2 不新增方法
几乎无法阻止你向任何对象新添方法,为对非自己的对象添加新方法是一个大问题,命名冲突。虽然对象目前可能没有该方法,但是不能保证未来也没有。更糟糕的是,一旦未来的原生方法和你的方法行为不一致,那么你整个代码将陷入维护的噩梦。
Prototype库就是一个不好的例子,很随意的修改了大量的原生对象和方法。导致在JS的历史上遇到很多问题。
在小于1.6的版本中,Prototype定义了一个document.getElementsByClassName() 方法,当然在很早以前这完全没有问题,因为本身还没有该方法,随后当在H5中官方定义后,其团队不得不加上防守性的代码
if( !document.getElementsByClassName ){
document.getElementsByClassName = function(){
// 非原生实现
}
}
然而,之前的Prototype所使用的方法结果是可以使用 each的,H5定义的可没有 each。所以结果可想而知。
11.2.3 不删除方法
删除JS方法其实和新增一个方法一样简单,当然覆盖也算删除的一种。不管方法有多糟糕,我们都不容易知道是否真的没有代码使用它,很多库都如此,即使很糟糕,在版本更新中也不会立即删除掉。
11.3 更好的途径
修改非自己拥有的对象是解决某些问题的很好的方案。可能有一些方法,所谓的设计模式,不直接修改这些对象而是扩展这些对象。
最受欢迎的对象扩充方式就是继承。一种类型的对象如果已经做到了你想要的大多数工作,那么继承,再加一些新的功能即可。JS中有两种基本形式:基于对象的继承和基于类型的继承。
11.3.1 基于对象的继承
基于对象的继承也就是原型继承,即ES5中的 Object.create() 方法。
var person = {
name : "Nicholas",
sayName : function(){
alert( this.name );
}
}
var ni = Object.create( person );
当然可以扩展对象
var ni = Object.create( person, {
name : {
value : "Greg"
}
} );
一旦以这种方式创建了对象,那么你可以随意修改新对象,毕竟你是对象的拥有者。
11.3.2 基于类型的继承
基于类型的继承是通过构造函数而非对象
function MyError( message ){
this.message = message;
}
MyError.prototype = new Error();
11.3.3 门面模式
门面模式是一种流行的设计模式,他为一个已经存在的对象创建新的接口,门面模式是一个全新的对象,背后是一个已经存在的对象在工作。如果你的用例中无法使用继承满足需要,可以使用门面模式。
funtion DOMWrapper( element ){
this.element = element;
}
DOMWrapper.prototype.addClass( className ){
this.element.classmName += " " + className;
}
var wrapper = new DOMWrapper( document.getElementBuId("myDiv") );
wrapper.addClass("selected");
从JS的可维护性来说,门面模式是非常合适的方式,你可以完全控制这些接口。你可以允许访问任何底层对象的方法或者属性,反之也可以有效地过滤对象的访问。
门面实现一个特定的接口,让一个对象看上去像另一个对象就是适配器。两者的差别就是前者创建接口,后者实现已经存在的接口。
11.4 关于Polyfill的注解
随着ES5和H5的特性在各个浏览器中实现,JS polyfills(shims)开始就行起来,polyfill是指一种功能的模拟,这些功能在新版本的浏览器中已经实现,然后用自定义的方式使其在老版本中兼容实现。
11.5 阻止修改
在ES5中已经引入了几个方法来防止对对象的修改。
有三种级别: 防止扩展, 密封, 冻结。
var person = {
name : "Nicholas"
}
Object.preventExtension(person);
person.age = 25;
就会悄悄的失败,如果是在 strict 的情况下会报错。
第十二章 浏览器嗅探
浏览器嗅探始终是web领域的一个热门话题。
12.1 User-Agent 检测
用户代理检测是更具客户端浏览器的 user-agent 字符串进行检测,但是最大的问题就是解析 user-agent 并不是很容易,有些浏览器为了保证兼容性,会复制另一个浏览器的 user-agent 字符串,每当一个新的浏览器出来的时候我们都需要对 user-agent 检测的代码进行重新修正。意味着我们无法预期的出了问题,只能等待问题出现后再进行处理。
如果你选用了 user-agent 检测,那么最安全的方式就是只去检测就浏览器,比如只针对 IE8及以下浏览器等。而不要判断比如 IE 的整个系列等。
12.2 特性检测
特性检测的原理就是为特定浏览器的各个特性进行检测,并当特性存在时处理问题。
比如早期的根据ID获取元素的特性检测
function getById( id ){
var element = null;
if( document.getElementById ){
element = document.getElementById( id );
}else if( document.all ){
element = document.all[id];
}else if( document.layers ){
element = document.layers[id];
}
return element;
}
特性检测的重要组成部分
1. 探测标准方法
2. 探测不同浏览器的特点方法
3. 当被检测方法不存在的时候提供合乎逻辑的备用方案。
12.3 避免特性推断
特性推断是更具一个特性的存在,推断另外一个特性是否存在。“如果他看起来像鸭子,就必定会像鸭子一样嘎嘎叫”
12.4 避免浏览器推断
12.5 如何取舍
特性推断和浏览器推断是非常糟糕的做法,应当不惜一切代价避免他,纯粹的使用特性检测是一种很好的方式,几乎在任何情况下都是你想要的结果。
对于 user-agent 检测,从来都不禁止他,因为总有非常适合的场景需要使用他。
阅读(1769) | 评论(0) | 转发(0) |