Imagine that you’re using bytes sizes in your javascript code. For example, handling files. Most libraries use bytes as the common unit to manage sizes. However, what if you want to humanize those values into your view? Instead of printing 1234567899 Bytes, you should print 1,23 GigaBytes.

Now, this can be implemented (and refactored) with the following block of javascript:

var ByteSize = function(bytes, size) {
  'use strict';

  // Constants
  var UNITS = [
    { short: 'B', long: 'Bytes', factor: 1 },
    { short: 'kB', long: 'kiloBytes', factor: 1e3 },
    { short: 'MB', long: 'MegaBytes', factor: 1e6 },
    { short: 'GB', long: 'GigaBytes', factor: 1e9 },
    { short: 'TB', long: 'TeraBytes', factor: 1e12 },
  ];
  var UNITS_SEPARATOR = ' ';
  var DECIMALS = 2;

  // Private vars
  var bytes_value = null;

  // Public methods
  this.set = function(bytes, unit) {
    if(unit) {
      unit = unit.toLowerCase();
      unit = UNITS.find(function(u) {
        return u.short.toLowerCase() == unit || u.long.toLowerCase() == unit;
      });
      if(!unit) throw('Invalid unit name: ' + unit);
      bytes_value = Math.round(bytes * unit.factor);
    }
    else {
      bytes_value = Math.round(bytes);
    }
  };

  this.human = function() {
    var unit = UNITS[0];
    var value = bytes_value;

    for(var i = 1; i < UNITS.length && value / UNITS[i].factor >= 1; i++) {
      unit = UNITS[i];
    }

    return { value: bytes_value / unit.factor, unit: unit };
  };

  this.humanize = function(long) {
    var data = this.human();
    var unit = long ? data.unit.long : data.unit.short;

    // Round and return as a string
    return (Math.round(data.value * 10**(DECIMALS)) / 10**DECIMALS) + UNITS_SEPARATOR + unit;
  };

  // Define Bytes(), kiloBytes(), ... TeraBytes() methods.
  var self = this;
  UNITS.forEach(function(unit, i) {
    self[unit.long] = function() {
      return bytes_value / unit.factor;
    };
  });

  // Initialize
  this.set(bytes, size);
};

Which can be used like this:

var data = new ByteSize(123456789);

console.log(data.Bytes());        // 123456789
console.log(data.kiloBytes());    // 123456.789
console.log(data.MegaBytes());    // 123.456789
console.log(data.GigaBytes());    // 0.123456789
console.log(data.TeraBytes());    // 0.123456789

console.log(data.human());        // { value: 123.456789, unit: { short: 'MB', long: 'MegaBytes', factor: 1000000 } }
console.log(data.humanize());     // 123.45 MB
console.log(data.humanize(true)); // 123.45 MegaBytes

data.set(1234);
console.log(data.humanize());     // 1.23 kB

data.set(1234, 'kB');
console.log(data.humanize());     // 1.23 MB

data.set(1234, 'MegaBytes');
console.log(data.humanize());     // 1.23 GB

data = new ByteSize(1.2, 'kB');
console.log(data.Bytes());        // 1200

data = new ByteSize(1.2555, 'kiloBytes');
console.log(data.Bytes());        // 1256