javaScript设计模式探究【1】

    这段时间,有空没空的看了一下博客园里一个博友-汤姆大叔的深入理解javascript系列,也挺有收获的,因为面试的临近,感觉自己唯一的优势可能就是javascript这一块了,所以就加强加强,去图书馆借了一本javascript设计模式,挺薄的一本书,放在书架上挺不显眼的,书架上各种外表光鲜亮丽的javascript书,都看的凌乱了,只是看到设计模式那四个大字,所以就借了来,刚开始只是那么随意的翻了那么一两章,就令我有一种豁然开朗之感,远比书架上那些看似很牛逼的书好的多,现在看书的口味也开始有点叼了,书怎么样,翻一翻就能感受出来。后来一看是图灵程序设计丛书,一时觉得外国人果然牛逼,那个翻译也挺用功的,有些地方斟酌的很仔细,推荐一下《javascript设计模式》---Ross harmes  Dustin Diaz著

         现在也发现个问题,看的书如果不做一些笔记,过了一段时间,就没什么印象了,倒是有点习惯这种一边看书,然后一边总结发博文,跟大家分享一下的做法了。其实要成为javascript大牛这条路挺崎岖的,个人感觉比成为C++/Java这条路更困难,因为由于脚本语言的定位,本身让它去实现非常复杂的逻辑功能就不太现实,即使有,也不是很容易能接触到的,另外由于它的灵活性,所以掌控它就不显得那么简单了,总的来说需要经验,还需要能够给你一个平台。其他语言学起来难道不累吗?也累吧,语言并不能代表什么,要更多的往深层次的东西思考,例如设计模式。当然像我这样的菜鸟,要走的路还有很长很长,加油,一步步来呗。总有爬到高出的时候。

        

View Code
//1.javascript里最简单,最常见的完成任务方式
function startAnimation(){
        ...
}
function stopAnimation(){
        ...    
}

//2.使用prototype

var Anim = function(){
        ...
};
Anim.prototype.start = function(){
        ...
}
Anim.prototype.stop = function(){
        ...    
}

//3.将类的定义封装在一条声明中
var Anim = function(){
    ...    
}
Anim.prototype = {
    start: fucntion(){
        ...    
    }    ,
    stop: function(){
        ...    
    }
};

//4.使用Function.prototype.method用于为类添加新方法
Function.prototype.method = function(name,fn){
    this.prototype[name] = fn ;
};
var Anim = function(){
    ...
};
Anim.method('starrt',function(){
    ...    
});
Anim.method('stop', function(){
    ...
});

//5.链式调用
Function.prototype.method = function(name,fn){
    this.prototype[name] = fn ;
    return this;
};
var Anim = function(){
    ...
};
Anim.method('starrt',function(){
    ...    
}).
method('stop', function(){
    ...
});

   不知道大家看完上面的5例代码有什么感受,反正我觉得挺惊艳的,可能语法什么的我都学过,但是从没有想过可以这么用。尤其是第五种,记得不错的话,JQuery应该大量的用到了这种链式调用的方式。

        1 接口

       在javascript中模仿接口的三种方法:注释法,属性检查法和鸭式辨型法

        1.1 注释法--用注释法最简单,但是效果最差,只是增加了一段注释而已,对性能没有影响,但是没有错误检查。

View Code
/*
interface Composite{
    function add(child);
    function remove(child);
    functiongetChild(index);
}
interface FormItem{
    function save();
}
*/
var CompositeForm = function(id, method,action){//iimplements Composite, FormItem
    ...
};
//Implement the Composite interface
CompositeForm.prototype.add = function(child){
    ...    
};
CompositeForm.prototype.remove = function(child){
    ...
};
CompositeForm.prototype.getChild = function(index){
    ...
};
//Implement the FormItem interface
COmpositeForm.prototype.save = function(){
    ...
};

   1.2 属性检查模仿接口--所有类都明确地声明自己实现了哪些接口,任何一个要求其啊承诺书属于特定类型的函数都可以对这个属性进行检查,并在所需接口未在声明之列时抛出一个错误,但是这种方法并未确保类真正实现了自称实现的接口。需要一些额外工作,对性能略有影响。

View Code
/*
interface Composite{
    function add(child);
    function remove(child);
    functiongetChild(index);
}
interface FormItem{
    function save();
}
*/
var CompositeForm = function(id,method,action){
    this.implementsInterfaces = ['Composite', 'FormItem'];
    ...    
};

function addForm(formInstance){
    if(!implements(formInstance,'Composite','FormItem')){
        throw new Error("Object does not implements a required interface:");
    }    
}
//The implememts function, which checks to see if an object declares that it
//implements the required interfaces.
function implements(object){
    for(var i = 1; i<arguments.length;i++){
        var interfaceName = arguments[i];
        var interfaceFound = false;
        for(var j =0;j<object.implementsInterfaces.length;j++){
            if(object.implementsInterfaces[j] == interfaceName){
                interfaceFound = true;
                break;
            }    
        }
        if(!interfaceFound){
            return false;
        }
    }    
    return true;
}

   1.3 鸭式辨型模仿接口--类是否声明自己支持哪些接口并不重要,只要它具有这些接口中的方法就行。定义:如果对象具有与接口定义的方法同名的所有方法,那么就可以认定它实现了这个接口。

