高质量前端代码是怎样写的?

过长的if else-if

//优化前
howManyDays(year, month){
    if(month === 1 ||
        month === 3 ||
        month === 5 ||
        month === 7 ||
        month === 8 ||
        month === 10 ||
        month === 12
    ){
        return 31
    }else if(month === 2){
        return isLeapYear(year) ? 29 : 28
    }else{
        return 30
    }
}
//优化后
howManyDays(year, month){
    const table = {
        1: 31, 3: 31, 5: 31, 7: 31, 8: 31, 10: 31, 12:31,
        4: 30, 6:30, 9: 30, 11: 30,
        2: isLeapYear(year) ? 29 : 28
    }
    return table[month]
}
//优化前
function calculateGrade(score){
    if(score>=90){
        return 'A'
    }else if(score >= 80){
        return 'B'
    }else if(score >= 70){
        return 'C'
    }else if(score >= 60){
        return 'D'
    }else {
        return 'E'
    }
}
//优化后
function calculateGrade(score){
    const table = {
        100: 'A', 
        90: 'A',
        80: 'B',
        70: 'C',
        60: 'D',
        others: 'E'
    }
    return table[Math.floor(score/10)*10] || table['others']
}    



策略/状态模式

策略模式基本结构:减少了if…else的数量,提升了代码的可读性,通过传入一个状态来选取具体的策略
假如我们需要做一个计算器,需要支持加减乘除,为了判断用户具体需要进行哪个操作,我们需要4个if…else来进行判断,如果支持更多操作,那if…else会更长,不利于阅读,看着也不优雅。所以我们可以用策略模式优化如下:

function calculator(type, a, b) {
  const strategy = {
    add: function(a, b) {
      return a + b;
    },
    minus: function(a, b) {
      return a - b;
    },
    division: function(a, b) {
      return a / b;
    },
    times: function(a, b) {
      return a * b;
    }
  }
  
  return strategy[type](a, b);
}

// 使用时
calculator('add', 1, 1);


状态模式基本架构

function stateFactor(state) {
  const stateObj = {
    status: '',
    state: {
      state1: function(){},
      state2: function(){},
    },
    run: function() {
      return this.state[this.status];
    }
  }
  
  stateObj.status = state;
  return stateObj;
}

// 使用时
stateFactor('state1').run();

外观模式(常用)

外观模式基本结构:将一个个小模块封装成更高级的接口内部,传给外部一个简单的接口
当我们设计一个模块时,里面的方法可以会设计得比较细,但是暴露给外面使用的时候,不一定非得直接暴露这些细小的接口,外部使用者需要的可能是组合部分接口来实现某个功能,我们暴露的时候其实就可以将这个组织好。

function model1() {}

function model2() {}

// 可以提供一个更高阶的接口,组合好了model1和model2给外部使用
function use() {
  model2(model1());
}

实例:常见的接口封装
外观模式说起来其实非常常见,很多模块内部都很复杂,但是对外的接口可能都是一两个,我们无需知道复杂的内部细节,只需要调用统一的高级接口就行,比如下面的选项卡模块:

// 一个选项卡类,他内部可能有多个子模块
function Tab() {}

Tab.prototype.renderHTML = function() {}    // 渲染页面的子模块
Tab.prototype.bindEvent = function() {}    // 绑定事件的子模块
Tab.prototype.loadCss = function() {}    // 加载样式的子模块

// 对外不需要暴露上面那些具体的子模块,只需要一个高级接口就行
Tab.prototype.init = function(config) {
  this.loadCss();
  this.renderHTML();
  this.bindEvent();
}

迭代器模式

迭代器模式基本结构:*当后端传来大量结构相似的数据时,js的数组并不能很好的处理这些数据时,我们可以自己按照需求封装迭代器功能
迭代器模式模式在JS里面很常见了,数组自带的forEach就是迭代器模式的一个应用,我们也可以实现一个类似的功能:

function Iterator(items) {
  this.items = items;
}

Iterator.prototype.dealEach = function(fn) {
  for(let i = 0; i < this.items.length; i++) {
    fn(this.items[i], i);
  }
}

备忘录模式

备忘录模式基本结构:**加一个缓存对象,来记录之前获取过的数据或者操作的状态,后面可以用来加快访问速度或者进行状态回滚
备忘录模式类似于JS经常使用的缓存函数,内部记录一个状态,也就是缓存,当我们再次访问的时候可以直接拿缓存数据,可以用其实现操作的前进后退功能:

