Skip to content

annatomka/jstips

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 

Repository files navigation

header

Introducing JavaScript Tips

New year, new project. A JS tip per day!

With great excitement, I introduce these short and useful daily JavaScript tips that will allow you to improve your code writing. With less than 2 minutes each day, you will be able to read about performance, conventions, hacks, interview questions and all the items that the future of this awesome language holds for us.

At midday, no matter if it is a weekend or a holiday, a tip will be posted and tweeted.

Can you help us enrich it?

Please feel free to send us a PR with your own JavaScript tip to be published here. Any improvements or suggestions are more than welcome! Click to see the instructions

Let’s keep in touch

To get updates, watch the repo and follow the Twitter account, only one tweet will be sent per day. It is a deal!

Don't forget to Star the repo, as this will help to promote the project!

Tips list

#17 - Node.js: Run a module if it is not required

2016-01-17 by @odsdq

In node, you can tell your program to do two different things depending on whether the code is run from require('./something.js') or node something.js. This is useful if you want to interact with one of your modules independently.

if (!module.parent) {
    // ran with `node something.js`
    app.listen(8088, function() {
        console.log('app listening on port 8088');
    })
} else {
    // used with `require('/.something.js')`
    module.exports = app;
}

See the documentation for modules for more info.

#16 - Passing arguments to callback functions

2016-01-16 by @minhazav

By default you cannot pass arguments to a callback function. For example:

function callback() {
  console.log('Hi human');
}

document.getElementById('someelem').addEventListener('click', callback);

You can take advantage of the closure scope in Javascript to pass arguments to callback functions. Check this example:

function callback(a, b) {
  return function() {
    console.log('sum = ', (a+b));
  }
}

var x = 1, y = 2;
document.getElementById('someelem').addEventListener('click', callback(x, y));

What are closure? Closures are functions that refer to independent (free) variables. In other words, the function defined in the closure 'remembers' the environment in which it was created. Check MDN Documentation to learn more.

So this way the arguments x and y are in scope of the callback function when it is called.

Another method to do this is using bind method. For example:

var alertText = function(text) {
  alert(text);
};

document.getElementById('someelem').addEventListener('click', alertText.bind(this, 'hello'));

There is a very slight difference in performance of both methods, checkout jsperf.

#15 - Even simpler way of using indexOf as a contains clause

2016-01-15 by @jhogoforbroke

JavaScript by default does not have a contains method. And for checking existence of a substring in string or item in array you may do this:

var someText = 'javascript rules';
if (someText.indexOf('javascript') !== -1) {
}

// or
if (someText.indexOf('javascript') >= 0) {
}

But let's look at these Expressjs code snippets.

examples/mvc/lib/boot.js

