美文网首页
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 书写优雅的代码

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