3

I have a function which I want to make it available to all objects. Its the mergeObject(object) function as defined below :

Object.prototype.mergeObjects = function(object){
    if (typeof object != "object") return;
    for(var key in object){
        if (object.hasOwnProperty(key))
            this[key] = object[key];
    }
    return this;
}

I have a variable events which is :

events : {
    "click #retryBtn":"onRetryBtnClick",
    "click #cancelBtn":"onCancelBtnClick",
    "click .dialogButton":"onDialogBtnClick"
}

This is a backbone's events json Object.

when I try to run a for loop for keys in events

for (var key in events) 
{
    console.log(key) 
}

It prints out

click #retryBtn
click #cancelBtn
click .dialogButton
mergeObjects

I dont understand why I get "mergeObjects" as key in "events" object.

The structure of events object is :

click #cancelBtn: "onCancelBtnClick"
click #retryBtn: "onRetryBtnClick"
click .dialogButton: "onDialogBtnClick"
__proto__: Object
    __defineGetter__: function __defineGetter__() { [native code] }
    __defineSetter__: function __defineSetter__() { [native code] }
    __lookupGetter__: function __lookupGetter__() { [native code] }
    __lookupSetter__: function __lookupSetter__() { [native code] }
    constructor: function Object() { [native code] }
    hasOwnProperty: function hasOwnProperty() { [native code] }
    isPrototypeOf: function isPrototypeOf() { [native code] }
    mergeObjects: function (object){                 
    propertyIsEnumerable: function propertyIsEnumerable() { [native code] }
    toLocaleString: function toLocaleString() { [native code] }
    toString: function toString() { [native code] }
    valueOf: function valueOf() { [native code] }
    get __proto__: function __proto__() { [native code] }
    set __proto__: function __proto__() { [native code] }

Can anyone explain me why "mergeObject" is part of the for loop keys?

How can I avoid it? Since there are other methods like "__defineGetter" which doesnt appear in the for loop keys.

Kpatel1989
  • 335
  • 3
  • 13

2 Answers2

3

mergeObjects is enumerable property( meaning it can be accessed using for in) which is being inherited by events object whereas methods like __defineGetter are non-enumerable(can't be accessed by for in).

for in loops traverse all the enumerable properties of the object including properties in the prototype chain as well.

Use hasOwnProperty to avoid the inherited properties and get only direct enumerable property.

for (var key in events) 
{
    if(events.hasOwnProperty(key))
    console.log(key) 
}

If you want all the direct properties of an object either enumerable or non-enumerable( set using defineProperty with enumerable false ) then you should iterate over Object.getOwnPropertyNames()(IE9+) like

var keys = events.getOwnPropertyNames(); 
for(var i=0;i<keys.length;i++)
{
    console.log(keys[i]) 
}
/*
Or using forEach
events.getOwnPropertyNames().forEach(function(key){
    console.log(key);
})
*/    

Additional Link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Enumerability_and_ownership_of_properties

EDIT: In order to not get mergeObjects inside for in loops use Object.defineProperty with enumerable false like

Object.defineProperty(Object.prototype, 'mergeObjects', {
  value: function(object){
             if (typeof object != "object") return;
                 for(var key in object){
                    if (object.hasOwnProperty(key))
                        this[key] = object[key];
                 }
             return this;
   },
   enumerable:false  //no need for this statement as default is false
});

It works in IE9 and above

bugwheels94
  • 30,681
  • 3
  • 39
  • 60
  • Yes I can do this in my code. But there is an issue : The above for loop was just an example. Such a for loop exists in Backbone framework, it iterates through the keys in events object, and thats where my code crashes. I cannot change It. So basically the for loop is not in my hand. I want to do something with my method which will prevent it from appearing in the loop.. Please give me another solution. – Kpatel1989 May 22 '15 at 11:14
  • then define your method as non-enumerable using `Object.defineProperty` – bugwheels94 May 22 '15 at 11:23
0

Use hasOwnProperty to skip inherited properties:

for (var key in events) 
{
    if (events.hasOwnProperty(key)) {
        console.log(key);
    }
}
Barmar
  • 741,623
  • 53
  • 500
  • 612
  • Yes I can do this in my code. But there is an issue : The above for loop was just an example. Such a for loop exists in Backbone framework, it iterates through the keys in events object, and thats where my code crashes. I cannot change It. So basically the for loop is not in my hand. I want to do something with my method which will prevent it from appearing in the loop.. Please give me another solution – Kpatel1989 May 22 '15 at 11:15
  • I'm very surprised that code for looping over an object in a popular framework doesn't already do this. – Barmar May 22 '15 at 11:17