function memo() {
  const cache = {};
  
  return function(arg) {
    if(cache[arg]) {
      return cache[arg];
    } else {
      // 没缓存的时候先执行方法,得到结果res
      // 然后将res写入缓存
      cache[arg] = res;
      return res;
    }
}

桥接模式

桥接模式人如其名,其实就相当于一个桥梁,把不同维度的变量桥接在一起来实现功能。假设我们需要实现三种形状(长方形,圆形,三角形),每种形状有三种颜色(红色,绿色,蓝色),这个需求有两个方案,一个方案写九个方法,每个方法实现一个图形:

function redRectangle() {}
function greenRectangle() {}
function blueRectangle() {}
function redCircle() {}
function greenCircle() {}
function blueCircle() {}
function redTriangle() {}
function greenTriangle() {}
function blueTriangle() {}


使用桥接模式后,我们可以观察重复代码拆成多个维度,再将这些维度拼接起来
function rectangle(color) {     // 长方形
  showColor(color);
}

function circle(color) {     // 圆形
  showColor(color);
}

function triangle(color) {   // 三角形
  showColor(color);
}

function showColor(color) {   // 显示颜色的方法
  
}

// 使用时,需要一个红色的圆形
let obj = new circle('red');

享元模式

当我们观察到代码中有大量相似的代码块,他们做的事情可能都是一样的,只是每次应用的对象不一样,我们就可以考虑用享元模式。现在假设我们有一个需求是显示多个弹窗,每个弹窗的文字和大小不同:

// 已经有一个弹窗类了
function Popup() {}

// 弹窗类有一个显示的方法
Popup.prototype.show = function() {}

如果我们不用享元模式,一个一个弹就是这样:
var popup1 = new Popup();
popup1.show();

var popup2 = new Popup();
popup2.show();

使用享元模式后:
var popupArr = [
  {text: 'popup 1', width: 200, height: 400},
  {text: 'popup 2', width: 300, height: 300},
]

var popup = new Popup();
for(var i = 0; i < popupArr.length; i++) {
  popup.show(popupArr[i]);    // 注意show方法需要接收参数
}

模板方法模式

模板方法模式其实类似于继承,就是我们先定义一个通用的模板骨架,然后后面在这个基础上继续扩展。我们通过一个需求来看下他的基本结构,假设我们现在需要实现一个导航组件,但是这个导航类型还比较多,有的带消息提示,有的是横着的,有的是竖着的,而且后面还可能会新增类型:

// 先建一个基础的类
function baseNav() {
}

baseNav.prototype.action = function(callback){}  //接收一个回调进行特异性处理


实例:弹窗

还是之前用过的弹窗例子,我们要做一个大小文字可能不同的弹窗组件,只是这次我们的弹窗还有取消和确定两个按钮,这两个按钮在不同场景下可能有不同的行为,比如发起请求什么的。但是他们也有一个共同的操作,就是点击这两个按钮后弹窗都会消失,这样我们就可以把共同的部分先写出来,作为一个模板:

function basePopup(word, size) {
  this.word = word;
  this.size = size;
  this.dom = null;
}

basePopup.prototype.init = function() {
  // 初始化DOM元素
  var div = document.createElement('div');
  div.innerHTML = this.word;
  div.style.width = this.size.width;
  div.style.height = this.size.height;
  
  this.dom = div;
}

// 取消的方法
basePopup.prototype.cancel = function() {
  this.dom.style.display = 'none';
}

// 确认的方法
basePopup.prototype.confirm = function() {
  this.dom.style.display = 'none';
}

工厂模式

**封装的模块就像一个工厂一样批量的产出需要的对象。常见工厂模式的一个特征就是调用的时候不需要使用new,而且传入的参数比较简单。但是调用次数可能比较频繁,经常需要产出不同的对象,频繁调用时不用new也方便很多。一个工厂模式的代码结构如下所示:

function factory(type) {
  switch(type) {
    case 'type1':
      return new Type1();
    case 'type2':
      return new Type2();
    case 'type3':
      return new Type3();
  }
}
// 我们传入了type,然后工厂根据不同的type来创建不同的对象
实例: 弹窗组件
我们项目需要一个弹窗,弹窗有几种:消息型弹窗,确认型弹窗,取消型弹窗,他们的颜色和内容可能是不一样的,工厂模式改造:
 // 新加一个方法popup把这几个类都包装起来
function popup(type, content, color) {
  switch(type) {
    case 'infoPopup':
      return new infoPopup(content, color);
    case 'confirmPopup':
      return new confirmPopup(content, color);
    case 'cancelPopup':
      return new cancelPopup(content, color);
  }
}
// 调用方法
let infoPopup1 = popup('infoPopup', content, color); 
改造成面向对象
上述代码虽然实现了工厂模式,但是switch始终感觉不是很优雅。我们使用面向对象改造下popup,将它改为一个类,将不同类型的弹窗挂载在这个类上成为工厂方法:
function popup(type, content, color) {
  // 如果是通过new调用的,返回对应类型的弹窗
  if(this instanceof popup) {
    return new this[type](content, color);
  } else {
    // 如果不是new调用的,使用new调用,会走到上面那行代码
    return new popup(type, content, color);
  }
}

// 各种类型的弹窗全部挂载在原型上成为实例方法
popup.prototype.infoPopup = function(content, color) {}
popup.prototype.confirmPopup = function(content, color) {}
popup.prototype.cancelPopup = function(content, color) {}

参考链接https://blog.csdn.net/m0_52009348/article/details/122395937

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容