for (var key in obj) {
  // "reserved" exports
  if (~['name', 'prefix', 'engine', 'before'].indexOf(key)) continue;

lib/utils.js

exports.normalizeType = function(type){
  return ~type.indexOf('/')
    ? acceptParams(type)
    : { value: mime.lookup(type), params: {} };
};

examples/web-service/index.js

// key is invalid
if (!~apiKeys.indexOf(key)) return next(error(401, 'invalid api key'));

The gotcha is the bitwise operator ~, "Bitwise operators perform their operations on such binary representations, but they return standard JavaScript numerical values."

It transforms -1 into 0, and 0 is false in javascript, so:

var someText = 'text';
!!~someText.indexOf('tex'); //sometext contains tex - true
!~someText.indexOf('tex'); //sometext not contains tex - false
~someText.indexOf('asd'); //sometext contains asd - false
~someText.indexOf('ext'); //sometext contains ext - true

String.prototype.includes()

In ES6 was introduced the includes() method and you can use to determine whether or not a string includes another string:

'something'.includes('thing'); // true

With ECMAScript 2016 (ES7) is even possible uses with Arrays, like indexOf:

!!~[1, 2, 3].indexOf(1); // true
[1, 2, 3].includes(1); // true

Unfortunately, It's got support only in Chrome, Firefox, Safari 9 or above and Edge. Not IE11 or less. It's better to using in controlled environments.

#14 - Fat Arrow Functions #ES6

2016-01-13 by @pklinger

Introduced as a new feature in ES6, fat arrow functions may come as a handy tool to write more code in less lines. The name comes from its syntax as => is a 'fat arrow' compared to a thin arrow ->. Some programmers might already know this type of functions from different languages such as Haskell as 'lambda expressions' respectively 'anonymous functions'. It is called anonymous, as these arrow functions do not have a descriptive function name.

What are the benefits?

  • Syntax: less LOC; no more typing function keyword over and over again
  • Semantics: capturing the keyword this from the surrounding context

Simple syntax example

Have a look at these two code snippets, which exactly do the same job. You will quickly understand what fat arrow functions do.

// general syntax for fat arrow functions
param => expression

// may also be written with parentheses
// parentheses are required on multiple params
(param1 [, param2]) => expression


// using functions
var arr = [5,3,2,9,1];
var arrFunc = arr.map(function(x) {
  return x * x;
});
console.log(arr)

// using fat arrow
var arr = [5,3,2,9,1];
var arrFunc = arr.map((x) => x*x);
console.log(arr)

As you may see, the fat arrow function in this case may save you time typing out the parentheses as well as the function and return keywords. I would advice you to always write parentheses around the parameter inputs as the parentheses will be needed for multiple input parameters such as in (x,y) => x+y anyways. It is just a way to cope with forgetting them in different use cases. But the code above would also work like this: x => x*x. So far these are only syntactical improvements, which lead to less LOC and better readability.

Lexically binding this

There is another good reason to use fat arrow functions. There is the issue with the context of this. With arrow functions, you will not worry about .bind(this) or setting that = this anymore, as fat arrow functions pick the context of this from the lexical surrounding. Have a look at the next [example] (https://jsfiddle.net/pklinger/rw94oc11/):

// globally defined this.i
this.i = 100;

var counterA = new CounterA();
var counterB = new CounterB();
var counterC = new CounterC();
var counterD = new CounterD();

// bad example
function CounterA() {
  // CounterA's `this` instance (!! gets ignored here)
  this.i = 0;
  setInterval(function () {
    // `this` refers to global object, not to CounterA's `this`
    // therefore starts counting with 100, not with 0 (local this.i)
    this.i++;
    document.getElementById("counterA").innerHTML = this.i;
  }, 500);
}

// manually binding that = this
function CounterB() {
  this.i = 0;
  var that = this;
  setInterval(function() {
    that.i++;
    document.getElementById("counterB").innerHTML = that.i;
  }, 500);
}

// using .bind(this)
function CounterC() {
  this.i = 0;
  setInterval(function() {
    this.i++;
    document.getElementById("counterC").innerHTML = this.i;
  }.bind(this), 500);
}

// fat arrow function
function CounterD() {
  this.i = 0;
  setInterval(() => {
    this.i++;
    document.getElementById("counterD").innerHTML = this.i;
  }, 500);
}

Further information about fat arrow functions may be found at [MDN] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions). To see different syntax options visit [this site] (http://jsrocks.org/2014/10/arrow-functions-and-their-scope/).

#13 - Tip to measure performance of a javascript block

2016-01-13 by @manmadareddy

For quickly measuring performance of a javascript block, we can use the console functions like console.time(label) and console.timeEnd(label)

console.time("Array initialize");
var arr = new Array(100),
    len = arr.length,
    i;

for (i = 0; i < len; i++) {
    arr[i] = new Object();
};
console.timeEnd("Array initialize"); // Outputs: Array initialize: 0.711ms

More info: Console object, Javascript benchmarking

Demo: jsfiddle - codepen (outputs in browser console)

#12 - Pseudomentatory parameters in ES6 functions #ES6

2016-01-12 by Avraam Mavridis

In many programming languages the parameters of a function is by default mandatory and the developer has to explicitly define that a parameter is optional. In Javascript every parameter is optional, but we can enforce this behavior without messing the actual body of a function taking advantage of the [es6's default values for parameters] (http://exploringjs.com/es6/ch_parameter-handling.html#sec_parameter-default-values) feature.

const _err = function( message ){
  throw new Error( message );
}

const getSum = (a = _err('a is not defined'), b = _err('b is not defined')) => a + b

getSum( 10 ) // throws Error, b is not defined
getSum( undefined, 10 ) // throws Error, a is not defined

_err is a function that immediately throws an Error. If no value is passed for one of the parameters, the default value is gonna be used, _err will be called and an Error will be throwed. You can see more examples for the default parameters feature on Mozilla's Developer Network

#11 - Hoisting

2016-01-11 by @squizzleflip

Understanding hoisting will help you organize your function scope. Just remember, variable declaration and function definition are hoisted to the top. Variable definition is not, even if you declare and define a variable on the same line. Also, variable declaration is letting the system know that the variable exists while definition is assigning it a value.

function doTheThing() {
  // ReferenceError: notDeclared is not defined
  console.log(notDeclared);

  // Outputs: undefined
  console.log(definedLater);
  var definedLater;

  definedLater = 'I am defined!'
  // Outputs: 'I am defined!'
  console.log(definedLater)

  // Outputs: undefined
  console.log(definedSimulateneously);
  var definedSimulateneously = 'I am defined!'
  // Outputs: 'I am defined!'
  console.log(definedSimulateneously)

  // Outputs: 'I did it!'
  doSomethingElse();

  function doSomethingElse(){
    console.log('I did it!');
  }

  // TypeError: undefined is not a function
  functionVar();

  var functionVar = function(){
    console.log('I did it!');
  }
}

To make things easier to read, declare all of your variables at the top of your function scope so it is clear which scope the variables are coming from. Define your variables before you need to use them. Define your functions at the bottom of your scope to keep them out of your way.

#10 - Check if a property is in a Object

2016-01-10 by @loverajoel

When you have to check if a property is present of an object, you probably are doing something like this:

var myObject = {
  name: '@tips_js'
};

if (myObject.name) { ... }

Thats ok, but you have to know that there are two native ways for this kind of thing, the in operator and Object.hasOwnProperty, every object descended from Object, has available both ways.

See the big Difference

var myObject = {
  name: '@tips_js'
};

myObject.hasOwnProperty('name'); // true
'name' in myObject; // true

myObject.hasOwnProperty('valueOf'); // false, valueOf is inherited from the prototype chain
'valueOf' in myObject; // true

Both differs in the depth how check the properties, in other words hasOwnProperty will only return true if key is available on that object directly, however in operator doesn't discriminate between properties created on an object and properties inherited from the prototype chain.

Here another example

var myFunc = function() {
  this.name = '@tips_js';
};
myFunc.prototype.age = '10 days';

var user = new myFunc();

user.hasOwnProperty('name'); // true
user.hasOwnProperty('age'); // false, because age is from the prototype chain

Check here the live examples!

Also recommends read this discussion about common mistakes at checking properties' existence in objects

#09 - Template Strings

2016-01-09 by @JakeRawr

As of ES6, JS now has template strings as an alternative to the classic end quotes strings.

Ex: Normal string

var firstName = 'Jake';
var lastName = 'Rawr';
console.log('My name is ' + firstName + ' ' + lastName);
// My name is Jake Rawr

Template String

var firstName = 'Jake';
var lastName = 'Rawr';
console.log(`My name is ${firstName} ${lastName}`);
// My name is Jake Rawr

You can do Multi-line strings without \n and simple logic (ie 2+3) inside ${} in Template String.

You are also able to to modify the output of template strings using a function; they are called [Tagged template strings] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/template_strings#Tagged_template_strings) for example usages of tagged template strings.

You may also want to read to understand template strings more

#08 - Converting a Node List to an Array

2016-01-08 by @Tevko

The querySelectorAll method returns an array-like object called a node list. These data structures are referred to as "Array-like", because they appear as an array, but can not be used with array methods like map and foreach. Here's a quick, safe, and reusable way to convert a node list into an Array of DOM elements:

const nodelist = document.querySelectorAll('div');
const nodelistToArray = Array.apply(null, nodelist);

//later on ..

nodelistToArray.forEach(...);
nodelistToArray.map(...);
nodelistToArray.slice(...);

//etc...

The apply method is used to pass an array of arguments to a function with a given this value. MDN states that apply will take an array like object, which is exactly what querySelectorAll returns. Since we don't need to specify a value for this in the context of the function, we pass in null or 0. The result is an actual array of DOM elements which contains all of the available array methods.

Or if you are using ES2015 you can use the spread operator ...

const nodelist = [...document.querySelectorAll('div')]; // returns a real Array

//later on ..

nodelist.forEach(...);
nodelist.map(...);
nodelist.slice(...);

//etc...

#07 - "use strict" and get lazy

2016-01-07 by @nainslie

Strict-mode JavaScript makes it easier for the developer to write "secure" JavaScript.

By default, JavaScript allows the programmer to be pretty careless, for example, by not requiring us to declare our variables with "var" when we first introduce them. While this may seem like a convenience to the unseasoned developer, it's also the source of many errors when a variable name is misspelled or accidentally referred to out of its scope.

Programmers like to make the computer do the boring stuff for us, and automatically check our work for mistakes. That's what the JavaScript "use strict" directive allows us to do, by turning our mistakes into JavaScript errors.

We add this directive either by adding it at the top of a js file:

// Whole-script strict mode syntax
"use strict";
var v = "Hi!  I'm a strict mode script!";

or inside a function:

function f()
{
  // Function-level strict mode syntax
  'use strict';
  function nested() { return "And so am I!"; }
  return "Hi!  I'm a strict mode function!  " + nested();
}
function f2() { return "I'm not strict."; }

By including this directive in a JavaScript file or function, we will direct the JavaScript engine to execute in strict mode which disables a bunch of behaviors that are usually undesirable in larger JavaScript projects. Among other things, strict mode changes the following behaviors:

  • Variables can only be introduced when they are preceded with "var"
  • Attempting to write to readonly properties generates a noisy error
  • You have to call constructors with the "new" keyword
  • "this" is not implicitly bound to the global object
  • Very limited use of eval() allowed
  • Protects you from using reserved words or future reserved words as variable names

Strict mode is great for new projects, but can be challenging to introduce into older projects that don't already use it in most places. It also can be problematic if your build chain concatenates all your js files into one big file, as this may cause all files to execute in strict mode.

It is not a statement, but a literal expression, ignored by earlier versions of JavaScript. Strict mode is supported in:

  • Internet Explorer from version 10.
  • Firefox from version 4.
  • Chrome from version 13.
  • Safari from version 5.1.
  • Opera from version 12.

See MDN for a fuller description of strict mode.

#06 - Writing a single method for arrays or single elements

2016-01-06 by @mattfxyz

Rather than writing separate methods to handle an array and a single element parameter, write your functions so they can handle both. This is similar to how some of jQuery's functions work (css will modify everything matched by the selector).

You just have to concat everything into an array first. Array.concat will accept an array or a single element.

function printUpperCase(words) {
  var elements = [].concat(words);
  for (var i = 0; i < elements.length; i++) {
    console.log(elements[i].toUpperCase());
  }
}

printUpperCase is now ready to accept a single node or an array of nodes as its parameter.

printUpperCase("cactus");
// => CACTUS
printUpperCase(["cactus", "bear", "potato"]);
// => CACTUS
//  BEAR
//  POTATO

#05 - Differences between undefined and null

2016-01-05 by @loverajoel

  • undefined means a variable has not been declared, or has been declared but has not yet been assigned a value

  • null is an assignment value that means "no value"

  • Javascript sets unassigned variables with a default value of undefined

  • Javascript never sets a value to null. It is used by programmers to indicate that a var has no value.

  • undefined is not valid in JSON while null is

  • undefined typeof is undefined

  • null typeof is an object. Why?

  • Both are primitives

  • Both are falsy (Boolean(undefined) // false, Boolean(null) // false)

  • You can know if a variable is undefined

    typeof variable === "undefined"
- You can check if a variable is [null](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null)

  ```javascript
  variable === null
  • The equality operator considers them equal, but the identity doesn't

    null == undefined // true
    
    null === undefined // false

## #04 - Sorting strings with accented characters

> 2016-01-04 by [@loverajoel](https://twitter.com/loverajoel)

Javascript has a native method **[sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)** that allows sorting arrays. Doing a simple `array.sort()` will treat each array entry as a string and sort it alphabetically. Also you can provide your [own custom sorting](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Parameters) function.

```javascript
['Shanghai', 'New York', 'Mumbai', 'Buenos Aires'].sort();
// ["Buenos Aires", "Mumbai", "New York", "Shanghai"]

But when you try order an array of non ASCII characters like this ['é', 'a', 'ú', 'c'], you will obtain a strange result ['c', 'e', 'á', 'ú']. That happens because sort works only with english language.

See the next example:

// Spanish
['único','árbol', 'cosas', 'fútbol'].sort();
// ["cosas", "fútbol", "árbol", "único"] // bad order

// German
['Woche', 'wöchentlich', 'wäre', 'Wann'].sort();
// ["Wann", "Woche", "wäre", "wöchentlich"] // bad order

Fortunately, there are two ways to overcome this behavior localeCompare and Intl.Collator provided by ECMAScript Internationalization API.

Both methods have their own custom parameters in order to configure it to work adequately.

Using localeCompare()

['único','árbol', 'cosas', 'fútbol'].sort(function (a, b) {
  return a.localeCompare(b);
});
// ["árbol", "cosas", "fútbol", "único"]

['Woche', 'wöchentlich', 'wäre', 'Wann'].sort(function (a, b) {
  return a.localeCompare(b);
});
// ["Wann", "wäre", "Woche", "wöchentlich"]

Using Intl.Collator()

['único','árbol', 'cosas', 'fútbol'].sort(Intl.Collator().compare);
// ["árbol", "cosas", "fútbol", "único"]

['Woche', 'wöchentlich', 'wäre', 'Wann'].sort(Intl.Collator().compare);
// ["Wann", "wäre", "Woche", "wöchentlich"]
  • For each method you can customize the location.
  • According to Firefox Intl.Collator is faster when comparing large numbers of strings.

So when you are working with arrays of strings in a language other than English, remember to use this method to avoid unexpected sorting.

#03 - Improve Nested Conditionals

2016-01-03 by AlbertoFuente

How can we improve and make more efficient nested if statement in javascript.

if (color) {
  if (color === 'black') {
    printBlackBackground();
  } else if (color === 'red') {
    printRedBackground();
  } else if (color === 'blue') {
    printBlueBackground();
  } else if (color === 'green') {
    printGreenBackground();
  } else {
    printYellowBackground();
  }
}

One way to improve the nested if statement would be using the switch statement. Although it is less verbose and is more ordered, It's not recommended to use it because it's so difficult to debug errors, here's why.

switch(color) {
  case 'black':
    printBlackBackground();
    break;
  case 'red':
    printRedBackground();
    break;
  case 'blue':
    printBlueBackground();
    break;
  case 'green':
    printGreenBackground();
    break;
  default:
    printYellowBackground();
}

But what if we have a conditional with several checks in each statement? In this case, if we like to do less verbose and more ordered, we can use the conditional switch. If we pass true as parameter to the switch statement, It allows us to put a conditional in each case.

switch(true) {
  case (typeof color === 'string' && color === 'black'):
    printBlackBackground();
    break;
  case (typeof color === 'string' && color === 'red'):
    printRedBackground();
    break;
  case (typeof color === 'string' && color === 'blue'):
    printBlueBackground();
    break;
  case (typeof color === 'string' && color === 'green'):
    printGreenBackground();
    break;
  case (typeof color === 'string' && color === 'yellow'):
    printYellowBackground();
    break;
}

But we must always avoid having several checks in every condition, avoiding use of switch as far as possible and take into account that the most efficient way to do this is through an object.

var colorObj = {
  'black': printBlackBackground,
  'red': printRedBackground,
  'blue': printBlueBackground,
  'green': printGreenBackground,
  'yellow': printYellowBackground
};

if (color && colorObj.hasOwnProperty(color)) {
  colorObj[color]();
}

Here you can find more information about this.

#02 - ReactJs - Keys in children components are important

2016-01-02 by @loverajoel

The key is an attribute that you must pass to all components created dynamically from an array. It's unique and constant id that React use for identify each component in the DOM and know that it's a different component and not the same one. Using keys will ensure that the child component is preserved and not recreated and prevent that weird things happens.

Key is not really about performance, it's more about identity (which in turn leads to better performance). randomly assigned and changing values are not identity Paul O’Shannessy

  • Use an existing unique value of the object.

  • Define the keys in the parent components, not in child components

     //bad
     ...
     render() {
     	<div key={{item.key}}>{{item.name}}</div>
     }
     ...
    
     //good
     <MyComponent key={{item.key}}/>
  • Using array index is a bad practice.

  • random() will not work

     //bad
     <MyComponent key={{Math.random()}}/>
  • You can create your own unique id, be sure that the method be fast and attach it to your object.

  • When the amount of child are big or involve expensive components, use keys has performance improvements.

  • You must provide the key attribute for all children of ReactCSSTransitionGroup.

#1 - AngularJs: $digest vs $apply

2016-01-01 by @loverajoel

One of the most appreciated features of AngularJs is the two way data binding. In order to make this work AngularJs evaluates the changes between the model and the view through cycles($digest). You need to understand this concept in order to understand how the framework works under the hood.

Angular evaluates each watcher whenever one event is fired, this is the known $digest cycle. Sometimes you have to force to run a new cycle manually and you must choose the correct option because this phase is one of the most influential in terms of performance.

$apply

This core method lets you to start the digestion cycle explicitly, that means that all watchers are checked, the entire application starts the $digest loop. Internally after execute an optional function parameter, call internally to $rootScope.$digest();.

$digest

In this case the $digest method starts the $digest cycle for the current scope and its children. You should notice that the parents scopes will not be checked and not be affected.

Recommendations

  • Use $apply or $digest only when browser DOM events have triggered outside of AngularJS.

  • Pass a function expression to $apply, this have a error handling mechanism and allow integrate changes in the digest cycle

     $scope.$apply(() => {
     	$scope.tip = 'Javascript Tip';
     });
  • If only needs update the current scope or its children use $digest, and prevent a new digest cycle for the whole application. The performance benefit it's self evident

  • $apply() is hard process for the machine and can lead to performance issues when having a lot of binding.

  • If you are using >AngularJS 1.2.X, use $evalAsync is a core method that will evaluate the expression during the current cycle or the next. This can improve your application's performance.

#0 - Insert item inside an Array

2015-12-29

Inserting an item into an existing array is a daily common task. You can add elements to the end of an array using push, to the beginning using unshift, or the middle using splice.

But those are known methods, doesn't mean there isn't a more performant way, here we go...

Adding an element at the end of the array is easy with push(), but there is a way more performant.

var arr = [1,2,3,4,5];

arr.push(6);
arr[arr.length] = 6; // 43% faster in Chrome 47.0.2526.106 on Mac OS X 10.11.1

Both methods modify the original array. Don't believe me? Check the jsperf

Now we are trying to add an item to the beginning of the array

var arr = [1,2,3,4,5];

arr.unshift(0);
[0].concat(arr); // 98% faster in Chrome 47.0.2526.106 on Mac OS X 10.11.1

Here is a little bit detail, unshift edits the original array, concat returns a new array. jsperf

Adding items at the middle of an array is easy with splice and is the most performant way to do it.

var items = ['one', 'two', 'three', 'four'];
items.splice(items.length / 2, 0, 'hello');

I tried to run these tests in various Browsers and OS and the results were similar. I hope these tips will be useful for you and encourage to perform your own tests!

License

CC0

About

This is about one JS tip every day!

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • JavaScript 100.0%