Get all combination of a nested object with arbitrary levels
Your code is clean and works fine.
The only little point I've noticed is that you didn't follow this rule:


instead of (for instance) for (var i = 0; i &lt;obj.length; i++)
you should write for (var i = 0, n = obj.length; i &lt; n; i++), so the obj.length is evaluated only once rather than for each iteration


That's said, it may be possible that it doesn't matter any longer in modern browsers, due to optimizers.

Beyond that, I was interested by your challenge to find "a better/more elegant solution".

Then I found one that uses reduced code, and probably works faster:



var data = {
  "title": {
     "original": "Hitchhiker",
     "more": ["HGTTG"],
     "link": null
  },
  "date": ["20150101", "20160101"]
}

function nestedObjectToArray(obj, prefix) {
  prefix = prefix ? prefix : '';
  var result = [], isArray = Array.isArray(obj), value, nextPrefix;
  obj = typeof obj == 'object' ? obj : JSON.parse('{"' + obj + '": null}');
  for (var key in obj) {
    if (obj.hasOwnProperty(key)) {
      value = obj[key];
      nextPrefix = prefix + (isArray ? '' : (key + '-'));
      result = result.concat(
        value === null ? (prefix + key) : nestedObjectToArray(value, nextPrefix)
      );
    }
  }
  return result;
}

console.log(nestedObjectToArray(data));




The main points of interest are:


The function gets a second argument prefix which is recursively transmitted, growing as depth increases: this allows to always add the complete prefix at each level, instead of having to re-process the complete result returned to prepend each item with the key of the ancestor level.
There is a unique loop to process arrays and objects: since the only difference is the absence/presence of a key, the test (is array ?) is made at this precise point, inside the generation for the current item:
nextPrefix = prefix + (isArray ? '' : (key + '-'));.
More over, also a final "leaf" is not processed separately, like:
if (typeof(obj) != "object") {return [obj];} in your version;
instead, the leaf is transformed into an object:
obj = typeof obj == 'object' ? obj : JSON.parse('{"' + obj + '": null}');
so it can be processed in the loop, where it falls into the (already planned by design, following your initial needs) case of null value: the trick is that the value became a key.