0

I came across this little snippet of code for property reflection in JavaScript:

function GetProperties(obj) {
    var result = [];
    for (var prop in obj) {
        if (typeof obj[prop] !== "function") {
            result.push(prop);
        }
    }
    return result;
}

I've tested it using the following "CustomObject":

var CustomObject = (function () {
    function CustomObject() {
        this.message = "Hello World";
        this.id = 1234;
    }

    Object.defineProperty(CustomObject.prototype, "Foo", {
        get: function () {
            return "foo";
        },
        enumerable: true,
        configurable: true
    });

    Object.defineProperty(CustomObject.prototype, "Bar", {
        get: function () {
            return "bar";
        },
        enumerable: true,
        configurable: true
    });

    return CustomObject;
})();

Here is a little test using jQuery:

$(document).ready(function () {
    console.log(GetProperties(new CustomObject()));
});

Here are the results:

["message", "id", "Foo", "Bar"]

I understand that the GetProperties function just returns an array of anything in the input object that is not a function, but I want to filter the results to get only the "real" properties, so my output should be:

["Foo", "Bar"]

Is this possible?

Also, can I do the opposite and just return the fields?

Matthew Layton
  • 39,871
  • 52
  • 185
  • 313

2 Answers2

1

There are two things you could do (and possibly more, it depends on your exact situation):

  1. Name "private" properties differently, e.g. with a trailing underscore and check whether the property name ends with an underscore when you are iterating over the properties (and exclude them).

  2. If by "real properties" you mean the properties defined on the prototype and you want to ignore all properties defined on the object itself, you can use .hasOwnPrototype to check where it is defined. Alternatively, you could use Object.getPrototypeOf and iterate over the properties of the prototype only.

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • After asking another question (http://stackoverflow.com/questions/16688537/implementing-instance-members-methods-in-javascript), I understood the difference between implementing properties on the object or on the prototype. My understanding prior to this was that anything implemented on the prototype became an instance member for anything that constructed the prototype. I can now see the difference. – Matthew Layton May 24 '13 at 08:54
0

Bad code. I'm leaving (with a comment) it 'cause the subsequent discussion might help somebody else.

If you always use defineProperty() to get non enumerable properties, then this would work:

function GetProperties(obj) {
    var result = [];
    for (var prop in obj) {
        // propertyIsEnumerable() returns false just because the properties
        // are inherited thru the prototype chain. It was just a coincidence
        // that it got the desired result. Don't do this.
        if (typeof obj[prop] !== "function" && !obj.propertyIsEnumerable(prop)) {
            result.push(prop);
        }
    }
    return result;
}

Otherwise, I'd be curious to know a general solution to the problem.

EDIT: I see that the code has enumerable: true and still my code does exactly what was asked. Double you tee ef?

Dek Dekku
  • 1,441
  • 11
  • 28
  • There's no reason for a `!obj.propertyIsEnumerable(prop)` in a loop. If the property is not enumerable, that that property will be skipped anyway. –  May 17 '13 at 16:46
  • Double WTF, then? Wait, I'm, running the code in a shell. Gonna try it locally and see if I get the same result. EDIT: Tried locally, same result. – Dek Dekku May 17 '13 at 16:49
  • Yeah, this is weird. `obj.propertyIsEnumerable(prop)` is returning `false` for the properties that were not added via `defineProperty`... and they're clearly being enumerated. Same behavior in FF and Chrome. That's a strange behavior for a method called `propertyIsEnumerable` –  May 17 '13 at 16:55
  • Same result in IE, Firefox and Chrome. This calls for another question. – Dek Dekku May 17 '13 at 16:57
  • 1
    Ah, I see what it is. I had it backwards. It says the ones on the prototype chain are not enumerable. Checked with the spec, and the `propertyIsEnumerable` doesn't look at prototyped properties. [15.2.4.7 Object.prototype.propertyIsEnumerable](http://es5.github.io/#x15.2.4.7) *"**NOTE 1** This method does not consider objects in the prototype chain."* –  May 17 '13 at 17:00
  • 1
    Faith in JavaScript restored, then... :D – Dek Dekku May 17 '13 at 17:03