美文网首页
JS 书写优雅的代码

JS 书写优雅的代码

作者: 风之化身呀 | 来源:发表于2019-02-12 10:13 被阅读15次

1、多条件判断

Bad

 */
const onButtonClick = (status,identity)=>{
  if(identity == 'guest'){
    if(status == 1){
      //do sth
    }else if(status == 2){
      //do sth
    }else if(status == 3){
      //do sth
    }else if(status == 4){
      //do sth
    }else if(status == 5){
      //do sth
    }else {
      //do sth
    }
  }else if(identity == 'master') {
    if(status == 1){
      //do sth
    }else if(status == 2){
      //do sth
    }else if(status == 3){
      //do sth
    }else if(status == 4){
      //do sth
    }else if(status == 5){
      //do sth
    }else {
      //do sth
    }
  }
}

Good

const actions = ()=>{
  const functionA = ()=>{/*do sth*/}       // 单独业务逻辑
  const functionB = ()=>{/*do sth*/}       // 单独业务逻辑
  const functionC = ()=>{/*send log*/}   // 公共业务逻辑
  return new Map([
    [/^guest_[1-4]$/,functionA],  // 用连字符拼装来组合多元判断条件
    [/^guest_5$/,functionB],
    [/^guest_.*$/,functionC],
    //...
  ])
}

const onButtonClick = (identity,status)=>{
  let action = [...actions()].filter(([key,value])=>(key.test(`${identity}_${status}`)))
  action.forEach(([key,value])=>value.call(this))
}

将condition写作正则存到Map里,利用数组循环的特性,符合正则条件的逻辑都会被执行,那就可以同时执行公共逻辑和单独逻辑。

2、Solid 原则

SOLID 是几个单词首字母组合而来,分别表示 单一功能原则开闭原则里氏替换原则接口隔离原则以及依赖反转原则

单一功能原则

如果一个类干的事情太多太杂,会导致后期很难维护。我们应该厘清职责,各司其职减少相互之间依赖。

Bad:

class UserSettings {
  constructor(user) {
    this.user = user;
  }

  changeSettings(settings) {
    if (this.verifyCredentials()) {
      // ...
    }
  }

  verifyCredentials() {
    // ...
  }
}

Good:

class UserAuth {
  constructor(user) {
    this.user = user;
  }
  verifyCredentials() {
    // ...
  }
}

class UserSetting {
  constructor(user) {
    this.user = user;
    this.auth = new UserAuth(this.user);
  }
  changeSettings(settings) {
    if (this.auth.verifyCredentials()) {
      // ...
    }
  }
}
}

开闭原则

“开”指的就是类、模块、函数都应该具有可扩展性,“闭”指的是它们不应该被修改。也就是说你可以新增功能但不能去修改源码。

Bad:

class AjaxAdapter extends Adapter {
  constructor() {
    super();
    this.name = 'ajaxAdapter';
  }
}

class NodeAdapter extends Adapter {
  constructor() {
    super();
    this.name = 'nodeAdapter';
  }
}

class HttpRequester {
  constructor(adapter) {
    this.adapter = adapter;
  }

  fetch(url) {
    if (this.adapter.name === 'ajaxAdapter') {
      return makeAjaxCall(url).then((response) => {
        // 传递 response 并 return
      });
    } else if (this.adapter.name === 'httpNodeAdapter') {
      return makeHttpCall(url).then((response) => {
        // 传递 response 并 return
      });
    }
  }
}

function makeAjaxCall(url) {
  // 处理 request 并 return promise
}

function makeHttpCall(url) {
  // 处理 request 并 return promise
}

Good:

class AjaxAdapter extends Adapter {
  constructor() {
    super();
    this.name = 'ajaxAdapter';
  }

  request(url) {
    // 处理 request 并 return promise
  }
}

class NodeAdapter extends Adapter {
  constructor() {
    super();
    this.name = 'nodeAdapter';
  }

  request(url) {
    // 处理 request 并 return promise
  }
}

class HttpRequester {
  constructor(adapter) {
    this.adapter = adapter;
  }

