• js笔记 |整洁代码技巧


    笔记源于:https://gitee.com/mirrors_trending/clean-code-javascript?_from=gitee_search

    变量

    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 使用可理解的名称

    Bad

    setTimeout(blastOff, 86400000);// 不知道86400000是什么意思
    

    Good

    //通过大写的常量来声明
    const MILLISECONDS_IN_A_DAY = 60 * 60 * 24 * 1000;//86400000;
    
    setTimeout(blastOff, MILLISECONDS_IN_A_DAY);
    

    4 使用具有解释性的名称

    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);
    

    5 使用明确的映射

    Bad

    const locations = ["Austin", "New York", "San Francisco"];
    locations.forEach(l => {
      doStuff();
      doSomeOtherStuff();
      // ...
      // ...
      // ...
      // Wait, what is `l` for again?
      dispatch(l);
    });
    

    Good

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

    6 不加没有意义的语境

    如果你的class或者对象名称已经告诉你一些东西,你就不必在变量中重复

    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";
    }
    

    7 使用默认arguments而非条件判断

    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) {
      // ...
    }
    
    createMenu("Foo", "Bar", "Baz", true);
    

    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 删除重复代码

    有时候会因为小的差别而重新写一遍代码,这会导致很多重复代码。

    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 "manager":
            data.portfolio = employee.getMBAProjects();
            break;
          case "developer":
            data.githubLink = employee.getGithubLink();
            break;
        }
        render(data);
      });
    }
    

    5 使用Object.assign来设置默认对象

    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",
      buttonText: "Send",
      cancellable: true
    };
    
    function createMenu(config) {
      let finalConfig = Object.assign(
        {
          title: "Foo",
          body: "Bar",
          buttonText: "Baz",
          cancellable: true
        },
        config
      );
      return finalConfig
    }
    
    createMenu(menuConfig);
    

    6 不要使用flag让函数完成多件事

    Bad

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

    Good

    function createFile(name) {
      fs.create(name);
    }
    
    function createTempFile(name) {
      createFile(`./temp/${name}`);
    }
    

    7 避免副作用

    • 如果修改一些全局变量,容易产生副作用。要避免在没有结构的对象之间分享状态、或是使用可在任何地方写入数据的类型。

      Bad

      let name = "Ryan McDermott";
      function splitIntoFirstAndLastName() {
        name = name.split(" ");
      }
      splitIntoFirstAndLastName();
      console.log(name); // ['Ryan', 'McDermott'];
      

    Good
    ```js
    function createFile(name) {
    fs.create(name);
    }

    function createTempFile(name) {
      createFile(`./temp/${name}`);
    }
    ```
    
    • 为了避免一些误触导致对象或者数组进行修改,可对其进行编辑并返回克隆,让原数据不会受到影响。

      Bad

      const addItemToCart = (cart, item) => {
        cart.push({ item, date: Date.now() });
      };
      

    Good
    js const addItemToCart = (cart, item) => { return [...cart, { item, date: Date.now() }]; };

    8 不要写全局函数

    使用全局函数是不好的做法,因为很可能和其他库冲突。比如你想写一个显示两数组差异的diff方法,你可能会写在Array.prototype上,那它可能会和另一个执行相同操作的库发生冲突。用ES2015/ES6类并将数组扩展为全局会更好

    Bad

    Array.prototype.diff = function diff(comparisonArray) {
      const hash = new Set(comparisonArray);
      return this.filter(elem => !hash.has(elem));
    };
    

    Good

    class SuperArray extends Array {
      diff(comparisonArray) {
        const hash = new Set(comparisonArray);
        return this.filter(elem => !hash.has(elem));
      }
    }
    

    9 封装条件

    Bad

    if (fsm.state === "fetching" && isEmpty(listNode)) {
      // ...
    }
    

    Good

    function shouldShowSpinner(fsm, listNode) {
      return fsm.state === "fetching" && isEmpty(listNode);
    }
    
    if (shouldShowSpinner(fsmInstance, listNodeInstance)) {
      // ...
    }
    

    10 避免否定条件

    Bad

    function isDOMNodeNotPresent(node) {
      // ...
    }
    
    if (!isDOMNodeNotPresent(node)) {
      // ...
    }
    

    Good

    function isDOMNodePresent(node) {
      // ...
    }
    
    if (isDOMNodePresent(node)) {
      // ...
    }
    

    11 避免条件语句

    Bad

    class Airplane {
      // ...
      getCruisingAltitude() {
        switch (this.type) {
          case "777":
            return this.getMaxAltitude() - this.getPassengerCount();
          case "Air Force One":
            return this.getMaxAltitude();
          case "Cessna":
            return this.getMaxAltitude() - this.getFuelExpenditure();
        }
      }
    }
    

    Good

    class Airplane {
      // ...
    }
    
    class Boeing777 extends Airplane {
      // ...
      getCruisingAltitude() {
        return this.getMaxAltitude() - this.getPassengerCount();
      }
    }
    
    class AirForceOne extends Airplane {
      // ...
      getCruisingAltitude() {
        return this.getMaxAltitude();
      }
    }
    
    class Cessna extends Airplane {
      // ...
      getCruisingAltitude() {
        return this.getMaxAltitude() - this.getFuelExpenditure();
      }
    }
    

    12 避免判断数据类型

    • 使用一致的方法

      Bad

      function travelToTexas(vehicle) {
        if (vehicle instanceof Bicycle) {
          vehicle.pedal(this.currentLocation, new Location("texas"));
        } else if (vehicle instanceof Car) {
          vehicle.drive(this.currentLocation, new Location("texas"));
        }
      }
      

      Good

      function travelToTexas(vehicle) {
        vehicle.move(this.currentLocation, new Location("texas"));
      }
      
    • 使用TypeScript

      Bad

      function combine(val1, val2) {
        if (
          (typeof val1 === "number" && typeof val2 === "number") ||
          (typeof val1 === "string" && typeof val2 === "string")
        ) {
          return val1 + val2;
        }
      
        throw new Error("Must be of type String or Number");
      }
      

      Good

      function combine(val1, val2) {
        return val1 + val2;
      }
      

    对象与类

    1 使用getters和setters

    这种方法可能会比直接在对象中查找属性要好,下面是该方法的一些优点:

    • 除了获得对象属性之外,如果还想做更多的事,就不需要查找和更改代码库中的每个访问器。
    • 通过set是验证变得更简单
    • 封装内部表示

    Bad

    function makeBankAccount() {
      return {
        balance: 0
      };
    }
    
    const account = makeBankAccount();
    account.balance = 100;
    

    Good

    function makeBankAccount() {
      let balance = 0;// this one is private
    	
      //getter
      function getBalance() {
        return balance;
      }
    	
      //setter
      function setBalance(amount) {
        // ... 在更新数据前进行验证
        balance = amount;
      }
    
      return {
        getBalance,
        setBalance
      };
    }
    
    const account = makeBankAccount();
    account.setBalance(100);
    

    2 使用链式方法

    Bad

    class Car {
      constructor(make, model, color) {
        this.make = make;
        this.model = model;
        this.color = color;
      }
    
      setMake(make) {
        this.make = make;
      }
    
      setModel(model) {
        this.model = model;
      }
    
      setColor(color) {
        this.color = color;
      }
    
      save() {
        console.log(this.make, this.model, this.color);
      }
    }
    
    const car = new Car("Ford", "F-150", "red");
    car.setColor("pink");
    car.save();
    

    Good

    class Car {
      constructor(make, model, color) {
        this.make = make;
        this.model = model;
        this.color = color;
      }
    
      setMake(make) {
        this.make = make;
        return this;
      }
    
      setModel(model) {
        this.model = model;
        return this;
      }
    
      setColor(color) {
        this.color = color;
        return this;
      }
    
      save() {
        console.log(this.make, this.model, this.color);
        return this;
      }
    }
    
    const car = new Car("Ford", "F-150", "red").setColor("pink").save();
    

    3 优先考虑组合而非继承

    如果是下面的情况,使用继承会更有意义,否则使用组合即可:

    • 你的继承表示“是”关系,而不是“具有”关系
    • 需要复用基类中的代码
    • 你想通过更改基类对派生类进行全局更改

    Bad

    class Employee {
      constructor(name, email) {
        this.name = name;
        this.email = email;
      }
    }
    
    // 不好,因为员工“拥有”税收数据,而EmployeeTaxData不是员工的类型
    class EmployeeTaxData extends Employee {
      constructor(ssn, salary) {
        super();
        this.ssn = ssn;
        this.salary = salary;
      }
    }
    

    Good

    class EmployeeTaxData {
      constructor(ssn, salary) {
        this.ssn = ssn;
        this.salary = salary;
      }
    }
    
    class Employee {
      constructor(name, email) {
        this.name = name;
        this.email = email;
      }
    
      setTaxData(ssn, salary) {
        this.taxData = new EmployeeTaxData(ssn, salary);
      }
      // ...
    }
    

    其他

    1 单一责任原则

    Bad

    class UserSettings {
      constructor(user) {
        this.user = user;
      }
    
      changeSettings(settings) {
        if (this.verifyCredentials()) {
          // ...
        }
      }
    
      verifyCredentials() {
        // ...
      }
    }
    

    Good

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

    2 开放封闭原则

    应该在不更改现有代码的情况下添加新功能

    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 => {
            // 转化响应并返回
          });
        } else if (this.adapter.name === "nodeAdapter") {
          return makeHttpCall(url).then(response => {
            // 转化响应并返回
          });
        }
      }
    }
    
    function makeAjaxCall(url) {
      // 请求并返回promise
    }
    
    function makeHttpCall(url) {
      // 请求并返回promise
    }
    

    Good

    class AjaxAdapter extends Adapter {
      constructor() {
        super();
        this.name = "ajaxAdapter";
      }
    
      request(url) {
        // 请求并返回promise
      }
    }
    
    class NodeAdapter extends Adapter {
      constructor() {
        super();
        this.name = "nodeAdapter";
      }
    
      request(url) {
        // 请求并返回promise
      }
    }
    
    class HttpRequester {
      constructor(adapter) {
        this.adapter = adapter;
      }
    
      fetch(url) {
        return this.adapter.request(url).then(response => {
          // 转化响应并返回
        });
      }
    }
    
  • 相关阅读:
    vs 调试的时候 使用IP地址,局域网的设备可以访问并调试
    jQuery Easing 使用方法及其图解
    win10使用Composer-Setup安装Composer以及使用Composer安装Yii2最新版
    PHP 字符串数组按照拼音排序的问题
    yii2 查询数据库语法
    css禁用鼠标点击事件
    内容显示在HTML页面底端的一些处理方式
    UltraISO制作U盘启动盘
    Swift中使用MPMoviePlayerController实现自定义视频播放器界面
    关于dismissViewControllerAnimated值得注意的一点(deinit)
  • 原文地址:https://www.cnblogs.com/sanhuamao/p/14723929.html
Copyright © 2020-2023  润新知