• JavaScript: 代码简洁之道


    一、变量

    1、用有意义且常用的单词命名变量

    Bad:
    const yyyymmdstr = moment().format('YYYY/MM/DD');
    Good: const currentDate
    = moment().format('YYYY/MM/DD');

    2、保持统一

    可能同一个项目对于获取用户信息,会有三个不一样的命名,应该保持统一。

    Bad:
      getUserInfo();
      getClientData();
      getCustomerRecord();
    Good: getUser()
     

    3、每个常量都该命名

    可以用 buddy.js 或者 ESLint 检测代码中未命名的常量。

    Bad:
    // 三个月之后你还能知道 86400000 是什么吗?
    setTimeout(blastOff, 86400000);
    
    Good:
    const MILLISECOND_IN_A_DAY = 86400000;
    setTimeout(blastOff, MILLISECOND_IN_A_DAY);

    4、可描述

    通过一个变量生成了一个新变量,也需要为这个新变量命名,也就是说每个变量当你看到他第一眼你就知道他是干什么的。

    Bad:
    const ADDRESS = 'One Infinite Loop, Cupertino 95014';
    const CITY_ZIP_CODE_REGEX = /^[^,\]+[,\s]+(.+?)s*(d{5})?$/;
    saveCityZipCode(ADDRESS.match(CITY_ZIP_CODE_REGEX)[1],
                    ADDRESS.match(CITY_ZIP_CODE_REGEX)[2]);
    
    Good:
    const ADDRESS = 'One Infinite Loop, Cupertino 95014';
    const CITY_ZIP_CODE_REGEX = /^[^,\]+[,\s]+(.+?)s*(d{5})?$/;
    const [, city, zipCode] = ADDRESS.match(CITY_ZIP_CODE_REGEX) || [];
    saveCityZipCode(city, zipCode);

    5、直接了当

    bad

    const locations = ['Austin', 'New York', 'San Francisco'];
    locations.forEach((l) => {
      doStuff();
      doSomeOtherStuff();
      // ...
      // ...
      // ...
      // 需要看其他代码才能确定 'l' 是干什么的。
      dispatch(l);
    });

    good

    const locations = ['Austin', 'New York', 'San Francisco'];
    locations.forEach((location) => {
      doStuff();
      doSomeOtherStuff();
      // ...
      // ...
      // ...
      dispatch(location);
    });

    4、避免无意义的前缀

    如果创建了一个对象 car,就没有必要把它的颜色命名为 carColor。

    Bad:
      const car = {
        carMake: 'Honda',
        carModel: 'Accord',
        carColor: 'Blue'
      };
    
      function paintCar(car) {
        car.carColor = 'Red';
      }
    
    Good:
    const car = {
      make: 'Honda',
      model: 'Accord',
      color: 'Blue'
    };
    
    function paintCar(car) {
      car.color = 'Red';
    }

    5、使用默认值

    Bad:
    function createMicrobrewery(name) {
      const breweryName = name || 'Hipster Brew Co.';
      // ...
    }
    
    Good:
    function createMicrobrewery(name = 'Hipster Brew Co.') {
      // ...
    }

    二、函数 

    1、参数越少越好

    如果参数超过两个,使用 ES2015/ES6 的解构语法,不用考虑参数的顺序。

    Bad:
    function createMenu(title, body, buttonText, cancellable) {
      // ...
    }
    
    Good:
    function createMenu({ title, body, buttonText, cancellable }) {
      // ...
    }
    
    createMenu({
      title: 'Foo',
      body: 'Bar',
      buttonText: 'Baz',
      cancellable: true
    });

    2、只做一件事情

    这是一条在软件工程领域流传久远的规则。严格遵守这条规则会让你的代码可读性更好,也更容易重构。如果违反这个规则,那么代码会很难被测试或者重用。

    Bad:
    function emailClients(clients) {
      clients.forEach((client) => {
        const clientRecord = database.lookup(client);
        if (clientRecord.isActive()) {
          email(client);
        }
      });
    }
    
    Good:
    function emailActiveClients(clients) {
      clients
        .filter(isActiveClient)
        .forEach(email);
    }
    function isActiveClient(client) {
      const clientRecord = database.lookup(client);    
      return clientRecord.isActive();
    }

    3、顾名思义

    看函数名就应该知道它是干啥的。

    Bad:
    function addToDate(date, month) {
      // ...
    }
    
    const date = new Date();
    
    // 很难知道是把什么加到日期中
    addToDate(date, 1);
    
    Good:
    function addMonthToDate(month, date) {
      // ...
    }
    
    const date = new Date();
    addMonthToDate(1, date);

    4、删除重复代码

    很多时候虽然是同一个功能,但由于一两个不同点,让你不得不写两个几乎相同的函数。

    要想优化重复代码需要有较强的抽象能力,错误的抽象还不如重复代码。所以在抽象过程中必须要遵循 SOLID 原则(SOLID 是什么?稍后会详细介绍)。

    Bad:

    function showDeveloperList(developers) {
      developers.forEach((developer) => {
        const expectedSalary = developer.calculateExpectedSalary();
        const experience = developer.getExperience();
        const githubLink = developer.getGithubLink();
        const data = {
          expectedSalary,
          experience,
          githubLink
        };
    
        render(data);
      });
    }
    
    function showManagerList(managers) {
      managers.forEach((manager) => {
        const expectedSalary = manager.calculateExpectedSalary();
        const experience = manager.getExperience();
        const portfolio = manager.getMBAProjects();
        const data = {
          expectedSalary,
          experience,
          portfolio
        };
    
        render(data);
      });
    }

    Good:

    function showEmployeeList(employees) {
      employees.forEach(employee => {
        const expectedSalary = employee.calculateExpectedSalary();
        const experience = employee.getExperience();
        const data = {
          expectedSalary,
          experience,
        };
    
        switch(employee.type) {
          case 'develop':
            data.githubLink = employee.getGithubLink();
            break
          case 'manager':
            data.portfolio = employee.getMBAProjects();
            break
        }
        render(data);
      })
    }

    5、对象设置默认属性

    bad:
    const menuConfig = { title: null, body: 'Bar', buttonText: null, cancellable: true }; function createMenu(config) { config.title = config.title || 'Foo'; config.body = config.body || 'Bar'; config.buttonText = config.buttonText || 'Baz'; config.cancellable = config.cancellable !== undefined ? config.cancellable : true; } createMenu(menuConfig);
    Good: const menuConfig
    = { title: 'Order', // 'body' key 缺失 buttonText: 'Send', cancellable: true }; function createMenu(config) { config = Object.assign({ title: 'Foo', body: 'Bar', buttonText: 'Baz', cancellable: true }, config); // config 就变成了: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true} // ... } createMenu(menuConfig);

    6、不要传 flag 参数

    通过 flag 的 true 或 false,来判断执行逻辑,违反了一个函数干一件事的原则。

    Bad:
    function createFile(name, temp) {
      if (temp) {
        fs.create(`./temp/${name}`);
      } else {
        fs.create(name);
      }
    }
    
    Good:
    function createFile(name) {
      fs.create(name);
    }
    function createFileTemplate(name) {
      createFile(`./temp/${name}`)
    } 

    7、避免副作用(第一部分)

    函数接收一个值返回一个新值,除此之外的行为我们都称之为副作用,比如修改全局变量、对文件进行 IO 操作等。

    当函数确实需要副作用时,比如对文件进行 IO 操作时,请不要用多个函数/类进行文件操作,有且仅用一个函数/类来处理。也就是说副作用需要在唯一的地方处理。

    副作用的三大天坑:随意修改可变数据类型、随意分享没有数据结构的状态、没有在统一地方处理副作用。

    Bad:
    // 全局变量被一个函数引用
    // 现在这个变量从字符串变成了数组,如果有其他的函数引用,会发生无法预见的错误。
    var name = 'Ryan McDermott';
    
    function splitIntoFirstAndLastName() {
      name = name.split(' ');
    }
    
    splitIntoFirstAndLastName();
    
    console.log(name); // ['Ryan', 'McDermott'];
    
    
    Good:
    var name = 'Ryan McDermott';
    var newName = splitIntoFirstAndLastName(name)
    
    function splitIntoFirstAndLastName(name) {
      return name.split(' ');
    }
    
    console.log(name); // 'Ryan McDermott';
    console.log(newName); // ['Ryan', 'McDermott'];

    8、避免副作用(第二部分)

    在 JavaScript 中,基本类型通过赋值传递,对象和数组通过引用传递。以引用传递为例:

    假如我们写一个购物车,通过 addItemToCart() 方法添加商品到购物车,修改 购物车数组。此时调用 purchase() 方法购买,由于引用传递,获取的 购物车数组 正好是最新的数据。

    看起来没问题对不对?

    如果当用户点击购买时,网络出现故障, purchase() 方法一直在重复调用,与此同时用户又添加了新的商品,这时网络又恢复了。那么 purchase() 方法获取到 购物车数组 就是错误的。

    为了避免这种问题,我们需要在每次新增商品时,克隆 购物车数组 并返回新的数组。

    Bad:
    const addItemToCart = (cart, item) => {
      cart.push({ item, date: Date.now() });
    };
    
    Good:
    const addItemToCart = (cart, item) => {
      return [...cart, {item, date: Date.now()}]
    };
  • 相关阅读:
    读书笔记 《如何阅读一本书》
    蓝桥杯 试题 历届试题 翻硬币 反转(开关问题)
    读书笔记 《如何阅读一本书》
    蓝桥杯 试题 历届试题 高僧斗法 博弈论
    读书笔记 《如何阅读一本书》
    蓝桥杯 试题 历届试题 格子刷油漆
    读书笔记 《如何阅读一本书》
    读书笔记 《如何阅读一本书》
    读书笔记 《如何阅读一本书》
    读书笔记 《如何阅读一本书》
  • 原文地址:https://www.cnblogs.com/Nyan-Workflow-FC/p/10464431.html
Copyright © 2020-2023  润新知