Javascript设计模式(1)

本文是学习了《JavaScript设计模式》(谢廷晟 译)做的学习笔记

一、JavaScript的灵活性

1. 普通 functon

    function startAnimation() {...}
    function stopAnimation() {...}

2. 类


    var Anim = function() {}   #构造函数

    # 方式一
        Anim.prototype.start = function() {...}
        Anim.prototype.stop = function() {...}

    # 方式二
        Anim.prototype = {
            start: function() {...},
            stop: function() {...}
        }

    # usage
        var myAnim = new Anim()
        myAnim.start()
        myAnim.stop()

3. 为实例创建方法


   # 方式一
    Function.prototype.method = function(name, fn) {
        this.prototype[name] = fn
    }
   # usage
    var Anim = function() {}
    Anim.method(‘start‘, function(){...})
    Anim.method(‘stop‘, function(){...})

   # 方式二 (链式)
    Function.prototype.method = function(name, fn) {
        this.prototype[name] = fn
        return this
    }
   # usage
    Anim.method(‘start‘, function(){...})
        .method(‘stop‘, function(){...})

4. 弱类型语言

  • 原始数据类型按值传递,其他数据类型按引用传递
  • javascript 类型系统可以分为标准类型对象类型,进一步标准类型又可以分为原始类型和引用类型,而对象类型又可以分为内置对象类型、普通对象类型、自定义对象类型。

  1. 原始类型(值类型):

    • Undefined undefined
    • Null null
    • Boolean true
    • String ‘hello‘
    • Number 123
  2. 引用类型(对象类型):
    • Object
    • Array
    • Data
    • RegExp
    • Function

二、接口

1. JavaScript 中模拟接口

注释描述接口

     # 只是使用注释说明接口

     /*

     interface Composite {
        function add (child)
        function remove (clild)
        function getChild(index)
     }
     interface FormItem {
        function save ()
     }

     */

使用属性检查模仿接口

假如我们要创建一个叫 CompositeForm 的类,并且它有两个方法 CompositeFormItem,我们可以在实现这个类的时候给它添加一个 implementsInterfaces 数组用于声明该类有哪些方法或属性,这样我们就能在需要的地方用统一的检测方法 implements 来检测是否该类作者已经实现了这个方法。

     var CompositeForm = function(id, method, action) {
        this.implementsInterfaces = [‘Composite‘, ‘FormItem‘];   //它自己说它实现了
     }

    // 该方法需要传入一个具有 Composite、FormItem 接口的对象,所以我们在里面使用 implements 方法进行检查
    function addForm(formInstance) {
        if(!implements(formInstance, ‘Composite‘, ‘FormItem‘)) {
            throw new Error("Object dost not implement a require interface.");
        }
        // 检查通过后进行其他处理
    }

    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
    }

    // 使用
     addForm(new CompositeForm())   //只能知道它是否‘说’自己实现了接口,并未确保类真正实现了接口

用鸭式辩型模仿接口

假如我们要实现一个 Composite 类,它继承了两个类的方法 Composite:[‘add‘, ‘remove‘, ‘getchild‘]FormItem:[‘save‘],那我们可以实现专门的类:Interface ,他的每个实例用于指定一些接口方法(有点像 Java 的接口类),它有一个公共的方法 ensureImplements 可以用来检测某个对象是否有实现对应的 Interface 实例指定的接口。

   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 call 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 + " interface. Method " + method + " was not found")
                }
            }
        }
     }

    // 创建两个 Interface 实例并指定各自需要实现的接口
     var Composite = new Interface(‘Composite‘, [‘add‘, ‘remove‘, ‘getchild‘]);
     var FormItem = new Interface(‘FormItem‘, [‘save‘])

     // 使用

    var CompositeForm = function(id, method, action) {
        this.add = function() {}   // 这里我们只实现了一个方法,所以会报错
     }
     function addForm(formInstance) {
        // 这里先检查传进来的对象是否实现对应的 Interface 实例所指定接口
        Interface.ensureImplements(formInstance, Composite, FormItem)

         // 通过后……
     }

     addForm(new CompositeForm())  // 调用方法

    // 它只关心方法的名称,并不检查其参数的名称、数目或类别

其他方式

  • TypeScript :可以检查函数参数,对类成员可以显式声明访问级别:public、protected、private 等

三、封装和信息隐藏

1. 创建对象的基本模式

1.1 门户大开型对象

   var Book = function(isbn, title) {
        this.setIsbn(isbn)
       this.setTitle(title)
   }

   Book.prototype = {
    checkIsbn: function(isbn) {...},

       getIsbn: function(isbn) { return this.isbn },
       setIsbn: function(isbn) {
            if(!this.checkIsbn(isbn)) throw new Error(‘Book: Invalid ISBN‘)
           this.isbn = isbn
       },

       getTitle: function() { return this.title },
       setTitle: function(title) {
            this.title = title || ‘No title specified‘  //验证
       }
   }

   // 虽然设置了赋值器,但是属性依然是公开的,可以被直接赋值
   // 如 var b = new Book(‘aa‘, ‘title1‘)  可直接通过 b.isbn 修改 isbn

