08月12, 2018

javascript设计模式笔记

这周周六上班,所以周末就直接呆在杭州了。一个人在宿舍,也怪无聊的。

上午本来想将安卓的环境搭建了一下,下了一小时的studio,然后折腾了一下,似乎电脑环境有些问题,就没再折腾了。看来暂时的学RN,只能先跑跑IOS虚拟机了。

本周将慕课网里面的一门实战给看了:Javascript 设计模式系统讲解与应用,个人觉得还不错。

下面做一个简单的总结吧。

SOLID五大设计原则

  • S-单一职责原则
  • O-开放封闭原则
  • L-李氏置换原则
  • I-接口独立原则
  • D-依赖导致原则

工厂模式

当需要根据不同参数产生不同实例,这些实例都有相同的行为,这时候我们可以使用工厂模式,简化实现的过程,同时也可以减少每种对象所需的代码量。工厂模式有利于消除对象间的耦合,提供更大的灵活性。

class jQuery {
    constructor(selector) {
        let slice = Array.prototype.slice
        let dom = slice.call(document.querySelectorAll(selector))
        let len = dom ? dom.length : 0
        for (let i = 0; i < len; i++) {
            this[i] = dom[i]
        }
        this.length = len
        this.selector = selector || ''
    }
    append(node) {

    }
    addClass(name) {

    }
    html(data) {

    }
    // 此处省略若干 API
}
window.$ = function (selector) {
    return new jQuery(selector)
}

单例模式

class SingleObject {
    login() {
        console.log('login...')
    }
}
SingleObject.getInstance = (function () {
    let instance
    return function () {
        if (!instance) {
            instance = new SingleObject();
        }
        return instance
    }
})()

// 测试
let obj1 = SingleObject.getInstance()
obj1.login()
let obj2 = SingleObject.getInstance()
obj2.login()
console.log(obj1 === obj2)

使用场景:购物车,全局缓存对象,线程池等。

JavaScript 单例模式

适配器模式

举个例子,拿我之前写的一个页面来说,js中有 (".xxx")$.ajax的代码,如果哪一天我想干掉jquery,那为了方便起见,最好的办法是重写 $.ajax方法。

const $ = function(selector) {
     return document.querySelect(selector);
}
$.ajax = function() {
      // 这里的代码并不完整
      return fetch();
}

装饰器模式

一般可以理解为js中的类装饰器和方法装饰器。

// 类装饰器
function mixins(...list) {
  return function (target) {
    Object.assign(target.prototype, ...list)
  }
}

const Foo = {
  foo() { alert('foo') }
}

@mixins(Foo)
class MyClass {}

let obj = new MyClass();
obj.foo()
// 方法装饰器
function readonly(target, name, descriptor){
  // descriptor对象原来的值如下
  // {
  //   value: specifiedFunction,
  //   enumerable: false,
  //   configurable: true,
  //   writable: true
  // };
  descriptor.writable = false;
  return descriptor;
}

class Person {
    constructor() {
        this.first = 'A'
        this.last = 'B'
    }

    @readonly
    name() { return `${this.first} ${this.last}` }
}

var p = new Person()
console.log(p.name())
p.name = function () {} // 这里会报错,因为 name 是只读属性
// 方法装饰器
function log(target, name, descriptor) {
  var oldValue = descriptor.value;

  descriptor.value = function() {
    console.log(`Calling ${name} with`, arguments);
    return oldValue.apply(this, arguments);
  };

  return descriptor;
}

class Math {
  @log
  add(a, b) {
    return a + b;
  }
}

const math = new Math();
const result = math.add(2, 4);
console.log('result', result);

github上有一个库:core-decorators,封装了一些常用的装饰器。

代理模式

在js中的应用有事件代理、jq的proxy、es6的Proxy。

这里说一下后两个。

jq的proxy

用来处理this。

jQuery.proxy()

当然说真的,有了箭头函数之后,这个似乎使用也越来越少了。不过在维护老项目时,还是可以使用一下的。

Proxy

// 明星
let star = {
    name: '张XX',
    age: 25,
    phone: '13910733521'
}

// 经纪人
let agent = new Proxy(star, {
    get: function (target, key) {
        if (key === 'phone') {
            // 返回经纪人自己的手机号
            return '18611112222'
        }
        if (key === 'price') {
            // 明星不报价,经纪人报价
            return 120000
        }
        return target[key]
    },
    set: function (target, key, val) {
        if (key === 'customPrice') {
            if (val < 100000) {
                // 最低 10w
                throw new Error('价格太低')
            } else {
                target[key] = val
                return true
            }
        }
    }
})

// 主办方
console.log(agent.name)
console.log(agent.age)
console.log(agent.phone)
console.log(agent.price)

// 想自己提供报价(砍价,或者高价争抢)
agent.customPrice = 150000
// agent.customPrice = 90000  // 报错:价格太低
console.log('customPrice', agent.customPrice)

外观模式

外观模式(门面模式),是一种相对简单而又无处不在的模式。外观模式提供一个高层接口,这个接口使得客户端或子系统更加方便调用。

JavaScript设计模式之外观模式

观察者模式

