# 综合应用

# 介绍

  • 使用 jQuery 做一个模拟购物车的示例
  • 包括:显示购物列表、加入购物车、从购物车删除

# 用到的设计模式

  • 工程模式:$('xxx'), 创建商品
// 1
  constructor(id) {
    // 工厂模式
    this.$el = $('#' + id)
  }
// 2
// 工厂函数
export default function(list, itemData) {
  if(itemData.discount) {
    itemData = createDiscount(itemData)
  }
  return new Item(list, itemData)
}
  • 单例模式:购物车
class Cart {
  constructor() {
    this.list = []
  }
  add(data) {
    this.list.push(data)
  }
  del(id) {
    this.list = this.list.filter(item => {
      if (item.id === id) {
        return false
      }
      return true
    })
  }
  getList() {
    return this.list.map(item => {
      return item.name
    }).join('\n')
  }
}

// 返回单例

let getCart = (function() {
  let cart
  return function() {
    if(!cart) {
      cart = new Cart()
    }
    return cart
  }
})()

// 一个函数
export default getCart
  • 装饰器模式:打点统计
  // 添加到购物车
  @log('add')
  addToCartHandle() {
    this.cart.add(this.data)
  }
  // 从购物车删除
  @log('del')
  deleteFromCartHandle() {
    this.cart.del(this.data.id)
  }
 export function log(type) {
   return function(target, name, descriptor) {
     let oldValue = descriptor.value
     descriptor.value = function() {
      // 在此统一打印日志
      console.log(`日志上报 ${type}`)
      // 执行原有的方法
      return oldValue.apply(this, arguments)
     }
     return descriptor
   }
 }
  • 观察者模式:网页事件,Promise
  • 状态模式:添加到购物车 & 从购物车删除
  initBtn() {
    let $el = this.$el
    let $btn = $('<button>')
    const _this = this
    let fsm = new StateMachine({
      init: '加入购物车',
      transitions: [
        {
          name: 'addToCart',
          form: '加入购物车',
          to: '从购物车删除'
        },
        {
          name: 'deleteFromCart',
          form: '从购物车删除',
          to: '加入购物车'
        }
      ],
      methods: {
        // 加入购物车
        onAddToCart() {
          _this.addToCartHandle()
          updateText()
        },
        // 从购物车移除
        onDeleteFromCart() {
          _this.deleteFromCartHandle()
          updateText()
        }
      }
    })
    function updateText() {
      $btn.text(fsm.state)
    }
    $btn.click(() => {
      if(fsm.is('加入购物车')) {
        fsm.addToCart()
      } else {
        fsm.deleteFromCart()
      }
    })
    updateText()
    $el.append($btn)
  }
  • 模板方法模式:渲染有统一的方法,内部包含了各模块渲染
  • 代理模式:打折商品信息处理
function createDiscount(itemData) {
  // 用代理做折扣显示
  return new Proxy(itemData, {
    get: function(target, key, receiver) {
      if(key === 'name') {
        return `${target[key]}【折扣】`
      }
      if(key === 'price') {
        return target[key] * 0.8
      }
      return target[key]
    }
  })
}

# UML 类图

在这里插入图片描述

# 代码

在这里插入图片描述

  • index.js
import App from './demo/App'

let app = new App('app')
app.init()
  • App.js
import $ from 'jquery'
import ShoppingCart from './ShoppingCart/ShoppingCart'
import List from './List/List'

export default class App {
  constructor(id) {
    // 工厂模式
    this.$el = $('#' + id)
  }
  // 初始化购物车
  initShoppingCart() {
    let shoppingCart = new ShoppingCart(this)
    shoppingCart.init()
  }
  // 初始化列表
  initList() {
    let list = new List(this)
    list.init()
  }
  init() {
    this.initShoppingCart()
    this.initList()
  }
}
  • ./List/List.js
import $ from 'jquery'
import { GET_LIST } from '../config/config'
import createItem from '../Item/CreateItem'
export default class List {
  constructor(app) {
    this.app = app
    this.$el = $('<div>')
  }
  // 获取数据
  loadData() {
    // 返回 promise 实例
    return fetch(GET_LIST).then(result => {
      return result.json()
    })
  }
  // 生成列表
  initItemList(data) {
    data.forEach(itemData => {
      // 创建一个 Item 然后 init
      let item = createItem(this, itemData)
      item.init()
    })
  }
  // 渲染
  render() {
    this.app.$el.append(this.$el)
  }
  init() {
    this.loadData().then(data => {
      this.initItemList(data)
    }).then(() => {
      // 渲染
      this.render()
    })
  }
}
  • ./ShoppingCart/GetCart.js
class Cart {
  constructor() {
    this.list = []
  }
  add(data) {
    this.list.push(data)
  }
  del(id) {
    this.list = this.list.filter(item => {
      if (item.id === id) {
        return false
      }
      return true
    })
  }
  getList() {
    return this.list.map(item => {
      return item.name
    }).join('\n')
  }
}

// 返回单例

let getCart = (function() {
  let cart
  return function() {
    if(!cart) {
      cart = new Cart()
    }
    return cart
  }
})()

// 一个函数
export default getCart
  • ./ShoppingCart/ShoppingCart.js
 import $ from 'jquery'
 import getCart from './GetCart'
 export default class ShoppingCart {
   constructor(app) {
     this.app = app
     this.$el = $('<div>').css({
       'padding-bottom': '10px',
       'border-bottom': '1px solid #ccc'
     })
     this.cart = getCart()
   }
   initBtn() {
     let $btn = $('<button>购物车</button>')
     $btn.click(() => {
       this.showCart()
     })
     this.$el.append($btn)
   }
   showCart() {
     alert(this.cart.getList())
   }
   render() {
     this.app.$el.append(this.$el)
   }
   init() {
     this.initBtn()
     this.render()
   }
   
 }
  • ./Item/Item.js
import $ from 'jquery'
import StateMachine from 'javascript-state-machine'
import getCart from '../ShoppingCart/GetCart'
import { log } from '../util/log'
export default class Item {
  constructor(list, data) {
    this.list = list
    this.data = data
    this.$el = $('<div>')
    this.cart = getCart()
  }
  initContent() {
    let $el = this.$el
    let data = this.data
    $el.append($(`<p>名称: ${data.name}</p>`))
    $el.append($(`<p>价格: ${data.price}</p>`))
  }
  initBtn() {
    let $el = this.$el
    let $btn = $('<button>')
    const _this = this
    let fsm = new StateMachine({
      init: '加入购物车',
      transitions: [
        {
          name: 'addToCart',
          form: '加入购物车',
          to: '从购物车删除'
        },
        {
          name: 'deleteFromCart',
          form: '从购物车删除',
          to: '加入购物车'
        }
      ],
      methods: {
        // 加入购物车
        onAddToCart() {
          _this.addToCartHandle()
          updateText()
        },
        // 从购物车移除
        onDeleteFromCart() {
          _this.deleteFromCartHandle()
          updateText()
        }
      }
    })
    function updateText() {
      $btn.text(fsm.state)
    }
    $btn.click(() => {
      if(fsm.is('加入购物车')) {
        fsm.addToCart()
      } else {
        fsm.deleteFromCart()
      }
    })
    updateText()
    $el.append($btn)
  }
  // 添加到购物车
  @log('add')
  addToCartHandle() {
    this.cart.add(this.data)
  }
  // 从购物车删除
  @log('del')
  deleteFromCartHandle() {
    this.cart.del(this.data.id)
  }
  render() {
    this.list.$el.append(this.$el)
  }
  init() {
    this.initContent()
    this.initBtn()
    this.render()
  }
}
  • ./Item/CreateItem.js
import Item from './Item'

function createDiscount(itemData) {
  // 用代理做折扣显示
  return new Proxy(itemData, {
    get: function(target, key, receiver) {
      if(key === 'name') {
        return `${target[key]}【折扣】`
      }
      if(key === 'price') {
        return target[key] * 0.8
      }
      return target[key]
    }
  })
}

// 工厂函数
export default function(list, itemData) {
  if(itemData.discount) {
    itemData = createDiscount(itemData)
  }
  return new Item(list, itemData)
}
  • ./util/log.js
 export function log(type) {
   return function(target, name, descriptor) {
     let oldValue = descriptor.value
     descriptor.value = function() {
      // 在此统一打印日志
      console.log(`日志上报 ${type}`)
      // 执行原有的方法
      return oldValue.apply(this, arguments)
     }
     return descriptor
   }
 }
  • ./config/config.js
 export const GET_LIST = '/api/list.json'
  • ./api/list.json
[
  {
      "id": 1,
      "name": "《JS 基础面试题》",
      "price": 149,
      "discount": 1
  },
  {
      "id": 2,
      "name": "《JS 高级面试题》",
      "price": 366,
      "discount": 1
  },
  {
      "id": 3,
      "name": "《React 模拟大众点评 webapp》",
      "price": 248,
      "discount": 0
  },
  {
      "id": 4,
      "name": "《zepto 设计与源码解读》",
      "price": 0,
      "discount": 0
  }
]

评 论:

更新: 11/21/2020, 7:00:56 PM