1.2 用命名规范区别私有成员

   var Book = function(isbn, title) {
        this.setIsbn(isbn)
       this.setTitle(title)
   }

   Book.prototype = {
    _checkIsbn: function(isbn) {...},

       getIsbn: function(isbn) {
            return this._isbn
       },
       setIsbn: function(isbn) {
            if(!this._checkout(isbn)) throw new Error(‘Book: Invalid ISBN‘)
           this._isbn = isbn
       },

       getTitle: function() {
            return this._title
       },
       setTitle: function(title) {
            this._title = title || ‘No title specified‘  //验证
       }
   }

   // 下划线只能防止程序员无意间使用某个属性,却不能防止对它的有意使用

?

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

   function foo() {
        var a = 10;
    return function bar() {
            a *= 2;
            return a;
       }
   }

   #usage
   var a = foo()
   var b = foo()
   a()      // 20
   a()  // 40
   b()  // 20

1.4 用闭包实现私有成员

   var Book = function(newIsbn, newTitel) {
       var isbn, title, author;          // Private attrbute 【私有属性】

       function checkIsbn(isbn) { return true}    // Private methods 【私有方法】

       this.getIsbn = function() {       // Privileged methods 【特权方法】
           return isbn
       };
       this.setIsbn = function(newIsbn) {
           if(!checkIsbn(newIsbn)) throw new Error(‘Book: Invalid ISBN‘);
           isbn = newIsbn
       }

       this.getTitle = function() {
           return title
       };
       this.setTitle = function(newTitel) {
           title = newTitel || ‘No title specified‘  //验证
       }

       this.setIsbn(newIsbn)
       this.setTitle(newTitel)
   }

   Book.prototype = {
       display: function() {}      //不需要访问私有属性的方法
   }
   // usage
   var b1 = new Book(‘11111‘, ‘title1‘)

   // 每生成一个新的对象实例都将为每个私用方法和特权方法生成一个新的副本,这会耗费更多内存
   // 这种对象创建模式不利于派生子类,因为派生的子类不能访问超类的任何私用属性或方法,这种方式被称为“继承破坏封装”
   // 如果创建的类以后可能会需要派生出子类,最好使用【门户大开型】或【命名规范区别私有成员】两种方式

2. 更多高级对象创建方式

2.1 静态方法和属性

   var Book = (function() {
       var numOfBooks = 0;    // Private static attributes 【静态私有属性】所有实例共享

       function checkIsbn(isbn) {return true}   // Private static methods【静态私有方法】所有实例共享

       return function(newIsbn, newTitel) {
           // 单个实例独有
           var isbn, title, author;   // Private attribute 【私有属性】

           this.getIsbn = function() {    // Privileged methods 【私有方法】
               return isbn
           };
           this.setIsbn = function(newIsbn) {
               if(!checkIsbn(newIsbn)) throw new Error(‘Book: Invalid ISBN‘);
               isbn = newIsbn
           }

           this.getTitle = function() {
               return title
           };
           this.setTitle = function(newTitel) {
               title = newTitel || ‘No title specified‘  //验证
           }

           numOfBooks++;
           if(numOfBooks > 50) {
               throw new Error(‘Book: only 50 instances of Book can be created‘)
           }

           this.setIsbn(newIsbn)
           this.setTitle(newTitel)
       }
   })()

   Book.convertToTitleCase = function(inputstring) {  //无需创建实例即可访问的方法
       return inputstring
   }

   Book.prototype = {     //不需要访问私有属性的方法
       display: function() {}
   }

   // usage
   var b1 = new Book(‘11111‘, ‘title1‘)

2.2 常量

   var Class = (function(){

        // private static attributes【静态私有属性】所有实例共享
        var constants = {
            UPPER_BOUND: 100,
            LOWER_BOUND: -100
        }

        var ctor = {}
        ctor.getConstant = function(name) {
            return constants[name]
        }

        return ctor
   })()

   # usage

   Class.getContant(‘UPPER_BOUNDA‘)

   // 创建只有取值器而没有赋值器的私有变量来模仿常量

四、继承

1. 类式继承

1.1 原型链

    function Person(name) {
        this.name = name
    }
    Person.prototype.getName = function() {
        return this.name
    }

    function Author(name, books) {
        Person.call(this, name);  // 将 Person 中 this.xxx 复制到这里
        this.books = books
    }

    Author.prototype = new Person();
    Author.prototype.constructor = Author;
    Author.prototype.getBooks = function() {
        return this.books
    }

    # usage
    var a1 = new Author(‘aaa‘, [‘a‘,‘b‘])

