前端设计模式

SOLID设计原则

  • s: 单一原则:一个类只做一种类型责任,当这个类需要承当其他类型的责任的时候,就需要分解这个类
  • o: 开放封闭原则:对外扩展是开放的,对于修改是封闭的
  • l: 里氏置换原则:当一个子类的实例应该能够替换任何其超类的实例时,它们之间才具有is-A关系
  • i: 接口分离原则:使用多个专门的接口比使用单一的总接口总要好
  • d: 依赖倒置原则: 抽象不应该依赖于细节,细节应该依赖于抽象

单例模式

单例就是保证一个类只有一个实例,实现方法一般是先判断实例存在与否,如果存在直接返回,如果不存在就创建了再返回

应用场景
在Jquery中

jquery中只会创建一个$对象,会先去判断$对象是否存在,如果不存在创建一个新的对象

if(window.jQuery!=null){
  return window.jQuery;
}else{
    //init~~~~~~~
}

模态窗口

一般网站都会有用户系统,登录窗口或者toast提示,会去创建一个getInstance的静态方法来统一调用

工厂模式

工厂模式主要出现在面向对象创建实例的过程中,其本质是为了更方便生成实例,构造函数和工厂方法分离。

应用场景
React.createElement
class Vnode(tag, attrs, children){
    ……
}

React.createElement = function(tag, attrs, children) {
	return new Vnode(tag, attrs, children)
}
jquery

当我们在使用jquery的时候,直接返回的是一个jquery实例,而不是需要new Jquery(′div′)才能够返回

class JQuery{
    construtor(selector){
        ……
    }
    ……
}

window.$ = function(selector) {
  return new JQuery(selector);
}

函数缓存(备忘模式)

高阶函数

高阶函数就是那种输入参数里面有一个或者多个函数,输出也是函数的函数,这个在js里面主要是利用闭包实现的,最简单的就是经常看到的在一个函数内部输出另一个函数,每次执行都会缓存方法执行的结果

适配器模式

适配器模式是一对相对简单的模式。但适配器模式在JS中的使用场景很多,在参数的适配上,有许多库和框架都使用适配器模式;数据的适配在解决前后端数据依赖上十分重要。我们要认识到的是适配器模式本质上是一个”亡羊补牢”的模式,它解决的是现存的两个接口之间不兼容的问题,你不应该在软件的初期开发阶段就使用该模式;如果在设计之初我们就能够统筹的规划好接口的一致性,那么适配器就应该尽量减少使用。

适配器模式不会增加或者减少接口的功能,提供一个不同的接口

应用场景
React-Redux

redux为了和react适配,所有有mapStateToProps()这个函数来吧state转为Props外部状态,这样就可以从外部又回到组件内了。你看这个就是适配器模式(Adaptor)

观察者模式

  • 发布 && 订阅
  • 一对N(一对一,一对多)

Promise实现

function Promise(fn) {
    // 加入状态
    var state = 'pending';
    var value = null;
    //callbacks为订阅数组,因为可能同时有很多个回调
    var callbacks = [];  
    //加入订阅数组
    this.then = function (onFulfilled) {
        if(state==='fullfilled'){
            callbacks.push(onFulfilled);
            //支持链式调用
            return this;
        }
    };
    //遍历订阅回调数组,执行回调
    function resolve(value) {
       // 加入setTimeout,保证先注册then,然后在执行回调
       setTimeout(function() {
        state = 'fulfilled';
        callbacks.forEach(function (callback) {
            callback(value);
        });
    }, 0)
    }

    fn(resolve);
}
参考

30分钟,让你彻底明白Promise原理

Events模块

node.js中有一个底层的Events模块,被其他的模块大量使用,可以说是node非常核心的一个模块了.其本质还是一个观察者模式

const eventEmitter = require('events').EventEmitter

const emitter1 = new eventEmitter()