View Code
//Interfaces
var Composite = new Interface('Composite',['add','remove','getChild']);
var FormItem = new interface('FormItem',['save']);

//CompositeForm class
var CompositeForm = function(id, method, action){
    ...    
};

function addForm(formInstance){
    ensureImplements(formInstance, Composite, FormItem);    
}

//ensureImplements函数至少需要两个参数,第一个参数是想要检查的对象,其余参数是据以对那个对象进行检查的接口。

      1.4 具体实现方法---综合第一种和第三种,用注释声明类支持的接口,提高代码的可重用性及文档的完整性。我们还用辅助类Interface及其类方法Interface.ensureImplements来对对象实现的方法进行显式检查。

View Code
//Interfaces
var Composite = new Interface('Composite',['add','remove','getChild']);
var FormItem = new interface('FormItem',['save']);

//CompositeForm class
var CompositeForm = function(id, method, action){
    ...    
};

function addForm(formInstance){
    ensureImplements(formInstance, Composite, FormItem);    
}

var interface = function(name,methods){
    if(Arguments.length != 2){
        throw new Error("Interface constructor called with:"+Arguments.length+"arguments, but expected exactly 2.");
    }
    this.name = name;
    this.methods = [];
    for(var i =0,len = methods.length;i<len;i++){
        if(typeof methods[i] != 'string'){
                throw new Error("Interface constructor expects method names to be passed in as a string.");
        }    
        this.methods.push(methods[i]);
    }
};
interface.ensureImplements = function(Object){
    if(Arguments.length<2){
        throw new Error("Function Interface.ensureImplements called with"+Arguments.length+"arguments,but expected at least 2.");    
    }    
    for(var i =1,len = Arguments.length;i<len;i++){
        var interface = Arguments[i];
        if(interface.constructor != Interface){
            throw new Error("Function interface.ensureImplements expects arguments two and above to be instances of Interface.");    
        }    
        for(var j = 0,methodsLen = interface.methods.length;j<methodsLen;j++){
            var method = interface.methods[j];
            if(!Object[method]||typeof Object[method]!=='function'){
                throw new Error("Function Interface.ensureImplements:object does not implement the"+interface.name+"inerface.Method"+method+
                "was not found.");    
            }    
        }
    }
};

  1.5 总结是否需要使用接口

        仁者见仁智者见智,对于一些小型的,不太费事的项目,就没必要使用了。具体实现的时候,还可以简化。使用公共的Interface.js文件,剔除所有针对构造器的显式检查,以Interface.ensureImplements取代原来的构造器检查。

     2 封装和信息隐藏

      javascript中创建对象的基本模式有3种。门户大开型,使用下划线来表示私有方法和属性,使用闭包来创建真正的私有成员。

  2.1 门户大开型---所有的属性和方法都是公开的,可访问的(就不举例了)

       2.2 用命名规范区别私有成员---本质上来说这种模式与门户大开型对象创建模式如出一辙,只不过在一些方法和属性的名称前加上了下划线以示其私用性而已。

       //this._menber = member;

     //_method = function(){}               但是这样外部其实还是可以访问的。

     2.3 作用域、嵌套函数和闭包

     在javascript中只有函数具有作用域。闭包的好处就是可以实现在函数外部调用内部的变量。返回一个内嵌函数是创建闭包的最常用的手段。

View Code
function foo(){
    var a = 10;
    function bar(){
        a * = 2;
        return a;
    }
    return bar;    
}
var baz = foo();      
baz(); //return 20
baz(); //return 40
baz(); //return 80

var blat = foo();
blat();          //return 20   because a new copy of a is being used

     上面这段代码其实涉及到共享变量的问题,var baz = foo() 声明一个函数bar的引用。使用baz的时候共享变量a,具体的可以看深入理解JavaScript系列(10):JavaScript核心(晋级高手必读篇)里面有涉及到,讲的挺详细的。

     2.4 总结封装利弊:

           封装保护了内部数据的完整性,通过将数据的访问途径限制为取值器和赋值器这两个方法可以获得对取值和赋值的完全控制,可以减少其它函数所需的错误检查代码的数量。 对   象重构因此变得轻松。通过只公开在那些接口中规定的方法,可以弱化模块间的耦合。

           封装导致了复杂的作用域链。学习起来挺困难的,新手容易迷糊。

      3 继承--在javascript中继承是非常复杂的,比其他任何面向对象语言中的继承都要复杂...下次再总结吧,我感觉就继承,我能写一篇博文了。没事的话,明天继续

       以上全部都属个人原创,请大家转载的时候附上原创链接: http://www.cnblogs.com/tonylp

原文地址:https://www.cnblogs.com/tonylp/p/2981456.html