1.2 extend 函数

    function extend(subClass, superClass) {
    var F = function() {}
    F.prototype = superClass.prototype;  // 使用 F 是为了避免创建超类的新实例,superClass实例做为原型时,其属性会在subClass的实例里面被共享(如果是引用类型如Array,里面的每一个项修改会导致所有实例都被改),这显然不是我们想要的,所以应该通过 superclass.call(this, ...) 把实例的属性直接引入到 subClass的构造函数中
    subClass.prototype = new F();
    subClass.prototype.constructor = subClass;

    /* 增强版 */
    subClass.superclass = superClass.prototype    // 可直接通过 subClass.superclass.prototype.constructor 访问到超类的构造函数,弱化子类与超类的联系。subClass 的实例是访问不了 superClass 属性的。

    // 检查超类的 prototype.contructor 是否指向自身构造函数,如果不是则改指导超类的构造函数
    if(superClass.prototype.contructor == Object.prototype.constructor) {
            superClass.prototype.constructor = superClass;
        }
    }

    # usage
    /* Class Person */
    function Person(name) {
    this.name = name
    }
    Person.prototype.getName = function() {
    return this.name
    }

    /* Class Author */
    function Author(name, books) {
    Person.call(this, name);  // 将 Person 中 this.xxx 复制到这里
    this.books = books
    }

    extend(Author, Person)    //需要在 prototype 上添加新方法前被调用

    Author.prototype.getBooks = function() {
    return this.books
    }

2. 原型式继承

得益于原型链查找的工作机制

    function clone(o) {     // 返回以给定对象为原型的【空对象】
        function F() {}
            F.prototype = o
            return new F()
    }

    var Person = {
        name: ‘default name‘,
        getName: function() {
        return this.name
        }
    }

    var reader = clone(Person)
    alert(reader.getName())  // ‘default name‘

    reader.name = ‘John Smith‘
    alert(reader.getName())  // ‘John Smith‘

    // 原型对象 Person 为其他对象应有的模样提供了一个原型,这正是原型式继承这个名字的由来

对继承而来的成员的读和写的不对等性

第一次读 name 时是读到 Person 上的 name 属性,而第一次写是写 reader.name = ‘xxx‘ 写在 reader 上(引用类型变量还是在原型对象上进行修改,这很糟糕,需要先 reader.books= [] , reader.books.push())


    # 工厂方法辅助

    function clone(o) {
        function F() {}
        F.prototype = o
        return new F()
    }

    var CompoundObject = {};
    CompoundObject.string1 = ‘default value‘;
    CompoundObject.createChildObject = function() {      // 工厂方法
        return {
            bool: true,
            num: 10
        }
    }
    CompoundObject.childObject = CompoundObject.createChildObject()

    var c1 = clone(CompoundObject);
    c1.childObject = CompoundObject.createChildObject()
    c1.childObject.num = 5

3. 掺元类

    function argument(receivingClass, givingClass) {
        // 查看是否有其他参数用于指定复制特定方法
        if(argument[2]) {
            for(var i=2, len=arguments.length; i<len; i++) {
                receivingClass.prototype[arguments[i]] = givingClass.prototype[arguments[i]]
            }
        }else {
        // 如果没有则全部方法都复制过去 receivingClass
            for(methodName in givingClass.prototype) {
                if(!receivingClass.prototype[methodName]) {
                    receivingClass.prototype[methodName] = givingClass.prototype[methodName]
                }
            }
        }
    }

    /* Mixin class */
    var Mixin = function () {}         // 掺原类
    Mixin.prototype = {
        serialize: function() {
            var output = [];
            for(key in this) {
                output.push(key + ‘: ‘ + this[key])
            }
            return output.join(‘, ‘)
        }
    }

    function Author(name, books) {
        this.name = name
        this.books = books
    }

    argument(Author, Mixin);          // 进行复制

    var author = new Author(‘czs‘, [‘js‘, ‘nodejs‘])
    var serializeStr = author.serialize()
    console.log(serializeStr)

    // 将某个类中指定的方法复制到其他类中,而不是整体继承过去


** 声明:**

转载、引用,但请标明作者和原文地址

原文地址:https://www.cnblogs.com/CccZss/p/8490300.html

时间: 2024-10-11 21:34:52

Javascript设计模式(1)的相关文章

JavaScript设计模式 - 迭代器模式

迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示. 迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺序访问其中的每个元素 许多浏览器都支持 Javascript 的 Array.prototype.forEach 迭代器可以分为 内部迭代器 和 外部迭代器 一.jQuery 中的迭代器 1 $.each( [1,2,3,4], function (i, n) { 2 console.log( "当

javascript设计模式详解之命令模式

每种设计模式的出现都是为了弥补语言在某方面的不足,解决特定环境下的问题.思想是相通的.只不过不同的设计语言有其特定的实现.对javascript这种动态语言来说,弱类型的特性,与生俱来的多态性,导致某些设计模式不自觉的我们都在使用.只不过没有对应起来罢了.本文就力求以精简的语言去介绍下设计模式这个高大上的概念.相信会在看完某个设计模式之后有原来如此的感慨. 一.基本概念与使用场景: 基本概念: 将请求封装成对象,分离命令接受者和发起者之间的耦合. 命令执行之前在执行对象中传入接受者.主要目的相互

javascript设计模式

javascript设计模式 阅读目录 什么是设计模式 单体模式: 工厂模式: 单例模式 观察者模式(发布订阅模式) 策略模式 模板模式 代理模式 外观模式 设计模式太多了,貌似有23种,其实我们在平时的工作中没有必要特意去用什么样的设计模式,或者你在不经意间就已经用了设计模式当中的一种.本文旨在总结平时相对来说用的比较多的设计模式. 回到顶部 什么是设计模式 百度百科: 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结. 使用设计模式是

javascript设计模式实践之迭代器--具有百叶窗切换图片效果的JQuery插件(一)

类似于幻灯片的切换效果,有时需要在网页中完成一些图片的自动切换效果,比如广告,宣传,产品介绍之类的,那么单纯的切就没意思了,需要在切换的时候通过一些效果使得切换生动些. 比较常用之一的就是窗帘切换了. 先贴上完成的效果. 实现原理不复杂,在动的一条一条的称之为“窗帘条”或者是“strip”,每一个strip都是一个div,类似于雪碧图的方式将其背景图的位置设置为strip的可视位置,然后用jquery的animate让他们按照一定规律动起来就完成窗帘切换效果了. 为了使用方便,将这个功能作为jq

Javascript设计模式系列三

继承,一个类或对象继承另一个类或对象的三种方法.类式继承.原型式继承.掺元类. 一.类式继承,原型链.Extend函数. <script type="text/javascript"> ////定义Person类 var Person = function (name) { this.name = name; }; Person.prototype.getName = function () { return this.name; }; ////原型链 function A

Javascript设计模式系列二

创建对象的基本模式,一.门户大开型,二.采用下划线来表示属性和方法的私用性,三.使用闭包来创建私用的成员. 一.门户大开型.只能提供公用成员.所有属性和方法都公开的.可访问的.这些共用属性都要使用this关键字来创建. <script type="text/javascript"> ////定义Dog对象 var Dog = function (name, age) { this.name = name; this.age = age; } Dog.prototype.Sa

《Javascript 设计模式》-读书笔记

第七章   设计模式类别 一.设计模式有三种类别 1.创建型模式:专注于处理对象机制.包括:构造器.工厂.抽象.原型.单例.生成器 2.结构型模式:用于找出在不同对象之间建立关系密切的简单方法. 包括:装饰者.外观.享元.适配器.代理 3.行为模式:用于改善或简化系统中不同对象之间的通信.包括:迭代器.中介者.观察者.访问者 第八章   设计模式分类 <Javascript 设计模式>-读书笔记,布布扣,bubuko.com

【读书笔记】读《JavaScript设计模式》之观察者模式

一.定义 在事件驱动的环境中,比如浏览器这种持续寻求用户关注的环境中,观察者模式(又名发布者-订阅者(publisher-subscripber)模式)是一种管理人与其任务之间的关系(确切地讲,是对象及其行为和状态之间的关系)的得力工具.用JavaScript的话来说,这种模式的实质就是你可以对程序中某个对象的状态进行观察,并且在其发生改变时能够得到通知. 二.例子 我们需要一个发布者的构造函数,它为该实例定义了一个类型为数组的属性,用来保存订阅者的引用. function Publisher(

方法的链式调用【参考javascript设计模式第6章】

对应经常使用jquery的朋友,方法的链式调用应该是已经很属性了,书上有模拟出一个很简单的类库代码, 见代码如下: Function.prototype.method = function(name,fn){ this.prototype[name] = fn; return this; }; (function(){ function _$(els){ ........ } /*Events  addEvent getEvent*/ _$.method("addEvent",func

模块化JavaScript设计模式(一)

在可扩展JavaScript的世界里,如果我们说一个应用程序是模块化(modular)的,那么通常意味着它是由一系列存储于模块中的高度解耦,不同的功能片段组成.在可能的情况下,通过一处依赖性,松耦合可以使应用程序的可维护性更加简单. 如果有效地实现了这点,就很容易地了解一部分如何影响另一个部分. 异步模块定义(AMD)的整体目标是提供模块化的JavaScript解决方案,以便开发者使用.诞生于使用XHR+eval的Dojo开发经验,这种格式的支持者希望能够避免未来的任何解决方案受到过去解决方案缺