One extremely common thing you want to be able to do in JavaScript is iterate over objects. There are a lot of different ways you can do this, and they have subtly different results. As with most things where the are a proliferation of different ways to do the same thing that all end up slightly differently, object iteration is one of the JavaScript common pitfalls. This page only looks at how object iteration works in the common es5 environments. If you support ie8 and before, this isn't helpful.
For our purposes there are essentially for different kinds of properties. Normal own properties, inherited properties, nonenumerable properties, and properties that are both inherited and nonenumerable.
The first are properties that are actually on the object we have a reference to, and haven't been explicitly set to be nonenumerable. This is mostly the stuff we have added to an object ourselves. My favorite way to iterate over these things is `Object.keys`
var junker = new Car('Jalopy') function Car (name) { Object.defineProperty(this, 'name', { value: name, enumerable: false }) } Car.prototype.honk = function () { console.log('Aoogah aoogah!') } junker.mileage = 300000 console.log(Object.keys(junker)) // [ 'mileage' ]
The second are properties that are available via the prototype chain. These are properties like "class" style methods from object constructors we have defined, or methods on objects we have newed up from libraries we required. My favorite way to include these in our iteration is the "for in" loop.
var junker = new Car('Jalopy') function Car (name) { Object.defineProperty(this, 'name', { value: name, enumerable: false }) } Car.prototype.honk = function () { console.log('Aoogah aoogah!') } junker.mileage = 300000 for (var i in junker) { console.log(i) } // mileage // honk
The third are properties we have defined via "Object.defineProperty" to be nonenumerable, maybe to hide private information on an object, or define a one off method that shouldn't be accessed by code iterating over an object. This is also the responsible way for third party code to define properties on objects they don't control the usage of. If you want to get properties defined directly on an object regardless of enumerable status, use `Object.getOwnPropertyNames`.
var junker = new Car('Jalopy') function Car (name) { Object.defineProperty(this, 'name', { value: name, enumerable: false }) } Car.prototype.honk = function () { console.log('Aoogah aoogah!') } junker.mileage = 300000 console.log(Object.getOwnPropertyNames(junker)) // [ 'mileage', 'name' ]
The last are generally properties from built in base class prototypes, like '.toString' that is defined on all objects that inherit from Object.prototype, or '.map' on arrays. There isn't a straight forward way to get all of these properties at once, which is probably ok, since it is pretty rare to want them all. Here's how you'd do it if you need it:
var junker = new Car('Jalopy') function Car (name) { Object.defineProperty(this, 'name', { value: name, enumerable: false }) } Car.prototype.honk = function () { console.log('Aoogah aoogah!') } junker.mileage = 300000 console.log(Object.getOwnPropertyNames(junker)) var step = junker while (step = Object.getPrototypeOf(step)) { console.log(Object.getOwnPropertyNames(step)) } //[ 'name', 'mileage' ] //[ 'constructor', 'honk' ] //[ 'constructor', // 'toString', // 'toLocaleString', // 'valueOf', // 'hasOwnProperty', // 'isPrototypeOf', // 'propertyIsEnumerable', // '__defineGetter__', // '__lookupGetter__', // '__defineSetter__', // '__lookupSetter__', // '__proto__' ]
These all do pretty sensible things now. It wasn't always so. In es3 there wasn't such a thing as nonenumerable, or `Object.keys`... So be happy that very few people really have to support es3 environments anymore. If you do, sorry, sucks to be you.