Moment For Technology

A front-end necessary JS code specification

Posted on Sept. 23, 2022, 3:35 p.m. by 李龍
Category: The front end Tag: javascript The front end

variable

Use meaningful, readable variable names

Example:

    var yyyymmdstr = moment().format('YYYY/MM/DD')
Copy the code

Is:

    var yearMonthDay = moment().format('YYYY/MM/DD');
Copy the code

Define constants using ES6 const

The "constant" defined in the counter example using "var" is mutable.

When a constant is declared, it should remain immutable throughout the program.

Example:

    var FIRST_US_PRESIDENT = "George Washington";
Copy the code

Is:

   const FIRST_US_PRESIDENT = "George Washington";
Copy the code

Use a common naming style for variable names that have similar functions

Example:

    getUserInfo();
    getClientData();
    getCustomerRecord();
Copy the code

Is:

    getUser();
Copy the code

Use easy-to-retrieve names

We need to read more code than we write, and it's important to make the code readable and easy to retrieve. Reading code with obscure variable names is a pretty bad experience for the reader. Make your variable names easy to retrieve.

Example:

    // What is 525600?
    for (var i = 0; i  525600; i++) {
      runCronJob();
    }
Copy the code

Is:

    // Declare them as capitalized `var` globals.
    var MINUTES_IN_A_YEAR = 525600;
    for (var i = 0; i  MINUTES_IN_A_YEAR; i++) {
      runCronJob();
    }

Copy the code

Use explanatory variables (that is, meaningful variable names)

Example:

    const cityStateRegex = /^(.+)[,\\s]+(.+?) \s*(\d{5})? $/;
    saveCityState(cityStateRegex.match(cityStateRegex)[1], 
    cityStateRegex.match(cityStateRegex)[2]);
Copy the code

Is:

    const ADDRESS = 'One Infinite Loop, Cupertino 95014';
    var cityStateRegex = /^(.+)[,\\s]+(.+?) \s*(\d{5})? $/;
    var match = ADDRESS.match(cityStateRegex)
    var city = match[1];
    var state = match[2];
    saveCityState(city, state);
Copy the code

Don't beat around the bush too much

Explicit is better than implicit.

Example:

    var locations = ['Austin'.'New York'.'San Francisco'];
    locations.forEach((l) = { doStuff(); doSomeOtherStuff(); . . .// What is l?
      dispatch(l);
    });
Copy the code

Is:

    var locations = ['Austin'.'New York'.'San Francisco'];
    locations.forEach((location) = { doStuff(); doSomeOtherStuff(); . . . dispatch(location); });Copy the code

Avoid repetitive descriptions

When the class/object name already makes sense, the variable name does not need to be repeated.

Example:

    var Car = {
      carMake: 'Honda'.carModel: 'Accord'.carColor: 'Blue'
    };

    function paintCar(car) {
      car.carColor = 'Red';
    }
Copy the code

Is:

    var Car = {
      make: 'Honda'.model: 'Accord'.color: 'Blue'
    };

    function paintCar(car) {
      car.color = 'Red';
    }
Copy the code

Avoid meaningless conditional judgments

Example:

    function createMicrobrewery(name) {
      var breweryName;
      if (name) {
        breweryName = name;
      } else {
        breweryName = 'Hipster Brew Co.'; }}Copy the code

Is:

    function createMicrobrewery(name) {
      var breweryName = name || 'Hipster Brew Co.'
    }
Copy the code

function

Function parameters (ideally no more than 2)

It is necessary to limit the number of function arguments to make it easier to test functions. Too many parameters makes it difficult to test each parameter of a function with an effective test case.

Avoid functions with more than three parameters. In general, having more than two parameters means that the function is too complex and needs to be re-optimized. When you do need multiple parameters, in most cases you can consider encapsulating them into an object.

JS is very convenient to define objects. When multiple parameters are needed, a single object can be used instead.

Is:

    function createMenu(title, body, buttonText, cancellable) {... }Copy the code

Example:

    var menuConfig = {
      title: 'Foo'.body: 'Bar'.buttonText: 'Baz'.cancellable: true
    }

    function createMenu(menuConfig) {... }Copy the code

The singleness of function

This is one of the most important principles of software functionality.

Functions that do not have a single function are difficult to refactor, test, and understand. Single-purpose functions are easy to refactor and make your code cleaner.

