Generate table of contents form HTML
First, your indentation is half-indentation. It should be four spaces instead of two. Try putting your code into JSFiddle's TidyUp function to see how to better format.



You can improve the following block by swapping the logic and not initialising last_item:

You also don't need a === check, == will do for integers.


function lastHeadingAtLevel(heading, level) {
  if (level !== 0) {
    // Recurse through lists until you hit the required level.
    var last_item = getLastHeading(heading);
    last_item = lastHeadingAtLevel(last_item, level-1);
    return last_item;
  }
  // No recursion required.
  return heading;
}



by applying those changes, you get the following:

function lastHeadingAtLevel(heading, level) {
    if (level == 0) {
        // No recursion required.
        return heading;
    }
    // Recurse through lists until you hit the required level.
    var last_item = getLastHeading(heading);
    return lastHeadingAtLevel(last_item, level - 1);
}




Instead of string concatenation (can be slow), you should use an array join instead (faster):


var id_name = id + '-' + level + '-' + title.replace(/\s+/g, '-').toLowerCase();



into: 

var id_name = [id, level, title.replace(/\s+/g, '-').toLowerCase()].join('-');




In the following block, keys is an array, so instead of a for loop, use a forEach loop:


  for (var i = 0; i &lt; keys.length; i++) {
    if (keys[i].startsWith('#')) {
      continue;
    }
    if (parseInt(keys[i], 10) &gt; last_element) {
      last_element = parseInt(keys[i], 10);
    }
  }



which could transform it into the following:

var lastElement = -1;
Object.keys(heading).forEach(function(key){
    if (key.startsWith('#')) {
        continue;
    }
    if (parseInt(key, 10) &gt; lastElement){
        lastElement = parseInt(key, 10);
    }
});


However, in fact, you don't even need to do that.

Using a polyfill, you can access the last item:

if (!Object.prototype.last){
    Object.prototype.last = function(){
        return this[Object.keys(this)[Object.keys(this).length - 1]];
    };
};


Now just access it like: {'a': 1, 'b': 2, 'c': '3'}.last();