Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avoiding using magic numbers in JavaScript - alternatives that work with JsHint

JSHint's inspections now built into PhpStorm informed me about JavaScript magic numbers and I realise it'll make for clearer code to avoid using them.

I tried this:

var constants = {
    millisecs: 1000,
    secs: 60
};

and also this:

var constants = function () {
    this.millisecs = 1000;
    this.getMillisecs = function () {
        return this.millisecs;
    };
};

JsHint complains about both.

Taking the solution from this answer though works fine:

var constants = (function() {
    var millisecs = 1000,
        defaultMsgsPerSecond = 60;
    this.getMillisecs = function() { return millisecs; };
    this.getDefaultMsgsPerSecond = function() { return defaultMsgsPerSecond; };
})();

Presumably because of the closure. Why is it that this is accepted, whereas the other two suggestions taken from another SO question are not?

Edit: Although not triggering an error, it doesn't actually work. It errors to say constants is undefined. JsFiddle.

To clarify - by "works" I mean "doesn't trigger a warning from JsHint"

like image 602
bcmcfc Avatar asked Aug 21 '12 08:08

bcmcfc


People also ask

What is magic numbers in Javascript?

What Are Magic Numbers? Magic numbers are unique values, typically numerical, with unexplained meaning in your program that could be replaced by a named constant. Date.

What is considered a magic number?

A number is said to be a magic number, if the sum of its digits are calculated till a single digit recursively by adding the sum of the digits after every addition. If the single digit comes out to be 1,then the number is a magic number.

Where are the magic numbers?

magic number, in physics, in the shell models of both atomic and nuclear structure, any of a series of numbers that connote stable structure. The magic numbers for atoms are 2, 10, 18, 36, 54, and 86, corresponding to the total number of electrons in filled electron shells.


2 Answers

In EcmaScript 6, you'll be able to just do:

const MILLISECS = 1000;
const DEFAULT_MSG_PER_SECOND = 60;

But until then, you can use EcmaScript 5's Object.freeze:

var constants = {
  millisecs: 1000,
  defaultMsgPerSecond: 60
};

var constants = Object.freeze(constants);

// Now constants is in fact... a constant!
constants.millisecs = 999;
constants.millisecs; // Still === 1000

And if it's your nature to be verbose, you can try Object.defineProperties:

var constants = {};

Object.defineProperties(constants, {
    'millisecs': {
        value: 1000,
        writable: false
     },
    'defaultMsgPerSecond': {
        value: 60,
        writable: false
     },
});

// Again, constants is in fact... a constant!
constants.millisecs = 999;
constants.millisecs; // Still === 1000
like image 177
rodrigo-silveira Avatar answered Sep 28 '22 11:09

rodrigo-silveira


about your edit

I think you wanted to new the inline object:

var constants = new (function() {
    var millisecs = 1000,
        defaultMsgsPerSecond = 60;
    this.getMillisecs = function() { return millisecs; };
    this.getDefaultMsgsPerSecond = function() { return defaultMsgsPerSecond; };
})();

But JSHint will also complain about that: Weird construction. Is 'new' unnecessary?.

If you use it as a closure, then you need to actually return something. If you don't, constants will indeed contain undefined. An easy fix would be to return this, but that would be a bad solution because you're extending this which is an instance of an object you do not own.

So returning an inline object seems to be the solution here:

var constants = (function() {
    var millisecs = 1000,
        defaultMsgsPerSecond = 60;
    return {
        getMillisecs: function() { return millisecs; }
        getDefaultMsgsPerSecond: function() { return defaultMsgsPerSecond; }
    };
})();
like image 22
huysentruitw Avatar answered Sep 28 '22 11:09

huysentruitw