- 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 |
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
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.
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 tolet
if necesary. This will give other developers a clue as to whether this variable ever changes or not.
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 usethis
. 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();
}
}