JavaScript设计模式与开发实践

Posted A_山水子农

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript设计模式与开发实践相关的知识,希望对你有一定的参考价值。

  最近在研读了腾讯AlloyTeam前端团队,高级工程师曾探编写的《javascript设计模式与开发实践》,所有设计模式的实现都遵循一条原则,即“找出程序中变化的地方,并将变化封装起来”。一个程序的设计总是可以分为可变的部分和不变的部分。当我们找出可变的部分,并且把这部分封装起来,那么剩下的就是不变和稳定的部分。

  JavaScript没有提供传统面向对象语言中的类式继承,而是通过原型委托的方式来实现对象与对象之间的继承。JavaScript也没有在语言层面提供对抽象类和接口的支持。所以在JavaScript用设计模式编写代码的时候,要跟传统面向对象语言加以区分。

1、单例模式

  单例模式的定义是:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
  单例模式是一种常用的模式,有一些对象我们往往只需要一个,比如线程池、全军缓存、浏览器中的window对象等。

<html>
  <head>
    <title>惰性单例-点击登录弹出登录浮窗</title>
  </head>
  <body>
    <button id="loginBtn">登录</button>
    <script>
      // 创建实例对象的职责
      let createLoginLayer = function () 
        let div = document.createElement('div')
        div.innerHTML = '我是登录浮窗'
        div.style.display = 'none'
        document.body.appendChild(div)
        return div
      
      let createIframeLayer =  function () 
        let iframe = document.createElement('iframe')
        document.body.appendChild(iframe)
        return iframe
      
      // 管理单例的职责
      let getSingle = function (fn) 
        let result
        return function () 
          return result || (result = fn.apply(this, arguments))
        
      
      // 创建div浮窗
      let createSingleLoginLayer = getSingle(createLoginLayer)
      // 点击多次都只会创建一个新的登录浮层div
      document.getElementById('loginBtn').onclick = function () 
        let loginLayer = createSingleLoginLayer()
        loginLayer.style.display = 'block';
      
    </script>
  </body>
</html>

2、策略模式

  策略模式的定义是:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。
  策略模式的目的就是将算法的使用和算法的实现分离开来。说的更详细点就是:定义一些列的算法,把它们各自封装成策略类,算法被封装在策略类内部的方法里。在客户对Context发起请求的时候,Context总是把请求委托给这些策略对象中间的某一个进行计算。

// 普通实现
let calculateBonusCommon = (performanceLevel, salary) => 
  if (performanceLevel === 'S') 
    return salary * 4
  
  if (performanceLevel === 'A') 
    return salary * 3
  
  if (performanceLevel === 'B') 
    return salary * 2
  

console.log(calculateBonusCommon('B', 20000))
console.log(calculateBonusCommon('S', 50000))

// 策略模式实现
// 策略类
let strategies = 
  'S': (salary) => salary * 4,
  'A': (salary) => salary * 3,
  'C': (salary) => salary * 2

// 环境类
let calculateBonus = (level, salary) => strategies[level](salary)
console.log(calculateBonus('S', 20000))
console.log(calculateBonus('A', 10000))

通过使用策略模式重构代码,我们消除了原程序中大片的条件分支语句。

3、代理模式

  代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问。
  代理模式的关键是,当客户不方便直接访问一个对象或者不满足需要的时候,提供一个替身对象来控制对这个对象的访问,客户实际上访问的是替身对象。替身对象对请求做出一些处理之后,再把请求转交给本体对象。

<html>
  <head>
    <title>虚拟代理实现图片预加载</title>
  </head>
  <body>
    <script>
      // 加载图片
      let myImage = (function () 
        let imgNode = document.createElement('img')
        document.body.appendChild(imgNode)
        return 
          setSrc: function (src) 
            imgNode.src = src
          
        
      )()
      // 代理对象proxyImage
      let proxyImage = (function () 
        let img = new Image
        img.onload = function () 
          myImage.setSrc(this.src)
        
        return 
          setSrc: function (src) 
            myImage.setSrc('./loading.gif')
            img.src = src
          
        
      )()
      proxyImage.setSrc('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1521956229659&di=36a2ea375f48e8328b3cab79e8b1ea0e&imgtype=0&src=http%3A%2F%2Ff0.topitme.com%2F0%2Fa9%2F3e%2F1164210455aae3ea90o.jpg')
    </script>
  </body>
</html>

  如果有一天我们不再需要预加载,那么就不再需要代理对象,可以选择直接请求本体。其中关键是代理对象和本体都对外提供了setSrc方法,在客户看来,代理对象和本体是一致的。

// 乘积
let mult = function () 
  console.log('开始计算乘积')
  let a = 1
  for (let i = 0, l = arguments.length; i < l; i++) 
    a *= arguments[i]
  
  return a

// 加和
let plus = function () 
  console.log('开始计算加和')
  let a = 1
  for (let i = 0, l = arguments.length; i < l; i++) 
    a += arguments[i]
  
  return a

// 减法
let subtraction = function () 
  console.log('开始计算减法')
  let a = 1
  for (let i = 0, l = arguments.length; i < l; i++) 
    a -= arguments[i]
  
  return a

// 缓存代理函数
let proxyMult = (function () 
  let cache = []
  return function () 
    let args = Array.prototype.join.call(arguments, ',')
    if (args in cache) 
      return cache[args]
    
    return cache [args] = mult.apply(this, arguments)
  
)()
console.log(proxyMult(1, 2, 3, 4))
console.log(proxyMult(1, 2, 3, 4))
console.log(proxyMult(1, 2, 3, 4, 5))

// 高阶函数动态创建缓存代理的工厂
let createProxyFactory = function (fn) 
  let cache = []
  return function () 
    let args = Array.prototype.join.call(arguments, ',')
    for (args in cache) 
      return cache[args]
    
    return cache[args] = fn.apply(this, arguments)
  

let proxyPlus = createProxyFactory(plus)
let proxySubtrsction = createProxyFactory(subtraction)
console.log(proxyPlus(1, 2, 3, 4))
console.log(proxyPlus(1, 2, 3, 4))
console.log(proxySubtrsction(10, 3, 4))
console.log(proxySubtrsction(10, 3, 4))

4、迭代器模式

  迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺序访问其中的每个元素。

let each = function (ary, callback) 
  for (let i = 0, l = ary.length; i < l; i++) 
    callback.call(ary[i], i, ary[i])
  

each([1, 2, 3, 4], function(i, n) 
  console.log([i, n])
)

5、发布-订阅模式

  发布—订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。在JavaScript开发中,我们一般用事件模型来代替传统的发布-订阅模式。
下面看实现发布-订阅模式的步骤:

  • 首先要指定好谁充当发布者。
  • 然后给发布者添加一个缓存列表,用于存放回调函数以便通知订阅者。
  • 最后发布消息的时候,发布者会遍历这个缓存列表,依次触发里面存放的订阅者回调函数
// 全局发布-订阅对象
let Event = (function () 
  // 缓存列表,存放订阅者的回调函数
  let clientList = 
  let listen, trigger, remove
  // 增加订阅者
  listen = (key, fn) => 
    if (!clientList[key]) 
      clientList[key] = []
    
    clientList[key].push(fn)
  ,
  // 发布消息
  trigger = (...value) => 
    let key = Array.prototype.shift.call(value)
    let fns = clientList[key]
    // 如果没有绑定的对应的消息
    if (!fns || fns.length === 0) 
      return false
    
    for ( let i = 0, fn; fn = fns[i++]; ) 
      fn.apply(this, value)
    
  ,
  // 取消订阅事件
  remove = (key, fn) => 
    let fns = clientList[key]
    // 如果key对应的消息没有被人订阅,则直接返回
    if (!fns) 
      return false
    
    if (!fn)  // 如果没有传入具体的回调函数,表示需要取消key对应消息的所有订阅
      fns && (fns.length = 0)
     else 
      for (let l = fns.length - 1; l >= 0; l--) 
        let _fn = fns[l]
        if (_fn === fn) 
          fns.splice(l, 1) // 删除订阅者的回调函数
        
      
    
  
  return 
    listen,
    trigger,
    remove
  
)()
// 小明订阅消息
Event.listen('squareMeter88', fn1 = function (price, squareMeter) 
  console.log('小明先生:')
  console.log('price = ' + price)
  console.log('squareMeter = ' + squareMeter)
)
// 小红订阅消息
Event.listen('squareMeter88', fn2 = function (price, squareMeter) 
  console.log('小红小姐:')
  console.log('price = ' + price)
  console.log('squareMeter = ' + squareMeter)
)
// 售楼处发布消息
Event.trigger('squareMeter88', 10000, 88)
Event.remove('squareMeter88', fn1)
Event.trigger('squareMeter88', 15000, 88)

6、命令模式

  命令模式中的命令(command)指的是一个执行某些特定事情的指令。
  命令模式最常见的应用场景是:有时候需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是什么。此时希望用一种松耦合的方式来设计程序,使得请求发送者和请求接收者能够消除彼此之间的耦合关系。

let button1 = document.getElementById('button1')
let button2 = document.getElementById('button2')
let button3 = document.getElementById('button3')
let MenuBar = 
  refresh: function() 
    console.log('刷新菜单界面')
  

let SubMenu = 
  add: function () 
    console.log('增加子菜单')
  ,
  del: function () 
    console.log('删除子菜单')
  

// 接收者被封闭在闭包产生的环境中,执行命令的操作可以更加简单,仅仅执行回调函数即可
let RefreshMenuBarCommand = function (receiver) 
  return 
    execute: function () 
      receiver.refresh()
    
  

let AddSubMenuBarCommand = function (receiver) 
  return 
    execute: function () 
      receiver.add()
    
  

let DelSubMenuBarCommand = function (receiver) 
  return 
    execute: function () 
      receiver.del()
    
  

// setCommand函数负责往按钮上面安装命令
let setCommand = function (button, command) 
  button.onclick = function () 
    command.execute()
  


let refreshMenuBarCommand = RefreshMenuBarCommand(MenuBar)
setCommand(button1, refreshMenuBarCommand)
let addSubMenuBarCommand = AddSubMenuBarCommand(SubMenu)
setCommand(button2, addSubMenuBarCommand)
let delSubMenuBarCommand = DelSubMenuBarCommand(SubMenu)
setCommand(button2, delSubMenuBarCommand)

宏命令是一组命令的集合,通过执行宏命令的方式,可以一次执行一批命令。

let closeDoorCommand = 
  execute: function () 
    console.log('关门')
  


let openPcCommand = 
  execute: function () 
    console.log('开电脑')
  

let openQQCommand = 
  execute: function () 
    console.log('登录QQ')
  

let MacroCommand = function () 
  return 
    commandsList: [],
    add: function (command) 
      this.commandsList.push(command)
    ,
    execute: function() 
      for (let i =0, command; command = this.commandsList[i++];) 
        command.execute()
      
    
  

let macroCommand = MacroCommand()
macroCommand.add(closeDoorCommand)
macroCommand.add(openPcCommand)
macroCommand.add(openQQCommand)
macroCommand.execute()

7、组合模式

  组合模式就是用小的子对象来构建更大的对象,而这些小的子对象本身也许是由更小的“孙对象”构成的。组合模式将对象组合成树形结构,以表示“部分-整体”的层次结构。除了用来表示树形结构之外,组合模式的另一个好处是通过对象的多态性表现,使得用户对单个对象和组合对象的使用具有一致性。

// 组合模式-扫描文件夹
// 文件夹
let Folder = function (name) 
  this.name = name
  this.files = []

Folder.prototype.add = function (file) 
  this.files.push(file)

Folder.prototype.scan = function () 
  console.log('开始扫描文件夹:' + this.name)  
  for(let i = 0, file; file = this.files[i++];) 
    file.scan()
  


// 文件
let File = function (name) 
  this.name = name

File.prototype.add = function () 
  throw new Error('文件下面不能再添加文件')

File.prototype.scan = function () 
  console.log('开始扫描文件:' + this.name)


// 创建文件夹
let folder = new Folder('学习资料')
let folder1 = new Folder('Javascript')
let folder2 = new Folder('JQuery')
// 创建文件
let file1 = new File('Javascript 设计模式与开发实践')
let file2 = new File('精通JQuery')
let file3 = new File('重构与模式')

folder1.add(file2)
folder2.add(file3)

folder.add(file1)
folder.add(file2)
folder.add(file3)
folder.add(folder1)
folder.add(folder2)

folder.scan()

8、模板方法模式

  模板方法模式是一种只需使用继承就可以实现的非常简单的模式。模板方法模式由两部分结构组成,第一部分是抽象父类,第二部分是具体的实现子类。在模板方法模式中,子类实现中的相同部分被上移到父类中,而将不同的部分留待子类来实现。这也很好地体现了泛化的思想。

let Beverage = function () 
Beverage.prototype.boilWater = function () 
  console.log('把水煮沸')

Beverage.prototype.brew = function () 
  throw new Error('子类必须重写brew方法')

Beverage.prototype.pourInCup = function () 
  throw new Error('子类必须重写pourInCup方法')

Beverage.prototype.addCondiments = function () 
  throw new Error('子类必须重写addCondiments方法')

// 钩子方法
Beverage.prototype.customerWantsCondiments = function () 
  return true // 默认需要调料

// 模板方法
Beverage.prototype.init = function ()  
  this.boilWater()
  this.brew()
  this.pourInCup()
  // 如果挂钩返回true。则需要调料
  if (this.customerWantsCondiments()) 
    this.addCondiments()
  


// 泡茶
let Tea = function () 
Tea.prototype = new Beverage()
Tea.prototype.brew = function () 
  console.log('用沸水浸泡茶叶')

Tea.prototype.pourInCup = function () 
  console.log('把茶倒进杯子')

Tea.prototype.addCondiments = function () 
  console.log('加柠檬')

Tea.prototype.customerWantsCondiments = function () 
  return window.confirm('请问需要调料吗?')

let tea = new Tea()
tea.init()

//泡咖啡
let Coffee = function () 
Coffee.prototype = new Beverage()
Coffee.prototype.brew = function () 
  console.log('用沸水冲泡咖啡')

Coffee.prototype.pourInCup = function () 
  console.log('把咖啡倒进杯子')

Coffee.prototype.addCondiments = function () 
  console.log('加牛奶和糖')

let coffee = new Coffee()
coffee.init()

利用好莱坞原则,下面的代码可以达到和继承一样的效果

let Beverage = function (param) 
  let boilWater = function () 
    console.log('把水煮沸')
  
  let brew = param.brew || function () 
    throw new Error('必须传递brew方法')
  
  let pourInCup = param.pourInCup || function () 
    throw new Error('必须传递pourInCup方法')
  
  let addCondiments = param.addCondiments || function () 
    throw new Error('必须传递addCondiments方法')
  
  let customerWantsCondiments = param.customerWantsCondiments ? true : false
  let F = function () 
  // 模板方法
  F.prototype.init = function ()  
    boilWater()
    brew()
    pourInCup()
    if( customerWantsCondiments) 
      addCondiments()
    
  
  return F

 let Coffee = Beverage(
   brew: function () 
     console.log('用沸水冲泡咖啡')
   ,
   pourInCup: function () 
     console.log('把咖啡倒入杯子')
   ,
   addCondiments: function () 
     console.log('加糖和牛奶')
   
 )
 let coffee = new Coffee()
 coffee.init()

 let Tea = Beverage (
   brew: function () 
     console.log('用沸水泡茶')
   ,
   pourInCup: function () 
     console.log('把茶倒入杯子')
   ,
   customerWantsCondiments: false
 )
 let tea = new Tea()
 tea.init()

9、享元模式

  享元模式是一种用于性能优化的模式。享元模式的核心是运用共享技术来有效支持大量细粒度的对象。
  享元模式要求将对象的属性划分为内部状态和外部状态,把所有内部状态相同的对象都指定为同一个共享的对象。而外部状态可以从对象身上剥离出来,并存储在外部。

<html>
  <head>
    <title>享元模式文件上传</title>
  </head>
  <body>
    <script>
      // uploadType是内部状态,fileName和fileSize是根据场景而变化,每个文件fileName和
      // fileSize都不一样,fileName和fileSize没有办法被共享,它们只能被划分为外部状态
      let Upload = function (uploadType) 
        this.uploadType = uploadType
      
      Upload.prototype.delFile = function (id) 
        uploadManager.setExternalState(id, this)
        if (this.fileSize < 3000) 
          return this.dom.parentNode.removeChild(this.dom)
        
        if (window.confirm('确定要删除该文件吗?' + this.fileName)) 
          return this.dom.parentNode.removeChild(this.dom)
        
      

      // 工厂进行对象实例化,如果某种内部状态对应的共享对象已经被创建过,那么直接返回
      // 这个对象,否则创建一个新的对象
      let UploadFactory = (function () 
        let createFlyWeightObjs = 
        return 
          create: function (uploadType) 
            if (createFlyWeightObjs[uploadType]) 
              return createFlyWeightObjs[uploadType]
            
            return createFlyWeightObjs[uploadType] = new Upload(uploadType)
          
        
      )()

      // 管理器封装外部状态
      let uploadManager = (function () 
        // 保存所有upload对象的外部状态
        let uploadDatabase = 
        return 
          add: function (id, uploadType, fileName, fileSize) 
            let flyWeightObj = UploadFactory.create(uploadType)
            let dom = document.createElement('div')
            dom.innerHTML = '<span>文件名称:' + fileName + ',文件大小:' + fileSize + '</span>' +
              '<button class="delFile">删除</button>'
            dom.querySelector('.delFile').onclick = function () 
              flyWeightObj.delFile(id)
            
            document.body.appendChild(dom)
            uploadDatabase[id] = 
              fileName: fileName,
              fileSize: fileSize,
              dom: dom
            
            return flyWeightObj
          ,
          setExternalState: function (id, flyWeightObj) 
            let uploadData = uploadDatabase[id]
            for (let i in uploadData) 
              flyWeightObj[i] = uploadData[i]
            
          
        
      )()

      // 触发上传动作
      let id = 0
      window.startUpload = function (uploadType, files) 
        for (let i = 0, file; file = files[i++];) 
          let uploadObj = uploadManager.add(++id, uploadType, file.fileName, file.fileSize)
        
      

      startUpload('plugin', [
        
          fileName: '1.txt',
          fileSize: 1000
        ,
        
          fileName: '2.html',
          fileSize: 3000
        ,
        
          fileName: '3.txt',
          fileSize: 5000
        
      ])

      startUpload('flash', [
        
          fileName: '4.txt',
          fileSize: 1000
        ,
        
          fileName: '5.html',
          fileSize: 3000
        ,
        
          fileName: '6.txt',
          fileSize: 5000
        
      ])
    </script>
  </body>
</html>

10、职责链模式

  职责链模式的定义是:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

let order500 = function (orderType, pay, stock) 
  if (orderType === 1 && pay) 
    console.log('500元定金预购,得到100优惠券')
   else 
    return 'nextSuccessor'
  

let order200 = function (orderType, pay, stock) 
  if(orderType === 2 && pay) 
    console.log('200元定金预购,得到50优惠券')
   else 
    return 'nextSuccessor'
  

let orderNomal = function (orderType, pay, stock) 
  if (stock > 0) 
    console.log('普通购买,无优惠券')
   else 
    console.log('手机库存不足')
  


// 用AOP实现职责链
Function.prototype.after = function (fn) 
  let self = this
  return function () 
    let ret = self.apply(this, arguments)
    if (ret === 'nextSuccessor') 
      return fn.apply(this, arguments)
    
    return ret
  


let order = order500.after(order200).after(orderNomal)
order(1, true, 500)
order(2, true, 500)
order(3, true, 500)
order(1, false, 0)

11、中介者模式

  中介者模式的作用就是解除对象与对象之间的紧耦合关系。增加一个中介者对象后,所有的相关对象都通过中介者对象来通信,而不是互相引用,所以当一个对象发生改变时,只需要通知中介者对象即可。中介者使各个对象之间耦合松散,而且可以独立地改变它们之间的交互。

/**
 * @description player对象的原型方法中,不负责具体的执行逻辑,而是把操作转交给中介者对象。
 * @param any name 
 * @param any teamColor 
 */
function Player (name, teamColor) 
  this.state = 'live' // 玩家状态
  this.name = name // 角色名字
  this.teamColor = teamColor // 队伍颜色


// 玩家胜利
Player.prototype.win = function () 
  console.log('winner: ' + this.name)


// 玩家失败
Player.prototype.lose = function () 
  console.log('loser: ' + this.name)


// 玩家死亡
Player.prototype.die = function () 
  this.state = 'dead'
  // 给中介者发送消息,玩家死亡
  playerDirector.ReceiveMessage('playerDead', this)


// 移除玩家
Player.prototype.remove = function () 
  // 给中介者发送消息,移除一个玩家
  playerDirector.ReceiveMessage('removePlayer', this)


// 玩家换队
Player.prototype.changeTeam = function (color) 
  // 给中介者发送消息,玩家换队
  playerDirector.ReceiveMessage('changeTeam', this, color)


/**
 * @description 工厂函数
 */
let playerFactory = function (name, teamColor) 
  // 创造一个新的玩家对象
  let newPlayer = new Player(name, teamColor)
  // 给中介者发送消息,新增玩家
  playerDirector.ReceiveMessage('addPlayer', newPlayer)
  return newPlayer

/**
 * @description 中介者
 */
let playerDirector = (function () 
  // 保存所有玩家
  let players = 
  // 中介者可以执行的操作
  let operations = 
  /******************* 新增一个玩家 ******************/
  operations.addPlayer = function (player) 
    // 玩家队伍的颜色
    let teamColor = player.teamColor
    // 如果该颜色的玩家还没有成立队伍,则新成立一个队伍
    players[teamColor] = players[teamColor] || []
    // 添加玩家进队伍
    players[teamColor].push(player)
  
  /******************* 移除一个玩家 ******************/
  operations.removePlayer = function (player) 
    // 玩家的队伍颜色
    let teamColor = player.teamColor
    // 该队伍的所有成员
    let teamPlayers = players[teamColor] || []
    // 遍历删除
    for (let i = teamPlayers.length - 1; i >=0; i--) 
      if(teamPlayers[i] === player) 
        teamPlayers.splice(i, 1)
      
    
  
  /******************* 玩家换队 ******************/
  operations.changeTeam = function (player, newTeamColor) 
    operations.removePlayer(player)
    player.teamColor = newTeamColor
    operations.addPlayer(player)
  
  /******************* 玩家死亡 ******************/
  operations.playerDead = function (player) 
    // 玩家的队伍颜色
    let teamColor = player.teamColor
    // 玩家所在队伍
    let teamPlayers = players[teamColor]
    let all_dead = true
    for (let i = 0, player; player = teamPlayers[i++];) 
      if (player.state !== 'dead') 
        all_dead = false
        break
      
    
    if (all_dead) 
      // 本队所有玩家都输了
      for(let i = 0, player; player = teamPlayers[i++];) 
        player.lose()
      
      for (let color in players) 
        if (color !== teamColor) 
          // 其他队伍的玩家
          let teamPlayers = players[color]
          // 其他队伍所有玩家胜利
          for (let i = 0, player; player = teamPlayers[i++];) 
            player.win()
          
        
      
    
  
  /********* 负责接收player对象发送的消息 *********/
  let ReceiveMessage = function () 
    let message = Array.prototype.shift.call(arguments)
    operations[message].apply(this, arguments)
  
  return 
    ReceiveMessage: ReceiveMessage
  
)()

// 红队
let player1 = playerFactory('皮蛋', 'red')
let player2 = playerFactory('小乖', 'red')
let player3 = playerFactory('小强', 'red')
let player4 = playerFactory('小雪', 'red')
let player5 = playerFactory('小明', 'red')

<

以上是关于JavaScript设计模式与开发实践的主要内容,如果未能解决你的问题,请参考以下文章

javascript设计模式与开发实践 阅读笔记

学习笔记javascript设计模式与开发实践(职责链模式)----13 http://blog.csdn.net/pigpigpig4587/article/details/50442406#

JavaScript设计模式与开发实践:惰性函数

JavaScript设计模式与开发实践:分时函数

Javascript设计模式与开发实践——面向对象的JavaScript(多态封装继承)

摘JavaScript设计模式与开发实践--单例模式