  fetch(url) {
    return this.adapter.request(url).then((response) => {
      // 传递 response 并 return
    });
  }
}

里氏替换原则

名字很唬人,其实道理很简单,就是子类不要去重写父类的方法。

Bad:

// 长方形
class Rectangle {
  constructor() {
    this.width = 0;
    this.height = 0;
  }

  setColor(color) {
    // ...
  }

  render(area) {
    // ...
  }

  setWidth(width) {
    this.width = width;
  }

  setHeight(height) {
    this.height = height;
  }

  getArea() {
    return this.width * this.height;
  }
}

// 正方形
class Square extends Rectangle {
  setWidth(width) {
    this.width = width;
    this.height = width;
  }

  setHeight(height) {
    this.width = height;
    this.height = height;
  }
}

function renderLargeRectangles(rectangles) {
  rectangles.forEach((rectangle) => {
    rectangle.setWidth(4);
    rectangle.setHeight(5);
    const area = rectangle.getArea(); 
    rectangle.render(area);
  });
}

const rectangles = [new Rectangle(), new Rectangle(), new Square()];
renderLargeRectangles(rectangles);

Good:

class Shape {
  setColor(color) {
    // ...
  }

  render(area) {
    // ...
  }
}

class Rectangle extends Shape {
  constructor(width, height) {
    super();
    this.width = width;
    this.height = height;
  }

  getArea() {
    return this.width * this.height;
  }
}

class Square extends Shape {
  constructor(length) {
    super();
    this.length = length;
  }

  getArea() {
    return this.length * this.length;
  }
}

function renderLargeShapes(shapes) {
  shapes.forEach((shape) => {
    const area = shape.getArea();
    shape.render(area);
  });
}

const shapes = [new Rectangle(4, 5), new Rectangle(4, 5), new Square(5)];
renderLargeShapes(shapes);

依赖反转原则

说就两点:

  1. 高层次模块不能依赖低层次模块,它们依赖于抽象接口。
  2. 抽象接口不能依赖具体实现,具体实现依赖抽象接口。

总结下来就两个字,解耦。

Bad:

// 库存查询
class InventoryRequester {
  constructor() {
    this.REQ_METHODS = ['HTTP'];
  }

  requestItem(item) {
    // ...
  }
}

// 库存跟踪
class InventoryTracker {
  constructor(items) {
    this.items = items;

    // 这里依赖一个特殊的请求类,其实我们只是需要一个请求方法。
    this.requester = new InventoryRequester();
  }

  requestItems() {
    this.items.forEach((item) => {
      this.requester.requestItem(item);
    });
  }
}

const inventoryTracker = new InventoryTracker(['apples', 'bananas']);
inventoryTracker.requestItems();

Good:

// 库存跟踪
class InventoryTracker {
  constructor(items, requester) {
    this.items = items;
    this.requester = requester;
  }

  requestItems() {
    this.items.forEach((item) => {
      this.requester.requestItem(item);
    });
  }
}

// HTTP 请求
class InventoryRequesterHTTP {
  constructor() {
    this.REQ_METHODS = ['HTTP'];
  }

  requestItem(item) {
    // ...
  }
}

// webSocket 请求
class InventoryRequesterWS {
  constructor() {
    this.REQ_METHODS = ['WS'];
  }

  requestItem(item) {
    // ...
  }
}

// 通过依赖注入的方式将请求模块解耦,这样我们就可以很轻易的替换成 webSocket 请求。
const inventoryTracker = new InventoryTracker(['apples', 'bananas'], new InventoryRequesterHTTP());
inventoryTracker.requestItems();

接口隔离原则

JavaScript 几乎没有接口的概念,所以这条原则很少被使用。官方定义是“客户端不应该依赖它不需要的接口”,也就是接口最小化,把接口解耦。

Bad:

class DOMTraverser {
  constructor(settings) {
    this.settings = settings;
    this.setup();
  }

  setup() {
    this.rootNode = this.settings.rootNode;
    this.animationModule.setup();
  }

  traverse() {
    // ...
  }
}

const $ = new DOMTraverser({
  rootNode: document.getElementsByTagName('body'),
  animationModule() {} // Most of the time, we won't need to animate when traversing.
  // ...
});