emitter1.on('some', info=> {
    console.log('fn1', info)
})
emitter1.on('some', info=> {
    console.log('fn2', info)
})	
emitter1.emit('some', 'xxxx')

vue中的watch函数

vue有个订阅数据中心,有订阅队列、发布方法、还有通知方法

生命周期

还有vue与react中的生命周期钩子,下面是vue源码,在new Vue的时候使用callHook方法执行我们定义的方法

迭代器模式

迭代器模式就是为了简化有序集合的遍历产生的模式,统一遍历的方式,在ES6中通过Symbol.iterator标识数据是可遍历的

function each(data, callback) {
    //通过js提供的Symbol.iterator返回迭代器
    const iterator = data[Symbol.iterator]()
    let item = {done: false}
    while(!item.done) {
        item.iterator.next()
        if(!item.done) {
            callback(item)
        }
    }
}
let log = (data)=>{
    console.log(data)
}

let arr = [1,2,3,4,5,6]
let map = new Map()
map.set('a', 100)
map.set('b', 200)

each(arr)
each(map)

拥有迭代器模式的数据,可以通过for……of语法糖来遍历

  for(let item of data) {
        cb(item)
}

装饰器模式

装饰器它能够动态为一个函数、方法或者类添加新的行为,而不需要通过子类继承或直接修改函数的代码来获取新的行为能力

  • 为对象添加新的功能(扩展功能)
  • 不改变原有的结构和逻辑
应用场景
简单案例
@test
class Demo{
    
}

function test(target){
    target.isDec=true
}

alert(Demo.isDec)
@test(true)
class Demo{
    
}

function test(isDec){
   return function(target) {
        target.isDec=isDec
   }
}

alert(Demo.isDec)
函数柯里化
 const curry = func => (...rest) => {
            // coding here
            var length = func.length;
            let arr = [].concat(rest)
            let temp = function (..._rest) {
                arr = arr.concat(_rest)
                if (arr.length === length) {
                    func(...arr)
                }
                return temp
            }
            return temp
        };

        const fn = curry(function (x, y, z) {
            console.log(x, y, z);
        });

        // 保证以下方式皆可调用成功,能够打印一次性出 4 次 1 2 3
        fn(1, 2, 3);
        fn(1, 2)(3);
        fn(1)(2)(3);
        fn(1)(2, 3);
React-Redux
class TodoListContainer extends React.Component {
}


export default connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoListContainer)



// 源码实现

connectHOC = connectAdvanced;
mergePropsFactories = defaultMergePropsFactories;
selectorFactory = defaultSelectorFactory;
function connect (
  mapStateToProps,
  mapDispatchToProps,
  mergeProps,
  {
  pure = true,
  areStatesEqual = strictEqual, // 严格比较是否相等
  areOwnPropsEqual = shallowEqual, // 浅比较
  areStatePropsEqual = shallowEqual,
  areMergedPropsEqual = shallowEqual,
  renderCountProp, // 传递给内部组件的props键,表示render方法调用次数
  // props/context 获取store的键
  storeKey = 'store',
  ...extraOptions
  } = {}
) {
  const initMapStateToProps = match(mapStateToProps, mapStateToPropsFactories, 'mapStateToProps')
  const initMapDispatchToProps = match(mapDispatchToProps, mapDispatchToPropsFactories, 'mapDispatchToProps')
  const initMergeProps = match(mergeProps, mergePropsFactories, 'mergeProps')

  // 调用connectHOC方法
  connectHOC(selectorFactory, {
    // 如果mapStateToProps为false,则不监听store state
    shouldHandleStateChanges: Boolean(mapStateToProps),
    // 传递给selectorFactory
    initMapStateToProps,
    initMapDispatchToProps,
    initMergeProps,
    pure,
    areStatesEqual,
    areOwnPropsEqual,
    areStatePropsEqual,
    areMergedPropsEqual,
    renderCountProp, // 传递给内部组件的props键,表示render方法调用次数
    // props/context 获取store的键
    storeKey = 'store',
    ...extraOptions // 其他配置项
  });
}

function connectAdvanced (
  selectorFactory,
  {
    renderCountProp = undefined, // 传递给内部组件的props键,表示render方法调用次数
    // props/context 获取store的键
    storeKey = 'store',
    ...connectOptions
  } = {}
) {
  // 获取发布订阅器的键
  const subscriptionKey = storeKey + 'Subscription';
  const contextTypes = {
    [storeKey]: storeShape,
    [subscriptionKey]: subscriptionShape,
  };
  const childContextTypes = {
    [subscriptionKey]: subscriptionShape,
  };

  return function wrapWithConnect (WrappedComponent) {
    const selectorFactoryOptions = {
      // 如果mapStateToProps为false,则不监听store state
      shouldHandleStateChanges: Boolean(mapStateToProps),
      // 传递给selectorFactory
      initMapStateToProps,
      initMapDispatchToProps,
      initMergeProps,
      ...connectOptions,
      ...others
      renderCountProp, // render调用次数
      shouldHandleStateChanges, // 是否监听store state变更
      storeKey,
      WrappedComponent
    }

    // 返回拓展过props属性的Connect组件
    return hoistStatics(Connect, WrappedComponent)
  }
}
属性只读

只能执行,不能修改,使用到了Object.defineProperty() 的 属性描述descriptor 的 wirtebale 属性

class Person{
    constructor(){
        this.first = "zhangsan0"
        this.last = "3"
    }
    
    @readonly
    name(){
        return `${this.first}${this.last}`
    }
}

function readonly(target,name,descriptor){
    descriptor.writebale = false
    return descriptor
}
日志打印
class Math {

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

function log(target,name,descriptor) {

    let oldValue = descriptor.value
    descriptor.value = function() {
        console.log('日志打印')
        return oldValue.apply(this,arguments)
    }
    return descriptor
}

const math = Math();
//console.log('日志打印')
const result = math.add(100,33); 

在日常开发中,可用其用来进行日志打印,埋点上报

代理模式

代理模式不愿意或者不想对原对象进行直接操作,我们使用代理就是让它帮原对象进行一系列的操作,代理和本体接口保持一致性,提供一模一样的接口

场景
事件代理
 <div class="container">
    <a>1</a>
    <a>2</a>
    <a>3</a>
    <a>4</a>
    <a>5</a>
</div>
    
 window.onload = function () {
    document.getElementsByClassName('container')[0].addEventListener('click', function (e) {
        if (e.target.nodeName === "A") {
            alert(e.target.innerHTML)
        }
    })
}
ES6 Proxy
const list = [1, 2];

const observer = new Proxy(list, {
  get: function(target,key){
    // 代理处理,如果获取的是元素的第二个元素
     if(key==1){
         return 3;
     }
  },
  set: function(target, key, value, receiver) {
    console.log(`prop: ${value} is changed!`);
    return Reflect.set(...arguments);
  },
});
observer[3] = 4;
console.log(observer[1])

外观模式

它为子系统中的一组接口提供一个一致的界面, Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。引入外观角色之后,使用者只需要直接与外观角色交互,使用者与子系统之间的复杂关系由外观角色来实现,从而降低了系统的耦合度

场景
批量设置style

将多个样式设置批量设置

function setStyles( id, styles ){
    var element = document.getElementById( id );
    for( var key in styles ){
        if( styles.hasOwnProperty( key ) ){
            element.style[ key ] = styles[ key ];
        }
    }
}

setStyles( 'content', {
    color : 'red',
    height : '200px'
} );
组件库设计

在组件目录中,统一有个出口 index.tsx 包括了各个子组件的引用,统一 export

posted @ 2020-05-10 13:06  浮云随笔  阅读(191)  评论(0编辑  收藏  举报