Example:

    function emailClients(clients) {
      clients.forEach(client=  {
        let clientRecord = database.lookup(client);
        if(clientRecord.isActive()) { email(client); }}); }Copy the code

Is:

    function emailClients(clients) {
      clients.forEach(client=  {
        emailClientIfNeeded(client);
      });
    }

    function emailClientIfNeeded(client) {
      if(isClientActive(client)) { email(client); }}function isClientActive(client) {
      let clientRecord = database.lookup(client);
      return clientRecord.isActive();
    }
Copy the code

The function name should clearly indicate its function

Example:

    function dateAdd(date, month) {
      // ...
    }

    let date = new Date(a);DateAdd (date, 1)
    dateAdd(date, 1);
Copy the code

Is:

    function dateAddMonth(date, month) {
      // ...
    }

    let date = new Date(a); dateAddMonth(date,1);
Copy the code

Functions should only have one layer of abstraction

When a function requires more than one level of abstraction, it usually means that the function is too complex and needs to be broken down to improve its reusability and testability.

Example:

    function parseBetterJSAlternative(code) {
      let REGEXES = [
        // ...
      ];

      let statements = code.split(' ');
      let tokens;
      REGEXES.forEach((REGEX) =  {
        statements.forEach((statement) =  {
          // ...})});let ast;
      tokens.forEach((token) =  {
        // lex...
      });

      ast.forEach((node) =  {
        // parse...})}Copy the code

Is:

    function tokenize(code) {
      let REGEXES = [
        // ...
      ];

      let statements = code.split(' ');
      let tokens;
      REGEXES.forEach((REGEX) =  {
        statements.forEach((statement) =  {
          // ...})});return tokens;
    }

    function lexer(tokens) {
      let ast;
      tokens.forEach((token) =  {
        // lex...
      });

      return ast;
    }

    function parseBetterJSAlternative(code) {
      let tokens = tokenize(code);
      let ast = lexer(tokens);
      ast.forEach((node) =  {
        // parse...})}Copy the code

Remove duplicate code

Never, never, never have duplicate code under any loop.

This is pointless and potentially dangerous. Repetitive code means that logic changes in more than one place. The weak type of JS makes the function more universal. Take advantage of it.

Example:

    function showDeveloperList(developers) {
      developers.forEach(developer=  {
        var expectedSalary = developer.calculateExpectedSalary();
        var experience = developer.getExperience();
        var githubLink = developer.getGithubLink();
        var data = {
          expectedSalary: expectedSalary,
          experience: experience,
          githubLink: githubLink
        };

        render(data);
      });
    }

    function showManagerList(managers) {
      managers.forEach(manager=  {
        var expectedSalary = manager.calculateExpectedSalary();
        var experience = manager.getExperience();
        var portfolio = manager.getMBAProjects();
        var data = {
          expectedSalary: expectedSalary,
          experience: experience,
          portfolio: portfolio
        };

        render(data);
      });
    }
Copy the code

Is:

    function showList(employees) {
      employees.forEach(employee=  {
        var expectedSalary = employee.calculateExpectedSalary();
        var experience = employee.getExperience();
        var portfolio;

        if (employee.type === 'manager') {
          portfolio = employee.getMBAProjects();
        } else {
          portfolio = employee.getGithubLink();
        }
    var data = {
          expectedSalary: expectedSalary,
          experience: experience,
          portfolio: portfolio
        };

        render(data);
      });
    }
Copy the code

Simplify code with default parameters

Example:

    function writeForumComment(subject, body) {
      subject = subject || 'No Subject';
      body = body || 'No text';
    }
Copy the code

Is:

    function writeForumComment(subject = 'No subject', body = 'No text') {... }Copy the code

Set the default Object using object. assign

Example:

    var 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);
Copy the code

Is:

    var menuConfig = {
      title: 'Order'.// User did not include 'body' key
      buttonText: 'Send'.cancellable: true
    }

    function createMenu(config) {
      config = Object.assign({
        title: 'Foo'.body: 'Bar'.buttonText: 'Baz'.cancellable: true
      }, config);

      // config now equals: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true}
      // ...
    }

    createMenu(menuConfig);
Copy the code

Do not use flags as function arguments

This usually means that the functional singleness of the function has been broken. Consider repartitioning the function at this point.

Example:

    function createFile(name, temp) {
      if (temp) {
        fs.create('./temp/' + name);
      } else{ fs.create(name); }}Copy the code

Example:

function createTempFile(name) {
  fs.create('./temp/'+ name); } -- -- -- -- -- -- -- -- -- --function createFile(name) {
  fs.create(name);
}
Copy the code

Avoid side effects

A function is said to have a side effect when it produces behavior other than "taking a value and returning a result." Such as writing a file, changing global variables, or giving all your money to a stranger.

Programs do require this behavior in some cases, such as writing files in the previous example. Instead of modifying a file with multiple functions/classes, keep these functions together. Accomplish this requirement with one and only one Service.

Example:

    // Global variable referenced by following function.
    // If we had another function that used this name, now it'd be an array and it could break it.
    var name = 'Ryan McDermott';

    function splitIntoFirstAndLastName() {
      name = name.split(' ');
    }

    splitIntoFirstAndLastName();

    console.log(name); // ['Ryan', 'McDermott'];
Copy the code

Is:

    function splitIntoFirstAndLastName(name) {
      return name.split(' ');
    }

    var name = 'Ryan McDermott'
    var newName = splitIntoFirstAndLastName(name);

    console.log(name); // 'Ryan McDermott';
    console.log(newName); // ['Ryan', 'McDermott'];
Copy the code

Don't write global functions

It is a bad practice to pollute globally in JS. Doing so may conflict with other libraries, and the user calling your API is unaware of the situation until he or she gets an exception in the real environment.

Imagine the following example: If you wanted to extend Array in JS by adding a diff function to show the difference between two arrays, what would you do? You could write diff to array. prototype, but doing so would conflict with other libraries with similar requirements. What if another library's requirement for diff is to compare the differences between the first and last elements in an array?

Simple extensions to global arrays using classes in ES6 are obviously a better choice.

Example:

    Array.prototype.diff = function(comparisonArray) {
      var values = [];
      var hash = {};

      for (var i of comparisonArray) {
        hash[i] = true;
      }

      for (var i of this) {
        if (!hash[i]) {
          values.push(i);
        }
      }

      return values;
    }
Copy the code

Is:

    class SuperArray extends Array {
      constructor(. args) {
        super(... args); }diff(comparisonArray) {
        var values = [];
        var hash = {};

        for (var i of comparisonArray) {
          hash[i] = true;
        }

        for (var i of this) {
          if (!hash[i]) {
            values.push(i);
          }
        }

        returnvalues; }}Copy the code

Use functional programming

Functional programming is cleaner and easier to test. Use this style whenever possible.

Example:

    const programmerOutput = [
      {
        name: 'Uncle Bobby'.linesOfCode: 500
      }, {
        name: 'Suzie Q'.linesOfCode: 1500
      }, {
        name: 'Jimmy Gosling'.linesOfCode: 150
      }, {
        name: 'Gracie Hopper'.linesOfCode: 1000}];var totalOutput = 0;

    for (var i = 0; i  programmerOutput.length; i++) {
      totalOutput += programmerOutput[i].linesOfCode;
    }
Copy the code

Is:

    const programmerOutput = [
      {
        name: 'Uncle Bobby'.linesOfCode: 500
      }, {
        name: 'Suzie Q'.linesOfCode: 1500
      }, {
        name: 'Jimmy Gosling'.linesOfCode: 150
      }, {
        name: 'Gracie Hopper'.linesOfCode: 1000}];var totalOutput = programmerOutput
      .map((programmer) =  programmer.linesOfCode)
      .reduce((acc, linesOfCode) =  acc + linesOfCode, 0);
Copy the code

Package judgment condition

Example:

    if (fsm.state === 'fetching'  isEmpty(listNode)) {
      / / /...
    }
Copy the code

Is:

    function shouldShowSpinner(fsm, listNode) {
      return fsm.state === 'fetching'  isEmpty(listNode);
    }

    if (shouldShowSpinner(fsmInstance, listNodeInstance)) {
      // ...
    }
Copy the code

Avoid "negative situation" judgments

Example:

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

    if(! isDOMNodeNotPresent(node)) {// ...
    }
Copy the code

Is:

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

    if (isDOMNodePresent(node)) {
      // ...
    }
Copy the code

Avoid conditional judgment

That seems unlikely.

The first thing most people think when they hear this is, "How can you do anything else without an IF?" In many cases a polymorphism can achieve the same goal.

The second question is why. The answer is the one we mentioned before: keep the function simple.

Example:

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

Is:

    class Airplane {
      / /...
    }

    class Boeing777 extends Airplane {
      / /...
      getCruisingAltitude() {
        returngetMaxAltitude() - getPassengerCount(); }}class AirForceOne extends Airplane {
      / /...
      getCruisingAltitude() {
        returngetMaxAltitude(); }}class Cessna extends Airplane {
      / /...
      getCruisingAltitude() {
        returngetMaxAltitude() - getFuelExpenditure(); }}Copy the code

Avoiding Type Judgments (Part 1)

JS is a weakly typed language, which means functions can accept arguments of any type.

Sometimes that can get you into trouble, and you make some type judgments about the parameters. There are many ways to avoid these situations.

Example:

    function travelToTexas(vehicle) {
      if (vehicle instanceof Bicycle) {
        vehicle.peddle(this.currentLocation, new Location('texas'));
      } else if (vehicle instanceof Car) {
        vehicle.drive(this.currentLocation, new Location('texas')); }}Copy the code

Is:

    function travelToTexas(vehicle) {
      vehicle.move(this.currentLocation, new Location('texas'));
    }
Copy the code

Avoiding Type Judgments (Part 2)

Consider TypeScript when you need to work with strings, integers, arrays, etc., and you can't use polymorphism and still need to type it.

Example:

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

Is:

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

Avoid over-optimization

Modern browsers optimize code automatically at runtime. Sometimes optimizing code artificially can be a waste of time.

Example:

    Len is used here because in older browsers,
    // Using the method directly causes the list.length value to be repeated for each loop,
    In modern browsers, optimizations are done automatically, which is unnecessary
    for (var i = 0, len = list.length; i  len; i++) {
      // ...
    }    
Copy the code

Is:

    for (var i = 0; i  list.length; i++) {
      // ...
    } 
Copy the code

Remove invalid code

Code that is no longer invoked should be deleted promptly.

Example:

    function oldRequestModule(url) {
      // ...
    }

    function newRequestModule(url) {
      // ...
    }

    var req = newRequestModule;
    inventoryTracker('apples', req, 'www.inventory-awesome.io'); 
Copy the code

Is:

    function newRequestModule(url) {
      // ...
    }

    var req = newRequestModule;
    inventoryTracker('apples', req, 'www.inventory-awesome.io');
Copy the code

Objects and data structures

Use getters and setters

JS has no interface or type, so implementing this pattern is difficult because we don't have keywords like public and private.

However, using getters and setters to get the object's data is far more advantageous than using the dot operator directly. Why is that?

  1. When additional operations need to be performed on acquired object properties.
  2. performsetRules can be added to judge the validity of variables.
  3. Encapsulates the internal logic.
  4. Log and error handling can be easily added at access time.
  5. You can override the default behavior when inheriting the class.
  6. Lazy loading can be done when fetching data from the server.

Example:

    class BankAccount {
      constructor() {
               this.balance = 1000; }}let bankAccount = new BankAccount();

    // Buy shoes...
    bankAccount.balance = bankAccount.balance - 100;
Copy the code

Is:

    class BankAccount {
      constructor() {
               this.balance = 1000;
      }

      // It doesn't have to be prefixed with `get` or `set` to be a getter/setter
      withdraw(amount) {
            if (verifyAmountCanBeDeducted(amount)) {
              this.balance -= amount; }}}let bankAccount = new BankAccount();

    // Buy shoes...
    bankAccount.withdraw(100);
Copy the code

Let objects have private members

This can be done with closures

Example:

    var Employee = function(name) {
      this.name = name;
    }

    Employee.prototype.getName = function() {
      return this.name;
    }

    var employee = new Employee('John Doe');
    console.log('Employee name: ' + employee.getName()); // Employee name: John Doe
    delete employee.name;
    console.log('Employee name: ' + employee.getName()); // Employee name: undefined
Copy the code

Is:

    var Employee = (function() {
      function Employee(name) {
        this.getName = function() {
          return name;
        };
      }

      returnEmployee; } ());var employee = new Employee('John Doe');
    console.log('Employee name: ' + employee.getName()); // Employee name: John Doe
    delete employee.name;
    console.log('Employee name: ' + employee.getName()); // Employee name: John Doe
Copy the code

class

Single Responsibility Principle (SRP)

As stated in Code Cleanliness, "There should be no more than one reason to change a class."

The idea of cramming multiple functions into a single class is tempting, but it results in your classes not being conceptually cohesive and often having to be modified.

It is necessary to minimize the number of changes to a class. If a class has too much functionality and too much clutter, when you change a small part of it, it's hard to imagine what impact that change will have on other modules in the code base that depend on that class.

Example:

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

      changeSettings(settings) {
        if (this.verifyCredentials(user)) {
          // ...}}verifyCredentials(user) {
        // ...}}Copy the code

Is:

   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()) {
          // ...}}}Copy the code

Open/Close principle (OCP)

"Code entities (classes, modules, functions, etc.) should be easy to extend but difficult to modify."

This principle means that we should allow users to easily extend the functionality of our code modules without having to open the js file source code to modify them manually.

Example:

    class AjaxRequester {
      constructor() {
        // What if we wanted another HTTP Method, like DELETE? We would have to
        // open this file up and modify this and put it in manually.
        this.HTTP_METHODS = ['POST'.'PUT'.'GET'];
      }

      get(url) {
        // ...}}Copy the code

Is:

    class AjaxRequester {
      constructor() {
        this.HTTP_METHODS = ['POST'.'PUT'.'GET'];
      }

      get(url) {
        // ...
      }

      addHTTPMethod(method) {
        this.HTTP_METHODS.push(method); }}Copy the code

Liskov substitution Principle (LSP)

Subclass objects should be able to replace their superclass objects used.

That is, if there is a parent class and a subclass, there should be no error when subclasses are used to replace the parent class.

Example:

    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 {
      constructor() {
        super(a); }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);
        let area = rectangle.getArea(); // BAD: Will return 25 for Square. Should be 20.rectangle.render(area); })}let rectangles = [new Rectangle(), new Rectangle(), new Square()];
    renderLargeRectangles(rectangles);
Copy the code

Is:

    class Shape {
      constructor() {}

      setColor(color) {
        // ...
      }

      render(area) {
        // ...}}class Rectangle extends Shape {
      constructor() {
        super(a);this.width = 0;
        this.height = 0;
      }

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

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

      getArea() {
        return this.width * this.height; }}class Square extends Shape {
      constructor() {
        super(a);this.length = 0;
      }

      setLength(length) {
        this.length = length;
      }

      getArea() {
        return this.length * this.length; }}function renderLargeShapes(shapes) {
      shapes.forEach((shape) =  {
        switch (shape.constructor.name) {
          case 'Square':
            shape.setLength(5);
          case 'Rectangle':
            shape.setWidth(4);
            shape.setHeight(5);
        }

        letarea = shape.getArea(); shape.render(area); })}let shapes = [new Rectangle(), new Rectangle(), new Square()];
    renderLargeShapes(shapes);
Copy the code

Interface Isolation Principle (ISP)

"A client should not rely on interfaces it does not need; The dependency of one class on another should be based on the smallest interface."

In JS, when a class requires a lot of parameter Settings to generate an object, you probably don't need to set so many parameters most of the time. At this point it is beneficial to reduce the need for the number of configuration parameters.

Example:

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

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

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

Is:

    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() {
        // ...}}let$=new DOMTraverser({
      rootNode: document.getElementsByTagName('body'),
      options: {
        animationModule: function() {}}});Copy the code

Dependency Inversion principle (DIP)

This principle has two core points:

  1. High-level modules should not depend on low-level modules. They should all rely on abstract interfaces.
  2. Abstract interfaces should be separated from concrete implementations, which should depend on abstract interfaces.

Example:

    class InventoryTracker {
      constructor(items) {
        this.items = items;

        // BAD: We have created a dependency on a specific request implementation.
        // We should just have requestItems depend on a request method: `request`
        this.requester = new InventoryRequester();
      }

      requestItems() {
        this.items.forEach((item) =  {
          this.requester.requestItem(item); }); }}class InventoryRequester {
      constructor() {
        this.REQ_METHODS = ['HTTP'];
      }

      requestItem(item) {
        // ...}}let inventoryTracker = new InventoryTracker(['apples'.'bananas']);
    inventoryTracker.requestItems();
Copy the code

Is:

    class InventoryTracker {
      constructor(items, requester) {
        this.items = items;
        this.requester = requester;
      }

      requestItems() {
        this.items.forEach((item) =  {
          this.requester.requestItem(item); }); }}class InventoryRequesterV1 {
      constructor() {
        this.REQ_METHODS = ['HTTP'];
      }

      requestItem(item) {
        // ...}}class InventoryRequesterV2 {
      constructor() {
        this.REQ_METHODS = ['WS'];
      }

      requestItem(item) {
        // ...}}// By constructing our dependencies externally and injecting them, we can easily
    // substitute our request module for a fancy new one that uses WebSockets.
    let inventoryTracker = new InventoryTracker(['apples'.'bananas'].new InventoryRequesterV2());
    inventoryTracker.requestItems();
Copy the code

Use ES6 classes instead of ES5 functions

Typical ES5 classes (functions) are less readable in terms of inheritance, construction, and method definitions.

Classes are preferred when inheritance is required.

However, when larger and more complex objects are needed, it is best to choose smaller functions over classes.

Example:

    var Animal = function(age) {
        if(! (this instanceof Animal)) {
            throw new Error("Instantiate Animal with `new`");
        }

        this.age = age;
    };

    Animal.prototype.move = function() {};

    var Mammal = function(age, furColor) {
        if(! (this instanceof Mammal)) {
            throw new Error("Instantiate Mammal with `new`");
        }

        Animal.call(this, age);
        this.furColor = furColor;
    };

    Mammal.prototype = Object.create(Animal.prototype);
    Mammal.prototype.constructor = Mammal;
    Mammal.prototype.liveBirth = function() {};

    var Human = function(age, furColor, languageSpoken) {
        if(! (this instanceof Human)) {
            throw new Error("Instantiate Human with `new`");
        }

        Mammal.call(this, age, furColor);
        this.languageSpoken = languageSpoken;
    };

    Human.prototype = Object.create(Mammal.prototype);
    Human.prototype.constructor = Human;
    Human.prototype.speak = function() {};
Copy the code

Is:

    class Animal {
        constructor(age) {
            this.age = age;
        }

        move(){}}class Mammal extends Animal {
        constructor(age, furColor) {
            super(age);
            this.furColor = furColor;
        }

        liveBirth(){}}class Human extends Mammal {
        constructor(age, furColor, languageSpoken) {
            super(age, furColor);
            this.languageSpoken = languageSpoken;
        }

        speak(){}}Copy the code

Method chain

Our understanding here is a little different from the advice of Code Cleanliness.

The argument that method chains are not clean and violate Demeter's laws is probably true, but this approach is very useful in JS and many libraries such as JQuery.

Therefore, I think it is very appropriate to use method chains in JS. Returning this in the class function makes it easy to chain methods that the class needs to execute.

Example:

    class Car {
      constructor() {
        this.make = 'Honda';
        this.model = 'Accord';
        this.color = 'white';
      }

      setMake(make) {
        this.name = name;
      }

      setModel(model) {
        this.model = model;
      }

      setColor(color) {
        this.color = color;
      }

      save() {
        console.log(this.make, this.model, this.color); }}let car = new Car();
    car.setColor('pink');
    car.setMake('Ford');
    car.setModel('F-150')
    car.save();
Copy the code

Is:

    class Car {
      constructor() {
        this.make = 'Honda';
        this.model = 'Accord';
        this.color = 'white';
      }

      setMake(make) {
        this.name = name;
        // NOTE: Returning this for chaining
        return this;
      }

      setModel(model) {
        this.model = model;
        // NOTE: Returning this for chaining
        return this;
      }

      setColor(color) {
        this.color = color;
        // NOTE: Returning this for chaining
        return this;
      }

      save() {
        console.log(this.make, this.model, this.color); }}let car = new Car()
      .setColor('pink')
      .setMake('Ford')
      .setModel('F-150')
      .save();
Copy the code

Use composite patterns in preference to inheritance

In the famous design Patterns book, composite patterns should be used more than inheritance.

There are many advantages to doing this, but before you think about using inheritance, think about whether you can satisfy your needs with the composite pattern.

So when is inheritance more advantageous? This depends on your specific needs, but in most cases, there are three things you can do:

  1. Inheritance is expressed as "is a" rather than "has a "(e.g., animals - people and users - user details)
  2. Can reuse base class code ("Human" can be considered a kind of "All animal")
  3. Expect all derived classes to be affected when the base class changes (e.g. "All animals" move calories)

Example:

    class Employee {
      constructor(name, email) {
        this.name = name;
        this.email = email;
      }

      // ...
    }

    // Bad because Employees "have" tax data. EmployeeTaxData is not a type of Employee
    class EmployeeTaxData extends Employee {
      constructor(ssn, salary) {
        super(a);this.ssn = ssn;
        this.salary = salary;
      }

      // ...
    }
Copy the code

Is:

    class Employee {
      constructor(name, email) {
        this.name = name;
        this.email = email;

      }

      setTaxData(ssn, salary) {
        this.taxData = new EmployeeTaxData(ssn, salary);
      }
      // ...
    }

    class EmployeeTaxData {
      constructor(ssn, salary) {
        this.ssn = ssn;
        this.salary = salary;
      }

      // ...
    }
Copy the code

test

Some good overlay tools.

Some good JS testing frameworks.

Single test each concept

Example:

    const assert = require('assert');

    describe('MakeMomentJSGreatAgain'.function() {
      it('handles date boundaries'.function() {
        let date;

        date = new MakeMomentJSGreatAgain('1/1/2015');
        date.addDays(30);
        date.shouldEqual('1/31/2015');

        date = new MakeMomentJSGreatAgain('2/1/2016');
        date.addDays(28);
        assert.equal('02/29/2016', date);

        date = new MakeMomentJSGreatAgain('2/1/2015');
        date.addDays(28);
        assert.equal('03/01/2015', date);
      });
    });
Copy the code

Is:

    const assert = require('assert');

    describe('MakeMomentJSGreatAgain'.function() {
      it('handles 30-day months'.function() {
        let date = new MakeMomentJSGreatAgain('1/1/2015');
        date.addDays(30);
        date.shouldEqual('1/31/2015');
      });

      it('handles leap year'.function() {
        let date = new MakeMomentJSGreatAgain('2/1/2016');
        date.addDays(28);
        assert.equal('02/29/2016', date);
      });

      it('handles non-leap year'.function() {
        let date = new MakeMomentJSGreatAgain('2/1/2015');
        date.addDays(28);
        assert.equal('03/01/2015', date);
      });
    });
Copy the code

concurrent

Promises instead of calls back

Callbacks are not neat and cause a lot of nesting. Promises are built into ES6, use them.

Example:

    require('request').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin'.function(err, response) {
      if (err) {
        console.error(err);
      }
      else {
        require('fs').writeFile('article.html', response.body, function(err) {
          if (err) {
            console.error(err);
          } else {
            console.log('File written'); }})}Copy the code

Is:

    require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin')
      .then(function(response) {
        return require('fs-promise').writeFile('article.html', response);
      })
      .then(function() {
        console.log('File written');
      })
      .catch(function(err) {
        console.error(err);
      })
Copy the code

Async/Await is a better choice than Promises

Promises are a better option than callback, but ASYNc and await in ES7 are better than Promises.

Use them instead of Promises if you can use ES7 features.

Example:

    require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin')
      .then(function(response) {
        return require('fs-promise').writeFile('article.html', response);
      })
      .then(function() {
        console.log('File written');
      })
      .catch(function(err) {
        console.error(err);
      })
Copy the code

Is:

    async function getCleanCodeArticle() {
      try {
        var request = await require('request-promise')
        var response = await request.get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin');
        var fileHandle = await require('fs-promise');

        await fileHandle.writeFile('article.html', response);
        console.log('File written');
      } catch(err) {
        console.log(err); }}Copy the code

Error handling

Error throws are a good thing! This allows you to successfully locate errors in a running program.

Don't forget to catch errors

It doesn't make sense to do nothing about the captured errors.

Try /catch in your code means that you think there might be some kind of error, and you should have a plan for dealing with those possible errors.

Example:

    try {
      functionThatMightThrow();
    } catch (error) {
      console.log(error);
    }
Copy the code

Is:

    try {
      functionThatMightThrow();
    } catch (error) {
      // One option (more noisy than console.log):
      console.error(error);
      // Another option:
      notifyUserOfError(error);
      // Another option:
      reportErrorToService(error);
      // OR do all three!
    }
Copy the code

Don't ignore promises that are rejected

For the same reason as try/catch.

Example:

    getdata()
    .then(data=  {
      functionThatMightThrow(data);
    })
    .catch(error=  {
      console.log(error);
    });
Copy the code

Is:

    getdata()
    .then(data=  {
      functionThatMightThrow(data);
    })
    .catch(error=  {
      // One option (more noisy than console.log):
      console.error(error);
      // Another option:
      notifyUserOfError(error);
      // Another option:
      reportErrorToService(error);
      // OR do all three!
    });
Copy the code

formatting

Formatting is a subjective matter. As with many rules here, there are no certain/immediate rules to follow. Format automation can be done here.

Case consistency

JS is a weakly typed language, and proper capitalization can tell you a lot about variables/functions, etc.

These rules are subjective and can be chosen by the team as they please. The point is that whatever style you choose, you need to pay attention to consistency.

Example:

    var DAYS_IN_WEEK = 7;
    var daysInMonth = 30;

    var songs = ['Back In Black'.'Stairway to Heaven'.'Hey Jude'];
    var Artists = ['ACDC'.'Led Zeppelin'.'The Beatles'];

    function eraseDatabase() {}
    function restore_database() {}

    class animal {}
    class Alpaca {}
Copy the code

Is:

    var DAYS_IN_WEEK = 7;
    var DAYS_IN_MONTH = 30;

    var songs = ['Back In Black'.'Stairway to Heaven'.'Hey Jude'];
    var artists = ['ACDC'.'Led Zeppelin'.'The Beatles'];

    function eraseDatabase() {}
    function restoreDatabase() {}

    class Animal {}
    class Alpaca {}
Copy the code

The calling function and the called function should be placed near each other

When functions call each other, they should be placed near each other.

Ideally, a function that calls another function should be written above the called function.

Example:

    class PerformanceReview {
      constructor(employee) {
        this.employee = employee;
      }

      lookupPeers() {
        return db.lookup(this.employee, 'peers');
      }

      lookupMananger() {
        return db.lookup(this.employee, 'manager');
      }

      getPeerReviews() {
        let peers = this.lookupPeers();
        // ...
      }

      perfReview() {
          getPeerReviews();
          getManagerReview();
          getSelfReview();
      }

      getManagerReview() {
        let manager = this.lookupManager();
      }

      getSelfReview() {
        // ...}}let review = new PerformanceReview(user);
    review.perfReview();
Copy the code

Is:

    class PerformanceReview {
      constructor(employee) {
        this.employee = employee;
      }

      perfReview() {
          getPeerReviews();
          getManagerReview();
          getSelfReview();
      }

      getPeerReviews() {
        let peers = this.lookupPeers();
        // ...
      }

      lookupPeers() {
        return db.lookup(this.employee, 'peers');
      }

      getManagerReview() {
        let manager = this.lookupManager();
      }

      lookupMananger() {
        return db.lookup(this.employee, 'manager');
      }

      getSelfReview() {
        // ...}}let review = new PerformanceReview(employee);
    review.perfReview();
Copy the code

annotation

Annotate only code that has some business logic complexity

Comments are not necessary; good code is self-explanatory and does not require unnecessary comments.

Example:

    function hashIt(data) {
      // The hash
      var hash = 0;

      // Length of string
      var length = data.length;

      // Loop through every character in data
      for (var i = 0; i  length; i++) {
        // Get character code.
        var char = data.charCodeAt(i);
        // Make the hash
        hash = ((hash  5) - hash) + char;
        // Convert to 32-bit integerhash = hash  hash; }}Copy the code

Is:

    function hashIt(data) {
      var hash = 0;
      var length = data.length;

      for (var i = 0; i  length; i++) {
        var char = data.charCodeAt(i);
        hash = ((hash  5) - hash) + char;

        // Convert to 32-bit integerhash = hash  hash; }}Copy the code

Don't leave commented out code in your code base

Version control exists for a reason. Let old code live in your history.

Example:

    doStuff();
    // doOtherStuff();
    // doSomeMoreStuff();
    // doSoMuchStuff();
Copy the code

Is:

    doStuff();
Copy the code

Version update type annotations are not required

Remember, we can use version control. Obsolete code, annotated code, and annotations documenting version updates in code are unnecessary.

You can use git log to obtain historical versions if necessary.

Example:

    /** * 2016-12-20: Removed monads, didn't understand them (RM) * 2016-10-01: Improved using special monads (JP) * 2016-02-03: Removed type-checking (LI) * 2015-03-14: Added combine with type-checking (JR) */
    function combine(a, b) {
      return a + b;
    }
Copy the code

Is:

    function combine(a, b) {
      return a + b;
    }
Copy the code
Search
About
mo4tech.com (Moment For Technology) is a global community with thousands techies from across the global hang out!Passionate technologists, be it gadget freaks, tech enthusiasts, coders, technopreneurs, or CIOs, you would find them all here.