Good:

class DOMTraverser {
  constructor(settings) {
    this.settings = settings;
    this.options = settings.options;
    this.setup();
  }

  setup() {
    this.rootNode = this.settings.rootNode;
    this.setupOptions();
  }

  setupOptions() {
    if (this.options.animationModule) {
      // ...
    }
  }

  traverse() {
    // ...
  }
}

const $ = new DOMTraverser({
  rootNode: document.getElementsByTagName('body'),
  options: {
    animationModule() {}
  }
});

3、其他

  • 及早 return
    发现无效的条件时,及早return
// 及早 return,能使代码可读性更强
function test(fruit, quantity) {
  const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];

  if (!fruit) throw new Error('No fruit!');

  if (redFruits.includes(fruit)) {
    console.log('red');
    if (quantity > 10) {
      console.log('big quantity');
    }
  }
}
  • 每一个变(常)量都应该命名
    对于常量,不要硬编码;对于计算出来的变量最好也要命名
// bad
if (value.length < 8) { // 为什么要小于8,8表示啥?长度,还是位移,还是高度?Oh,my God!!
    ....
}
// good
const MAX_INPUT_LENGTH = 8;
if (value.length < MAX_INPUT_LENGTH) { // 一目了然,不能超过最大输入长度
    ....
}
// bad 
const address = 'One Infinite Loop, Cupertino 95014';
const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
saveCityZipCode(
  address.match(cityZipCodeRegex)[1], // 这个公式到底要干嘛,对不起,原作者已经离职了。自己看代码
  address.match(cityZipCodeRegex)[2], // 这个公式到底要干嘛,对不起,原作者已经离职了。自己看代码
);
// good
const address = 'One Infinite Loop, Cupertino 95014';
const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
const [, city, zipCode] = address.match(cityZipCodeRegex) || [];
saveCityZipCode(city, zipCode);
  • 函数传参数时最好使用对象的形式
// bad
page.getSVG(api, true, false); // true和false啥意思,一目不了然

// good
page.getSVG({
    imageApi: api,
    includePageBackground: true, // 一目了然,知道这些true和false是啥意思
    compress: false,
})

参考

相关文章

  • JS 书写优雅的代码

    1、多条件判断 Bad Good 将condition写作正则存到Map里,利用数组循环的特性,符合正则条件的逻辑...

  • JS 如何书写优雅的代码

    优雅的代码:符合规范,代码合理、易于阅读和维护。 一、备注 1.文档注释: 简单描述当前js文件作用。是页面js逻...

  • 优雅的书写代码!

    如何才能写好代码呢? 写代码就是学一门语言然后开始撸代码吗?看完了我的《GoF设计模式》系列文章的同学或者本身已经...

  • JQuery&bootstrap

    内容回顾 jQuery的书写步骤引入JQ的包(xxx.js)书写JQ代码$(function(){ JQ代码...

  • 本周JS学习总结

    一,JS的三种书写位置 JS的书写位置:行内,内嵌和外部 1.行内式JS 可以将单行或少量js代码写在HTML标签...

  • 怎么使用swiper快速实现轮播图?方法在这里!

    首先引入框架js和css代码 书写css代码 然后写html代码 最后动起来需要js代码 这样就可以滑动了

  • JS笔记6:JS入门

    1、JS简介 2、第一行Js代码 JS书写方式 3、变量 4、JS注释 // 单行注释/*多行注释*/

  • HTML5学习笔记-3.JavaScript在HTML中

    在HTML中,JavaScript书写方式 页内JS:在当前网页的script元素中写 JS代码 外部JS

  • 图片上传到七牛云

    引入element-ui组件库 在main.js中 书写vue代码 书写后端代码,获取token Base64.j...

  • JavaScript的初体验

    js的书写位置 JS有三种书写位置,分别是行内、内嵌和外部。 行内式 内嵌式 可以将多行JS代码写到 标签中 内嵌...

网友评论

      本文标题:JS 书写优雅的代码

      本文链接:https://www.haomeiwen.com/subject/bheheqtx.html