You can achieve a generic JSON.stringify() that includes non-enumerables with code like below. But first, some notes:
- This code has not been thoroughly tested for possible bugs or unexpected behavior; also, it turns functions into basic objects. Treat accordingly. 
- copyEnumerable()is the function to pay attention to. It does not itself internally call- JSON.stringify()(you can do that yourself, with the output object) mainly because recursive calls to- JSON.stringify()(due to nested objects) will yield uglier, unwanted results.
 
- I'm only checking for - objectand- functiontypes to be treated specially; I believe that these are the only types which need to be handled specially... Note that JS- Arrays(- []) fall into this category (objects), as well as classes (functions), and- nulldoes not. Since- nullis of type- object, I included the- !!check.
 
- The - stopRecursiveCopy- Setis just to make sure it's not doing infinite recursion.
 
- I'm using a trick with the 2 extra parameters to - JSON.stringify()calls to make it output something formatted prettier, for easy reading.
 
The code is in an easy format to try out and tweak in the console:
{
  o = {};
  d = {};
  Object.defineProperties(o, {
    a: {
      value: 5,
      enumerable: false
    },
    b: {
      value: "test",
      enumerable: false
    },
    c: {
      value: {},
      enumerable: false
    }
  });
  Object.defineProperty(o.c, "d", {
    value: 7,
    enumerable: false
  });
  //The code to use (after careful consideration!).
  function isObject(testMe) {
    return ((typeof(testMe) === "object" && !!testMe) ||
      typeof(testMe) === "function");
  }
  let stopRecursiveCopy = new Set();
  function copyEnumerable(obj) {
    if (!isObject(obj)) {
      return obj;
    }
    let enumerableCopy = {};
    for (let key of Object.getOwnPropertyNames(obj)) {
      if (isObject(obj[key])) {
        if (!stopRecursiveCopy.has(obj[key])) {
          stopRecursiveCopy.add(obj[key]);
          enumerableCopy[key] = copyEnumerable(obj[key]);
          stopRecursiveCopy.delete(obj[key]);
        }
      } else {
        enumerableCopy[key] = obj[key];
      }
    }
    return enumerableCopy;
  }
  console.log(JSON.stringify(copyEnumerable(o), null, "  "));
  Object.defineProperty(copyEnumerable, "test", {
    value: 10,
    enumerable: false
  });
  console.log(JSON.stringify(copyEnumerable(copyEnumerable), null, "  "));
  Object.defineProperty(o, "f", {
    value: copyEnumerable,
    enumerable: false
  });
  console.log(JSON.stringify(copyEnumerable(o), null, "  "));
}
which outputs:
//o
{
  "a": 5,
  "b": "test",
  "c": {
    "d": 7
  }
}
//copyEnumerable itself
{
  "test": 10,
  "prototype": {
    "constructor": {
      "test": 10,
      "length": 1,
      "name": "copyEnumerable"
    }
  },
  "length": 1,
  "name": "copyEnumerable"
}
//o again, but with `f: copyEnumerable` added
{
  "a": 5,
  "b": "test",
  "c": {
    "d": 7
  },
  "f": {
    "test": 10,
    "prototype": {},
    "length": 1,
    "name": "copyEnumerable"
  }
}
Pertinent links: