Skip to content

Instantly share code, notes, and snippets.

@btpoe
Created October 17, 2016 18:39
Show Gist options
  • Save btpoe/dae3c976bb58a6df6fc5399f00c234fb to your computer and use it in GitHub Desktop.
Save btpoe/dae3c976bb58a6df6fc5399f00c234fb to your computer and use it in GitHub Desktop.

Imports

  • can not be conditional ( if (someVar) { import 'path' }
  • can not be evaluated ( import 'path-' + someVar )
Feature Node.js Browserify ES Import
Conditional require yes yes no
Dynamic path yes no no

Default Function Values

Function arguments can now have a default value. Here are some examples:

function createElement(tag = 'div', options = { className: 'new-widget' }) {
  const elem = document.createElement(tag);
  
  Object.keys(options).forEach(prop => {
    elem[prop] = options[prop];
  });
  
  return elem;
}

If the default value is a function call, it will be evaluated when the newly defined function is called.

function oneDayLater(date = new Date()) {
  return new Date(date.getTime() + (60 * 60 * 24));
}

A plain object will return a new object every time.

function getInitialState(state = { active: true, clicked: false }) {
  return state;
}

foo = getInitialState();
foo.active = false;

bar = getInitialState();
bar.active; // true

A reference to an object will return the same reference every time.

const defaultState = {
  active: true,
  clicked: false,
};

function getInitialState(state = defaultState) {
  return state;
}

foo = getInitialState();
foo.active = false;

bar = getInitialState();
bar.active; // false

Arrow Functions

Javascript has a bit of a confusing use of this. To limit the confusion, arrow functions have been introduced. These functions do not have their own function scope, therefore, their reference of this is whatever this references in their parent function scope.

Suggestion: Use arrow functions when possible, only refactor to a regular function if necessary.

Variables (let vs const)

var is function scoped. With the arrival of arrow functions (which do not have their own scope) a solution was needed for variables to be scoped in arrow functions. let and const are block scoped. Any use of { ... } such as if, for, and while, will have a block scope. Manipulating properties of a object, array, etc. can still be done on a const what cannot be done is change what a variable points to.

Suggestion: Use const at first. Refactor to let if necesary. This will give other developers a clue as to whether this variable ever changes or not.

Classes

This is largely syntastic sugar, but can become really handy when dealing with a class that you would like to extend another.

class BaseModule {
  constructor() {
    this.apiEndpoint = '/';
  }
  
  getMeta() {
    fetch(this.apiEndpoint)
      .then(res => res.json())
      .then(data => {
        this.meta = data;
      });
  }
}

class Carousel extends BaseModule {
  constructor(options) {
    super();
    this.apiEndpoint = `/carousels/${options.id}`;
    this.getMeta();
  }
}

Note: You must call on super() in a constructor before you can use this. This only applies to extended classes.

These are all ok:

// doesn't extend a class
class Carousel {
  constructor(options) {
    this.apiEndpoint = `/carousels/${options.id}`;
    this.getMeta();
  }
  
  getMeta() {
    fetch(this.apiEndpoint)
      .then(res => res.json())
      .then(data => {
        this.meta = data;
      });
      
    return this;
  }
}

// doesn't have a constructor (parents constructor will be used)
class Carousel {
  setMeta(key, value) {
    this.meta[key] = value;
    
    return this;
  }
}

// calls `super()` before use of `this`
class Carousel extends BaseModule {
  constructor(options) {
    super();
    this.apiEndpoint = `/carousels/${options.id}`;
    this.getMeta();
  }
}

// doesn't use `this` before calling `super()`
class Carousel extends BaseModule {
  constructor(options) {
    console.log(options);
    options.manipulatedKey = 'changed';
    super();
    this.apiEndpoint = `/carousels/${options.id}`;
    this.getMeta();
  }
}

// won't break, but a bad idea. This will not call the parent constructor
class Carousel extends BaseModule {
  constructor(options) {
    console.log(options);
  }
}

There are not ok:

// doesn't call `super()` in an extended module
class Carousel extends BaseModule {
  constructor(options) {
    this.apiEndpoint = `/carousels/${options.id}`;
    this.getMeta();
  }
}

// calls `super()` after referencing `this`
class Carousel extends BaseModule {
  constructor(options) {
    this.apiEndpoint = `/carousels/${options.id}`;
    super();
    this.getMeta();
  }
}

Also note, it is a good idea to pass the constructor arguments through to the super() call.

class BaseModule {
  constructor(options) {
    this.options = options;
    this.apiEndpoint = '/';
    if (!extended) {
      this.getMeta();
    }
  }
  
  getMeta() {
    ...
  }
}

class Carousel extends BaseModule {
  constructor(options = {}) {
    super(options);
    this.apiEndpoint = `/carousels/${options.id}`;
    this.getMeta();
  }
}

Keep in mind it's possible to have different arguments passed to an extended constructor, for example:

class BaseModule {
  constructor(node, options, extended = false) {
    this.apiEndpoint = '/';
    if (!extended) {
      this.getMeta();
    }
  }
  
  ...
}

class Carousel extends BaseModule {
  constructor(node, options = {}) {
    super(node, options, true);
    this.apiEndpoint = `/carousels/${options.id}`;
    this.getMeta();
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment