This is because the rules for enumeration include a clause requiring string keys. Bear in mind that enumeration and asking for keys are different operations with entirely different rules.
Looking at the section for for ... in/for ... of head evaluation (13.7.5.12), it states that the iteration is done using:
- If iterationKind is enumerate, then - c. Return - obj.[[Enumerate]]().
 
The description of [[Enumerate]] (9.1.11) very clearly states that it:
Return an Iterator object (25.1.1.2) whose next method iterates over all the String-valued keys of enumerable properties of O.
The check for enumerable properties comes later in the body, and the pseudo-code example makes this even more clear:
function* enumerate(obj) {
  let visited=new Set;
  for (let key of Reflect.ownKeys(obj)) {
      if (typeof key === "string") { // type check happens first
          let desc = Reflect.getOwnPropertyDescriptor(obj,key);
          if (desc) {
              visited.add(key);
              if (desc.enumerable) yield key; // enumerable check later
          }
      }
  }
  ...
}
(comments mine)
Clearly, properties with non-string keys will not be enumerated. Using this example:
var symbol = Symbol();
var object = {};
Object.defineProperty(object, symbol, {
    value: 'value',
    enumerable: true
});
Object.defineProperty(object, 'foo', {
  value: 'bar',
  enumerable: true
});
Object.defineProperty(object, 'bar', {
  value: 'baz',
  enumerable: false
});
Object.defineProperty(object, () => {}, {
  value: 'bin',
  enumerable: true
});
for (let f in object) {
  console.log(f, '=', object[f]);
}
for (let k of Object.getOwnPropertyNames(object)) {
  console.log(k);
}
you can verify that in Babel and Traceur.
However, you'll see two interesting things:
- getOwnPropertyNamesincludes non-enumerable properties. This makes sense, as it follows completely different rules.
- for...inincludes non-string properties under both transpilers. This does not seem to match the spec, but does match ES5's behavior.