// 主题,接收状态变化,触发每个观察者
class Subject {
    constructor() {
        this.state = 0
        this.observers = []
    }
    getState() {
        return this.state
    }
    setState(state) {
        this.state = state
        this.notifyAllObservers()
    }
    attach(observer) {
        this.observers.push(observer)
    }
    notifyAllObservers() {
        this.observers.forEach(observer => {
            observer.update()
        })
    }
}

// 观察者,等待被触发
class Observer {
    constructor(name, subject) {
        this.name = name
        this.subject = subject
        this.subject.attach(this)
    }
    update() {
        console.log(`${this.name} update, state: ${this.subject.getState()}`)
    }
}

// 测试代码
let s = new Subject()
let o1 = new Observer('o1', s)
let o2 = new Observer('o2', s)
let o3 = new Observer('o3', s)

s.setState(1)
s.setState(2)
s.setState(3)

像node的event、流,还有像jquery的Callbacks都是属于观察者模式。

// jQuery的Callbacks演示
var callbacks = $.Callbacks() // 注意大小写
callbacks.add(function (info) {
    console.log('fn1', info)
})
callbacks.add(function (info) {
    console.log('fn2', info)
})
callbacks.add(function (info) {
    console.log('fn3', info)
})
callbacks.fire('gogogo')
callbacks.fire('fire')

迭代器模式

class Iterator {
    constructor(conatiner) {
        this.list = conatiner.list
        this.index = 0
    }
    next() {
        if (this.hasNext()) {
            return this.list[this.index++]
        }
        return null
    }
    hasNext() {
        if (this.index >= this.list.length) {
            return false
        }
        return true
    }
}

class Container {
    constructor(list) {
        this.list = list
    }
    getIterator() {
        return new Iterator(this)
    }
}

// 测试代码
let container = new Container([1, 2, 3, 4, 5])
let iterator = container.getIterator()
while(iterator.hasNext()) {
    console.log(iterator.next())
}
// es6迭代
let arr = [1, 2, 3, 4]
let nodeList = document.getElementsByTagName('p')
let m = new Map()
m.set('a', 100)
m.set('b', 200)

// function each(data) {
//     // 生成遍历器
//     let iterator = data[Symbol.iterator]()

//     // console.log(iterator.next())  // 有数据时返回 {value: 1, done: false}
//     // console.log(iterator.next())
//     // console.log(iterator.next())
//     // console.log(iterator.next())
//     // console.log(iterator.next())  // 没有数据时返回 {value: undefined, done: true}

//     let item = {done: false}
//     while (!item.done) {
//         item = iterator.next()
//         if (!item.done) {
//             console.log(item.value)
//         }
//     }
// }

function each(data) {
    for (let item of data) {
        console.log(item)
    }
}

each(arr)
each(nodeList)
each(m)

除此之外,还有像es6的generator

状态模式

class State {
    constructor(color) {
        this.color = color
    }
    handle(context) {
        console.log(`turn to ${this.color} light`)
        context.setState(this)
    }
}

class Context {
    constructor() {
        this.state = null
    }
    setState(state) {
        this.state = state
    }
    getState() {
        return this.state
    }
}

// 测试代码
let context = new Context()

let greed = new State('greed')
let yellow = new State('yellow')
let red = new State('red')

// 绿灯亮了
greed.handle(context)
console.log(context.getState())
// 黄灯亮了
yellow.handle(context)
console.log(context.getState())
// 红灯亮了
red.handle(context)
console.log(context.getState())

使用npm包:javascript-state-machine

简单使用如下:

// 状态机模型
var fsm = new StateMachine({
    init: '收藏',  // 初始状态,待收藏
    transitions: [
        {
            name: 'doStore', 
            from: '收藏',
            to: '取消收藏'
        },
        {
            name: 'deleteStore',
            from: '取消收藏',
            to: '收藏'
        }
    ],
    methods: {
        // 执行收藏
        onDoStore: function () {
            alert('收藏成功')
            updateText()
        },
        // 取消收藏
        onDeleteStore: function () {
            alert('已取消收藏')
            updateText()
        }
    }
})

var $btn = $('#btn')

// 点击事件
$btn.click(function () {
    if (fsm.is('收藏')) {
        fsm.doStore(1)
    } else {
        fsm.deleteStore()
    }
})

// 更新文案
function updateText() {
    $btn.text(fsm.state)
}

// 初始化文案
updateText()

其他模式

  • 原型模式
  • 桥接模式
  • 组合模式
  • 享元模式
  • 策略模式
  • 模板模式
  • 职责链模式
  • 命令模式
  • 备忘录模式
  • 中介者模式
  • 访问者模式
  • 解释器模式

结语

我个人感觉其他模式并不是太重要,所以也没有一一举例,在视频中老师也给了几个示例,但不多,有些个模式是没有的。

除了设计模式之外,最重要的还有的一个事:就是要学会画UML类图。

我们可以在拿到实际的需求之后,前端就可以开始画类图,那个可以思考一下,大概需要哪些类,类上面需要有哪些属性和方法。这个我想才是这个视频教会我的东西。

最后,在此感谢!

本文链接:www.my-fe.pub/post/javascript-design-mode-note.html

-- EOF --

Comments

评论加载中...

注:如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理。