`
eric_weitm
  • 浏览: 245025 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

js 设计模式

阅读更多
js 设计模式

参考资料:http://www.addyosmani.com/resources/essentialjsdesignpatterns/book/

一 单例
var SingletonTester = (function(){

  // args: an object containing arguments for the singleton // 构造函数在匿名闭包内,所以外部不可见
  function Singleton( args ) {

   // set args variable to args passed or empty object if none provided.
    var args = args || {};
    //set the name parameter
    this.name = 'SingletonTester';
    //set the value of pointX
    this.pointX = args.pointX || 6; //get parameter from arguments or set default
    //set the value of pointY
    this.pointY = args.pointY || 10;

  }

// this is our instance holder
  var instance;

// this is an emulation of static variables and methods
  var _static = {
    name: 'SingletonTester',
   // This is a method for getting an instance

   // It returns a singleton instance of a singleton object
    getInstance: function ( args ){
      if (instance === undefined) {
        instance = new Singleton( args );
      }
      return instance;
    }
  };
  return _static; // 外部可见的只有这个_static , _static在闭包内定义,所以可以调用构造函数
})();

var singletonTest = SingletonTester.getInstance({pointX: 5});
console.log(singletonTest.pointX); // 输出 5

var anothersSingletonTest = SingletonTester.getInstance({pointX: 66666});
console.log(anothersSingletonTest.pointX); // 输出 5

if (singletonTest === anothersSingletonTest)
  console.log('OK');


  小结:主要技巧是使用匿名闭包

二 模块化
即js中实现c++中的namespace或者java中的package,
1、基本形式
var basketModule = (function() {
    var basket = []; // 私有变量
    function doSomethingPrivate(){
      //... // 私有函数
    }

    function doSomethingElsePrivate(){
      //...
    }
    return { // 提供给外部的接口
        addItem: function(values) {
            basket.push(values);
        },
        getItemCount: function() {
            return basket.length;
        },
        doSomething: doSomethingPrivate(),
        getTotal: function(){
           var q = this.getItemCount(),p=0;
            while(q--){
                p+= basket[q].price;
            }
            return p;
        }
    }
}());

//basketModule is an object with properties which can also be methods
basketModule.addItem({item:'bread',price:0.5});
basketModule.addItem({item:'butter',price:0.3});

console.log(basketModule.getItemCount());
console.log(basketModule.getTotal());

//however, the following will not work:
console.log(basketModule.basket);// 无法访问,其在闭包中
console.log(basket); // 也无法访问

2、jquery中组织库的方法
function library(module) { // 此函数用于定义一个包
  $(function() {
    if (module.init) {
      module.init();  // init是初始化的接口
    }
  });
  return module;
}

var myLibrary = library(function() {
   return {
     init: function() {
       /*implementation*/  // 这里可以对此模块进行初始化
     }
   };
}());

3、改进
var myRevealingModule = (function(){

    var name = 'John Smith';
    var age = 40;

    function updatePerson(){
      name = 'John Smith Updated';
    }
    function setPerson () {
       name = 'John Smith Set';
    }
    function getPerson () {
       return name;
    }
    return {
        set: setPerson, // 此处不再是直接写函数体,而是设个指针
        get: getPerson
    };
}());

// Sample usage:
myRevealingModule.get();

好处是:return部分的代码更简洁

三 观察者模式
好处:灵活,低耦合、好内聚
坏处:不容易看清楚调用关系(调用栈是从回调处开始的);一个listener挂了,发出消息的对象不知道。

例子:
var pubsub = {};

(function(q) {

    var topics = {},
        subUid = -1;

    // Publish or broadcast events of interest
    // with a specific topic name and arguments
    // such as the data to pass along
    q.publish = function( topic, args ) {  // 发布消息

        if ( !topics[topic] ) {
            return false;
        }

        setTimeout(function() {           // 放到下一个tick里面会安全一点
            var subscribers = topics[topic],
                len = subscribers ? subscribers.length : 0;

            while (len--) {
                subscribers[len].func(topic, args); // 调用回调函数
            }
        }, 0);

        return true;

    };

    // Subscribe to events of interest
    // with a specific topic name and a
    // callback function, to be executed
    // when the topic/event is observed
    q.subscribe = function( topic, func ) { // 注册回调

        if (!topics[topic]) {
            topics[topic] = [];
        }

        var token = (++subUid).toString();  // 这个主要是标记各个回调函数
        topics[topic].push({
            token: token,
            func: func
        });
        return token;
    };

    // Unsubscribe from a specific
    // topic, based on a tokenized reference
    // to the subscription
    q.unsubscribe = function( token ) {
        for ( var m in topics ) {
            if ( topics[m] ) {
                for (var i = 0, j = topics[m].length; i < j; i++) {
                    if (topics[m][i].token === token) {
                        topics[m].splice(i, 1);
                        return token;
                    }
                }
            }
        }
        return false;
    };
}( pubsub ));

var testSubscriber = function( topics , data ){
    console.log( topics + ": " + data );
};

// 1, 基本使用方法:
// Publishers are in charge of "publishing" notifications about events

pubsub.publish( 'example1', 'hello world!' );
pubsub.publish( 'example1', ['test','a','b','c'] );
pubsub.publish( 'example1', [{'color':'blue'},{'text':'hello'}] );

// Subscribers basically "subscribe" (or listen)
// And once they've been "notified" their callback functions are invoked
var testSubscription = pubsub.subscribe( 'example1', testSubscriber );

// Unsubscribe if you no longer wish to be notified

setTimeout(function(){
    pubsub.unsubscribe( testSubscription );
}, 0);

pubsub.publish( 'example1', 'hello again! (this will fail)' );


// 2, 股票交易信息模拟
var grid = {

   addEntry: function(data){

       if (data !== 'undefined') {

          console.log('Entry:'

                      + data.title

                      + ' Changenet / %'

                      + data.changenet

                      + '/' + data.percentage + ' % added');

       }

   },

   updateCounter: function(timestamp){
       console.log('grid last updated at: ' + timestamp);
   }
};



var gridUpdate = function(topics, data){
       grid.addEntry(data);
       grid.updateCounter(data.timestamp);
}



var gridSubscription = pubsub.subscribe( 'dataUpdated', gridUpdate );

pubsub.publish('dataUpdated',   { title: "Microsoft shares", changenet: 4, percentage: 33, timestamp: '17:34:12'  });

pubsub.publish('dataUpdated',   { title: "Dell shares", changenet: 10, percentage: 20, timestamp: '17:35:16'  });

四、中介者模式 The Mediator Pattern
  此模式基本概念:把模块之间的网状引用关系,变成一对多的关系,即Mediator负责维护模块间的调用关系。
优点:解耦合、重用
缺点:性能低(不是直接通信)、不容易看清调用流程

与门面模式区别:维护网状依赖, 门面仅仅是简化一个模块的调用。

例子:
var mediator = (function(){
    // Subscribe to an event, supply a callback to be executed
    // when that event is broadcast
    var subscribe = function(channel, fn){
        if (!mediator.channels[channel]) mediator.channels[channel] = [];
        mediator.channels[channel].push({ context: this, callback: fn });
        return this;
    },

    // Publish/broadcast an event to the rest of the application
    publish = function(channel){
        if (!mediator.channels[channel]) return false;
        var args = Array.prototype.slice.call(arguments, 1);
        for (var i = 0, l = mediator.channels[channel].length; i < l; i++) {
            var subscription = mediator.channels[channel][i];
            subscription.callback.apply(subscription.context, args);
        }
        return this;
    };

    return {
        channels: {},
        publish: publish,
        subscribe: subscribe,
        installTo: function(obj){
            obj.subscribe = subscribe;
            obj.publish = publish;
        }
    };

}());

(function( m ){

    function initialize(){

      // Set a default value for 'person'
      var person = "tim";

      // Subscribe to an event called 'nameChange' with
      // a callback function which will log the original
      // person's name and (if everything works) the new
      // name

      m.subscribe('nameChange', function( arg ){
          console.log( person ); // tim
          person = arg;
          console.log( person ); // david
     });
    }

    function updateName(){
      // Publish/Broadcast the 'nameChange' event with the new data 将原来的直接回调,变成了这样中转一次
      m.publish( 'nameChange', 'david' );
    }

})( mediator );

五 The Prototype Pattern 原型模式 JS本身就支持

// 所有vehicle都该有的样子
var vehiclePrototype = {
    init: function( carModel ) {
        this.model = carModel;
    },
    getModel: function() {
        console.log( 'The model of this vehicle is..' + this.model );
    }
};


function vehicle( model ) {
    function F() {};
    F.prototype = vehiclePrototype;

    var f = new F();// 这个新对象数据是空的,但是函数却是和vehiclePrototype一样(prototype链机制)

    f.init( model );// 将数据进行初始化
    return f;
}

var car = vehicle( 'Ford Escort' );
car.getModel();// 调用原型上的函数

六 命令模式
以统一的接口来调用各个函数,JS中函数可以直接当变量传,所以很容易实现
$(function(){

  var CarManager = {

      // request information
      requestInfo: function( model, id ){
        return 'The information for ' + model +
        ' with ID ' + id + ' is foobar';
      },

      // purchase the car
      buyVehicle: function( model, id ){
        return 'You have successfully purchased Item '
        + id + ', a ' + model;
      },

      // arrange a viewing
      arrangeViewing: function( model, id ){
        return 'You have successfully booked a viewing of '
        + model + ' ( ' + id + ' ) ';
      }

    };

})();

// 把一个请求映射为具体的函数
CarManager.execute = function( command ){
  return CarManager[command.request](command.model,command.carID);
};

// 使用方法
CarManager.execute({request: "arrangeViewing", model: 'Ferrari', carID: '145523'});
CarManager.execute({request: "requestInfo", model: 'Ford Mondeo', carID: '543434'});
CarManager.execute({request: "requestInfo", model: 'Ford Escort', carID: '543434'});
CarManager.execute({request: "buyVehicle", model: 'Ford Escort', carID: '543434'});

七 The DRY Pattern  --------相似代码只写一次
1、一般实现
// Let's store some defaults about a car for reference
var defaultSettings = {};
defaultSettings['carModel']   = 'Mercedes';
defaultSettings['carYear']    = 2010;
defaultSettings['carMiles']   = 5000;
defaultSettings['carTint']    = 'Metallic Blue';

// Let's do something with this data if a checkbox is clicked
$('.someCheckbox').click(function(){

if ( this.checked ){

    $('#input_carModel').val(activeSettings.carModel);
    $('#input_carYear').val(activeSettings.carYear);
    $('#input_carMiles').val(activeSettings.carMiles);
    $('#input_carTint').val(activeSettings.carTint);

} else {

    $('#input_carModel').val('');
    $('#input_carYear').val('');
    $('#input_carMiles').val('');
    $('#input_carTint').val('');
}
});


2、DRY Pattern

$('.someCheckbox').click(function(){
    var checked = this.checked,
    fields = ['carModel', 'carYear', 'carMiles', 'carTint'];
    /*
        What are we repeating?
        1. input_ precedes each field name
        2. accessing the same array for settings
        3. repeating value resets

        What can we do?
        1. programmatically generate the field names
        2. access array by key
        3. merge this call using terse coding (ie. if checked,
            set a value, otherwise don't)
  */
  $.each(fields, function(i,key){
    $('#input_' + key).val(checked ? defaultSettings[key] : ''); // 这里不是写很多的$('#input_XX').val(activeSettings.YY); 而是统一写一句通用的代码
  });
});

3、自己项目中的一段代码实例:

CraftData.inheritsFrom(BasicData);

CraftData.prototype.CRAFT_TYPE_INFO = {
  // order is:speed, range, acceleration, maxFuel, weaponNum, maxDamage, maxCapacity, HWPs, costPerMonth
  'SKYRANGER'   : [760, 13500, 2, 1500, 0, 150, 14, 3, 500000], // 这里的数据没有写成罗嗦的json格式
  'INTERCEPTOR' : [2100, 8050, 3, 1000, 2, 100, 0,  0, 600000],

  'LIGHTNING'   : [3100, 7750, 8, 30,   1, 800, 12, 0, 600000],
  'FIRESTORM'   : [4200, 7000, 9, 20, 2, 500, 0,  0, 400000],
  'AVENGER'     : [5400, 27000, 10, 60, 2, 1200, 26,  4, 900000],
};

CraftData.prototype.initSpecialArg = function(){
  var that = this;

  var arg = that.CRAFT_TYPE_INFO[that.type];

  if (!arg){
    alert("facility type error! in CraftData.prototype.initSpecialArg ");
    throw new error("facility type error! in CraftData.prototype.initSpecialArg ");
  }

  var order = ['speed', 'range', 'acceleration', 'maxFuel', 'weaponNum', 'maxDamage', 'maxCapacity', 'HWPs', 'costPerMonth'];

  for (var index = 0;index < arg.length; index++){
    that[order[index]] = arg[index];  // 这里将对应的属性设置上去
  }
};

八 门面 The Facade Pattern -----降低使用代码的难度
var module = (function() {
    var _private = {
        i:5,
        get : function() {
            console.log('current value:' + this.i);
        },
        set : function( val ) {
            this.i = val;
        },
        run : function() {
            console.log( 'running' );
        },
        jump: function(){
            console.log( 'jumping' );
        }
    };
    return {
        facade : function( args ) { // 一次性提供一个facade就好。不使用门面时,需要暴露给client程序员set 和get方法,并且要求client程序员要知道怎么组织这部分的逻辑(先set 后run)
            _private.set(args.val);
            _private.get();
            if ( args.run ) {
                _private.run();
            }
        }
    }
}());


module.facade({run: true, val:10});
//outputs current value: 10, running


九 工厂模式

var Car = (function() {
   var Car = function ( model, year, miles ){
       this.model = model;
       this.year   = year;
       this.miles = miles;
   };

   return function ( model, year, miles ) {
       return new Car( model, year, miles ); // 工厂内部进行new
   };

})();

var civic = Car( "Honda Civic", 2009, 20000 ); // 不是直接new 而是调用工厂的方法
var mondeo = Car("Ford Mondeo", 2010, 5000 );

十 The Mixin Pattern  类似于c++的多继承
// Car
var Car = function( settings ){
  this.model = settings.model || 'no model provided';
  this.colour = settings.colour || 'no colour provided';
};

// Mixin
var Mixin = function(){};
Mixin.prototype = {
  driveForward: function(){
    console.log('drive forward');
  },
  driveBackward: function(){
    console.log('drive backward');
  }
};


// 第一个参数要使用别人功能的对象, 第二个参数 提供功能的对象, 后面的参数是提供的函数的名字
function augment( receivingClass, givingClass ) {
  // only provide certain methods
  if ( arguments[2] ) {
    for (var i=2, len=arguments.length; i<len; i++) {
      receivingClass.prototype[arguments[i]] = givingClass.prototype[arguments[i]]; // 还是使用原型
    }
  }
  // provide all methods
  else {
    for ( var methodName in givingClass.prototype ) {
      /* check to make sure the receiving class doesn't
         have a method of the same name as the one currently
         being processed */
      if ( !receivingClass.prototype[methodName] ) {
        receivingClass.prototype[methodName] = givingClass.prototype[methodName];
      }
    }
  }
}


// Augment the Car have the methods 'driveForward' and 'driveBackward'*/
augment( Car, Mixin,'driveForward','driveBackward' );

// Create a new Car
var vehicle = new Car({model:'Ford Escort', colour:'blue'});

// Test to make sure we now have access to the methods
vehicle.driveForward(); // 尽管vehicle是car,但是通过augment,使得Car的对象都具有了Mixin的函数
vehicle.driveBackward();


十一 The Decorator Pattern 装饰模式
1、OOP的经典重用技术 -------继承
var subclassExample = subclassExample || {};
subclassExample = {
    Person: function( firstName , lastName ){
        this.firstName = firstName;
        this.lastName =  lastName;
        this.gender = 'male'
    }
}

//a new instance of Person can then easily be created as follows:
var clark = new subclassExample.Person( "Clark" , "Kent" );

//Define a subclass constructor for for 'Superhero':
subclassExample.Superhero = function( firstName, lastName , powers ){
    /*
        Invoke the superclass constructor on the new object
        then use .call() to invoke the constructor as a method of
        the object to be initialized.
    */
    subclassExample.Person.call(this, firstName, lastName); // 利用js的动态性,实现java中super类似的功能
    //Finally, store their powers, a new array of traits not found in a normal 'Person'
    this.powers = powers;
}
subclassExample.Superhero.prototype = new subclassExample.Person; // 通过prototype将方法共享过来
var superman = new subclassExample.Superhero( "Clark" ,"Kent" , ['flight','heat-vision'] );
console.log(superman); /* includes superhero props as well as gender*/

2、不用继承 而用装饰者模式 ,java的io流中大量使用了此模式。不使用继承,所以类的层次结构不会被打乱。
//What we're going to decorate
function MacBook() {
    this.cost = function () { return 997; };
    this.screenSize = function () { return 13.3; };
}

/*Decorator 1*/
function Memory( macbook ) {
    var v = macbook.cost();
    macbook.cost = function() {
        return v + 75; // 这时cost返回的是 997+75
    }
}
/*Decorator 2*/
function Engraving( macbook ){
   var v = macbook.cost();
   macbook.cost = function(){
     return  v + 200;// 这时cost返回的是 997+75+200
  };
}

/*Decorator 3*/
function Insurance( macbook ){
   var v = macbook.cost();
   macbook.cost = function(){
     return  v + 250;// 这时cost返回的是 997+75+200+250
  };
}
var mb = new MacBook();
Memory(mb);
Engraving(mb);
Insurance(mb);
console.log(mb.cost()); //1522,所以这个值是1522
console.log(mb.screenSize()); //13.3


架构模式
一 MVC
例子:
backbone.js
二 MVP

分享到:
评论

相关推荐

    JavaScript设计模式与开发实践.pdf

    "JavaScript设计模式与开发实践.pdf" 本书《JavaScript设计模式与开发实践》是JavaScript语言的设计模式和开发实践的指南,旨在帮助初、中、高级Web前端开发人员和想往架构师晋级的中高级程序员,掌握JavaScript...

    JavaScript设计模式.pdf

    JavaScript设计模式.pdf JavaScript设计模式是指在软件开发中使用JavaScript语言编写的设计模式。这些设计模式旨在提高代码的重用性、可读性、维护性和扩展性。以下是JavaScript设计模式的知识点总结: 1. 单体...

    JavaScript设计模式与开发实践_himppf_js_jspremise_精通javascript_Js设计模式_

    JavaScript设计模式与开发实践是深入理解并提升JavaScript编程能力的关键领域。设计模式是对在软件设计中经常出现的问题的解决方案的描述,它代表了最佳实践,是经验丰富的开发者们经过时间检验后总结出来的解决常见...

    JS设计模式与开发实践

    #### 一、JavaScript设计模式的概念与起源 - **设计模式起源**:设计模式最初来源于建筑学领域,建筑师Christopher Alexander提出的“模式语言”概念,旨在解决建筑设计中的常见问题。随后,这一理念被引入到软件...

    学用 JavaScript 设计模式.pdf

    外文翻译:学用JavaScript设计模式,pdf版本,中文版 设计模式是可重用的用于解决软件设计中一般问题的方案。设计模式如此让人着迷,以至在任何编程语言中都有对其进行的探索。 其中一个原因是它可以让我们站在巨人...

    js设计模式详解和 函数式编程PDF

    JavaScript设计模式详解与函数式编程是开发者提升代码质量和可维护性的重要工具。设计模式是对在软件设计中经常出现的问题和解决方案的一种模式化描述,而函数式编程则是一种编程范式,强调程序数据的不可变性和函数...

    JAVASCRIPT设计模式[收集].pdf

    在现代前端开发中,JavaScript设计模式已经成为了一门必修课程。它不仅能够提升开发者的编码效率,而且还能保证编写的代码更加健壮、可维护。本文将对JavaScript设计模式的核心概念进行深入探讨,以期帮助开发者更好...

    node.js 设计模式 英文版

    ### Node.js设计模式第二版概览 #### 一、书籍基本信息 - **书名**:《Node.js设计模式》第二版 - **作者**:Mario Casciaro 和 Luciano Mammino - **出版社**:Packt Publishing - **出版时间**:第二版于2016年7...

    几种js设计模式.rar

    在这个"js设计模式.rar"压缩包中,我们可能会发现一系列关于JavaScript设计模式的文档或代码示例。 设计模式可以分为三大类:创建型、结构型和行为型。下面将详细介绍这三类中的主要设计模式,并结合JavaScript的...

    javascript 面向对象编程.pdf javascript 设计模式与开发实践.pdf

    面向对象编程(Object-Oriented Programming, OOP)是JavaScript中的核心概念,而设计模式则是解决常见编程问题的经验总结,对于提升代码质量和可维护性至关重要。这两本书——"JavaScript 面向对象编程.pdf"和...

    Javascript 设计模式系统讲解与应用

    在深入探讨《JavaScript设计模式系统讲解与应用》的内容之前,我们先来了解一下设计模式的基本概念以及为什么它对于前端开发人员尤为重要。设计模式是一套被反复使用的、经过分类编目的、包含结构化的解决方案,用于...

    Javascript 设计模式系统讲解与应用视频资源地址.7z

    JavaScript设计模式是编程实践中的一种重要思想,它提供了一套经过时间考验的最佳实践,用来解决常见的编程问题和提高代码的可维护性、可扩展性和可复用性。在JavaScript这种动态类型的脚本语言中,设计模式尤其重要...

    js设计模式

    JavaScript设计模式是软件开发中的一种最佳实践,它总结了在解决特定问题时,代码组织和交互的常见方式。这些模式可以帮助开发者编写可维护、可扩展、易于理解和复用的代码。"js-design-pattern"这个主题涵盖了...

    JS设计模式笔记和代码

    JavaScript设计模式是软件开发中的一种最佳实践,它们提供了一种在特定情况下解决复杂问题的标准化方法。本笔记和代码集合涵盖了多种重要的设计模式,旨在帮助开发者编写更可维护、可扩展和可重用的JavaScript代码。...

    Node.js Design Patterns Second Edition.pdf (node.js 设计模式)高清

    ### Node.js设计模式第二版 #### 一、概述 《Node.js Design Patterns Second Edition》(Node.js设计模式第二版)是一本深入探讨Node.js高级编程技术的书籍,它旨在帮助开发者充分利用Node.js的强大功能和最佳...

    javascript 设计模式简化版

    包括面向对象,面向对象编程思想,javascript面向对象,设计模式通俗版,快速进入模式学习与实践。

    Javascript 设计模式系统讲解与应用视频资源地址.txt

    Javascript 设计模式系统讲解与应用视频资源地址.txt

Global site tag (gtag.js) - Google Analytics