编写可维护的 JavaScript 笔记 (11

来源:互联网 发布:ubuntu 文档 编辑:程序博客网 时间:2024/05/18 03:15
编写可维护的 JavaScript 笔记 (11 - 12 章)2013-07-24 14:14:16

分类: JavaScript


第十一章 不是你的对象不要动

    
    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 检测,从来都不禁止他,因为总有非常适合的场景需要使用他。
0 0