lib/stringify.js
function canonicalize(value, stack) {
  var canonicalizedObj;
  /* eslint-disable no-unused-vars */
  var prop;
  /* eslint-enable no-unused-vars */
  var type = getType(value);
  function withStack(value, fn) {
    stack.push(value);
    fn();
    stack.pop();
  }

  stack = stack || [];

  if (stack.indexOf(value) !== -1) {
    return '[Circular]';
  }

  switch (type) {
    case 'undefined':
    case 'buffer':
    case 'null':
      canonicalizedObj = value;
      break;
    case 'array':
      withStack(value, function() {
        canonicalizedObj = value.map(function(item) {
          return canonicalize(item, stack);
        });
      });
      break;
    case 'function':
      /* eslint-disable guard-for-in */
      for (prop in value) {
        canonicalizedObj = {};
        break;
      }
      /* eslint-enable guard-for-in */
      if (!canonicalizedObj) {
        canonicalizedObj = emptyRepresentation(value, type);
        break;
      }
    /* falls through */
    case 'object':
      canonicalizedObj = canonicalizedObj || {};
      withStack(value, function() {
        Object.keys(value).sort().forEach(function(key) {
          canonicalizedObj[key] = canonicalize(value[key], stack);
        });
      });
      break;
    case 'date':
    case 'number':
    case 'regexp':
    case 'boolean':
      canonicalizedObj = value;
      break;
    default:
      canonicalizedObj = value.toString();
  }

  return canonicalizedObj;
}
function canonicalize(value, stack) {
  var canonicalizedObj;
  /* eslint-disable no-unused-vars */
  var prop;
  /* eslint-enable no-unused-vars */
  var type = getType(value);
  function withStack(value, fn) {
    stack.push(value);
    fn();
    stack.pop();
  }

  stack = stack || [];

  if (stack.indexOf(value) !== -1) {
    return '[Circular]';
  }

  switch (type) {
    case 'undefined':
    case 'buffer':
    case 'null':
      canonicalizedObj = value;
      break;
    case 'array':
      withStack(value, function() {
        canonicalizedObj = value.map(function(item) {
          return canonicalize(item, stack);
        });
      });
      break;
    case 'function':
      /* eslint-disable guard-for-in */
      for (prop in value) {
        canonicalizedObj = {};
        break;
      }
      /* eslint-enable guard-for-in */
      if (!canonicalizedObj) {
        canonicalizedObj = emptyRepresentation(value, type);
        break;
      }
    /* falls through */
    case 'object':
      canonicalizedObj = canonicalizedObj || {};
      withStack(value, function() {
        Object.keys(value).sort().forEach(function(key) {
          canonicalizedObj[key] = canonicalize(value[key], stack);
        });
      });
      break;
    case 'date':
    case 'number':
    case 'regexp':
    case 'boolean':
      canonicalizedObj = value;
      break;
    default:
      canonicalizedObj = value.toString();
  }

  return canonicalizedObj;
}
function canonicalize ( value , stack ) { var canonicalizedObj ; /* eslint-disable no-unused-vars */ var prop ; /* eslint-enable no-unused-vars */ var type = getType ( value ) ; function withStack ( value , fn ) { stack . push ( value ) ; fn ( ) ; stack . pop ( ) ; } stack = stack || [ ] ; if ( stack . indexOf ( value ) !== - 1 ) { return '[Circular]' ; } switch ( type ) { case 'undefined' : case 'buffer' : case 'null' : canonicalizedObj = value ; break ; case 'array' : withStack ( value , function ( ) { canonicalizedObj = value . map ( function ( item ) { return canonicalize ( item , stack ) ; } ) ; } ) ; break ; case 'function' : /* eslint-disable guard-for-in */ for ( prop in value ) { canonicalizedObj = { } ; break ; } /* eslint-enable guard-for-in */ if ( ! canonicalizedObj ) { canonicalizedObj = emptyRepresentation ( value , type ) ; break ; } /* falls through */ case 'object' : canonicalizedObj = canonicalizedObj || { } ; withStack ( value , function ( ) { Object . keys ( value ) . sort ( ) . forEach ( function ( key ) { canonicalizedObj [ key ] = canonicalize ( value [ key ] , stack ) ; } ) ; } ) ; break ; case 'date' : case 'number' : case 'regexp' : case 'boolean' : canonicalizedObj = value ; break ; default : canonicalizedObj = value . toString ( ) ; } return canonicalizedObj ; }
Return a new Thing that has the keys in sorted order. Recursive.

If the Thing...
- has already been seen, return string `'[Circular]'`
- is `undefined`, return string `'[undefined]'`
- is `null`, return value `null`
- is some other primitive, return the value
- is not a primitive or an `Array`, `Object`, or `Function`, return the value of the Thing's `toString()` method
- is a non-empty `Array`, `Object`, or `Function`, return the result of calling this function again.
- is an empty `Array`, `Object`, or `Function`, return the result of calling `emptyRepresentation()`
Return a new Thing that has the keys in sorted order . Recursive .