hasOwnProperty when to use, and when not needed? - javascript

It is recommended to always use hasOwnProperty, but in many cases this is not needed.
For example consider the following code:
var object = JSON.parse(somejsontext);
for(var prop in object) {
console.log(object[prop]);
}
I know in this case that prop is part of the object, it is explict define by the for..in.
But according to MOZ https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty we should use it to avoid iterating over non-enumarable props, with this example:
var buz = {
fog: 'stack'
};
for (var name in buz) {
if (buz.hasOwnProperty(name)) {
console.log('this is fog (' + name + ') for sure. Value: ' + buz[name]);
}
else {
console.log(name); // toString or something else
}
}
But testing this code actually, never goes to the else.
So when does make sense the use of hasOwnProperty ?
UPDATE: Considering the choosen answer, we can safetly avoid the use of hasOwnProperty in this cases:
- Object js has not been extended by any javascript library or by our code
- Object is a simple code that we have control on

The problem arises when a prototype includes enumerable properties that your code did not anticipate. For example, suppose this ran just before your example:
Object.prototype.foobar = "hello";
In this case, iterating over buz would include the enumerable foobar prototype property. This hasOwnProperty pattern allows your code to differentiate between properties that are directly on the object, versus properties that are inherited from a prototypal ancestor.
The problem is not "enumerating over non-enumerable properties" (that's by definition not possible, unless you explicitly get them via getOwnPropertyNames for each level of the prototype hierarchy), but rather enumerating over inherited properties. This is a concern when using libraries that may add enumerable properties to high-level prototypes, exactly as I have illustrated above.
If you want to add a property to a prototype without causing that property to be enumerated, you can make a non-enumerable property with Object.defineProperty:
Object.defineProperty(Object.prototype, "foobar", {
value: "hello",
enumerable: false,
writeable: true,
configurable: true
});
Such a property would not appear in a for..in loop on buz (or in a for..in loop directly on Object.prototype, either).

Related

In Javascript, "for...in" doesn't iterate over properties of prototype?

On MDN (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty), I saw snippet like this:
var buz = {
fog: 'stack'
};
for (var name in buz) {
if (buz.hasOwnProperty(name)) {
console.log('this is fog (' + name + ') for sure. Value: ' + buz[name]);
}
else {
console.log(name); // toString or something else
}
}
In the snippet, console.log(name); // toString or something else looks a bit confusing to me. I understand toString is a property of Object.prototype and this property can be accessed by buz.toString.
However, when I run the snippet in Chrome, I didn't saw the toString property is printed (console.log), though the comments say it should be iterated by for..in and printed..
Does anyone have ideas about why the properties from one's prototype can't be iterated by "for..in"?
for x in obj iterates over only enumerable properties of the object (including those on the prototype). A given property can be marked as enumerable or not. So, if for/in is skipping some property, then that is likely because that property is not configured as enumerable.
See working demo here: http://jsfiddle.net/jfriend00/gyc9gnmj/ that shows how only enumerable properties are iterated with for x in obj.
And, in fact, you can see that the toString() method is marked as enumerable: false with this code:
function log(x) {
document.write(JSON.stringify(x));
}
var buz = {
fog: 'stack'
};
log(Object.getOwnPropertyDescriptor(Object.getPrototypeOf(buz), "toString"));
Just to shed more light in continuation with what jfriend00 has already answered, Object's data properties in JavaScript has four attributes namely [[configurable]], [[enumerable]], [[writable]] and [[value]] all of which is set to for the former three the default value is true and for the last it defaults to undefined.
Say suppose there exists a need where you would like to avoid a property's value not to be returned in a for-in loop. You can write as
var car = {};
Object.defineProperty(car, 'name',{
enumerable : false,
value : 'BMW'
});

Access to internal object fields in Js

Having this code
var a = {date : 12};
for(var t in a){
alert(t);
}
I have one alert with date property.
But everywhere (i.e. here) i see that it is needed to write :
for (var property in object) {
if (object.hasOwnProperty(property)) {
// do stuff
}
}
So why i don't see internal properties of object?
The for..in syntax iterates every property of the object, including those found on the prototype chain (if they are enumerable). This is usually not what you want, so the hasOwnProperty check is usually recommended.
In your case it's not necessary, as your plain object doesn't inherit any enumerable properties from the Object prototype (the only thing in its prototype chain)
Also, for..in gives you the property names (keys) of the object, if you're interested in the value of that particular property, you have to use the bracket notation, extending your example to this:
var a = {date : 12};
for(var t in a){
alert(a[t]);
}
In JavaScript there exists the 'prototype chain', a form of inheritance. If you were to add a property to the prototype of a, and then add your date property directly onto a, your for loop would find both those properties.
The hasOwnProperty checks whether the current property has been attached directly to the context object or whether it has been inherited through the prototype chain.
So in my example, if you had added a property to a's prototype, the hasOwnProperty condition would return false and you wouldn't "do stuff" with it.
I guess the best way to see it is through an example:
Object.prototype.aBaseProperty = 123;
var childObject = { date: 12 };
for (var property in childObject) {
console.log(property)
}
// output: date aBaseProperty
for (var property in childObject) {
if (childObject.hasOwnProperty(property)) {
console.log(property)
}
}
// output: date
Also, rather than doing the hasOwnProperty check, you might prefer
Object.keys(object).forEach(function(value, key) { ... });
Where Object.keys will only return own properties.

Extend already defined class in JavaScript

The traditional way of extending classes in JS is something like this:
// define constructor function
function Fuu(){}
// extend prototype and override prototype's constructor
Fuu.prototype = Object.create(OtherClass.prototype, {
constructor: {
value: Fuu,
enumerable: false,
writable: true,
configurable: true
}
});
Then you add the methods you want to the prototype
Fuu.prototype.method = function() {}
And just like that you have a function extending another. A nice example of inheritance in JS!
My question is how to extend when the sub class already has a prototype with methods and properties. I could try to copy the the methods of the old prototype to the new one using a for in loop but the methods are non enumerable(class is created with a transpiler) and doing something with getOwnPropertyNames doesn't seem right. Any suggestion? can I do something like keeping the prototype and adding a prototype to the prototype?
Edit: example
class Fuu {
someMethod(){} // non enumerable method in Fuu's prototype
}
// My first option: (extending this way `someMethod` is lost)
Fuu.protoype = Object.create(HTMLElement.prototype, {//...same as before})
// Option 2: copy methods from old to new prototype
// Option 3: prototype of prototype?
// Fuu.prototype.prototype = Object.create(HTMLElement.prototype, {...})
You want something like
┌──> Fuu.prototype
instances ──┤
└──> OtherClass.prototype
But that's not possible, because objects only have one [[Prototype]].
Therefore, you must achieve one of these:
instances ───> Fuu.prototype ───> OtherClass.prototype
instances ───> OtherClass.prototype ───> Fuu.prototype
So you must set the [[Prototype]] of one of those to be the other one. I will assume the first possibility.
There are two main ways to set the [[Prototype]]:
Object.create, when creating the object
The problem is that both Fuu.prototype and OtherClass.prototype have been created already.
However, you can create a new object with the right [[Prototype]] and assign the properties of the old one.
Since there may be non-enumerable properties, you must use getOwnPropertyNames. Using defineProperty and getOwnPropertyDescriptor may also be a good idea, in case there are getters or setters.
var old = Fuu.prototype,
props = Object.getOwnPropertyNames(old);
Fuu.prototype = Object.create(OtherClass.prototype);
for(var i=0; i<props.length; ++i)
Object.defineProperty(
Fuu.prototype,
props[i],
Object.getOwnPropertyDescriptor(old, props[i])
);
setPrototypeOf or __proto__ (ES6), once the object has been created:
Object.setPrototypeOf(Fuu.prototype, OtherClass.prototype);
Fuu.prototype.__proto__ = OtherClass.prototype;
However, be aware that
Mutating the [[Prototype]] of an object is, by the nature of how
modern JavaScript engines optimize property accesses, a very slow
operation, in every browser and JavaScript engine. The effects on
performance of mutating prototypes [...] may extend to any code that
has access to any object whose [[Prototype]] has been mutated. If you
care about performance you should avoid mutating the [[Prototype]] of
an object.
I think the method you have suggested is probably the bet way to go. Is there a reason why you think it is wrong?
var old = Fuu.prototype;
Fuu.prototype = Object.create(OtherClass.prototype, {
constructor: {
value: Fuu,
enumerable: false,
writable: true,
configurable: true
}
});
var names = Object.getOwnPropertyNames(old);
for (var i = 0; i < names.length; i++) {
var name = names[i];
Fuu.prototype[name] = old[name];
}
The only thing I'd be concerned about is your constructor method being overridden by the old version, and for your old prototype's prototype chain being lost; however you can do things to fix this.

What does enumerable mean?

I was directed to MDN's for..in page when it said, "for..in Iterates over the enumerable properties of an object."
Then I went to the Enumerability and ownership of properties page where it said "Enumerable properties are those which can be iterated by a for..in loop."
The dictionary defines enumerable as countable, but I can't really visualize what that means. Could i get an example of something being enumerable?
An enumerable property is one that can be included in and visited during for..in loops (or a similar iteration of properties, like Object.keys()).
If a property isn't identified as enumerable, the loop will ignore that it's within the object.
var obj = { key: 'val' };
console.log('toString' in obj); // true
console.log(typeof obj.toString); // "function"
for (var key in obj)
console.log(key); // "key"
A property is identified as enumerable or not by its own [[Enumerable]] attribute. You can view this as part of the property's descriptor:
var descriptor = Object.getOwnPropertyDescriptor({ bar: 1 }, 'bar');
console.log(descriptor.enumerable); // true
console.log(descriptor.value); // 1
console.log(descriptor);
// { value: 1, writable: true, enumerable: true, configurable: true }
A for..in loop then iterates through the object's property names.
var foo = { bar: 1, baz: 2};
for (var prop in foo)
console.log(prop); // outputs 'bar' and 'baz'
But, only evaluates its statement – console.log(prop); in this case – for those properties whose [[Enumerable]] attribute is true.
This condition is in place because objects have many more properties, especially from inheritance:
console.log(Object.getOwnPropertyNames(Object.prototype));
// ["constructor", "toString", "toLocaleString", "valueOf", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", /* etc. */]
Each of these properties still exists on the object:
console.log('constructor' in foo); // true
console.log('toString' in foo); // true
// etc.
But, they're skipped by the for..in loop because they aren't enumerable.
var descriptor = Object.getOwnPropertyDescriptor(Object.prototype, 'constructor');
console.log(descriptor.enumerable); // false
If you create an object via myObj = {foo: 'bar'} or something thereabouts, all properties are enumerable. So the easier question to ask is, what's not enumerable? Certain objects have some non-enumerable properties, for example if you call Object.getOwnPropertyNames([]) (which returns an array of all properties, enumerable or not, on []), it will return ['length'], which includes the non-enumerable property of an array, 'length'.
You can make your own non-enumerable properties by calling Object.defineProperty:
var person = { age: 18 };
Object.defineProperty(person, 'name', { value: 'Joshua', enumerable: false });
person.name; // 'Joshua'
for (prop in person) {
console.log(prop);
}; // 'age'
This example borrows heavily from Non-enumerable properties in JavaScript, but shows an object being enumerated over. Properties can either be or not be writable, configurable, or enumerable. John Resig discusses this in the scope of ECMAScript 5 Objects and Properties.
And, there's a Stack Overflow question about why you'd ever want to make properties non-enumerable.
It's a lot more boring than something that should be visualized.
There is literally an attribute on all properties called "enumerable." When it is set to false the for..in method will skip that property, pretend it doesn't exist.
There are a lot of properties on objects that have "enumerable" set to false, like "valueOf" and "hasOwnProperty," because it's presumed you don't want the JavaScript engine iterating over those.
You can create your own non-enumerable properties using the Object.defineProperty method:
var car = {
make: 'Honda',
model: 'Civic',
year: '2008',
condition: 'bad',
mileage: 36000
};
Object.defineProperty(car, 'mySecretAboutTheCar', {
value: 'cat pee in back seat',
enumerable: false
});
Now, the fact that there is even a secret about the car is hidden. Of course they can still access the property directly and get the answer:
console.log(car.mySecretAboutTheCar); // prints 'cat pee in back seat'
But, they would have to know that the property exists first, because if they're trying to access it through for..in or Object.keys it will remain completely secret:
console.log(Object.keys(car)); //prints ['make', 'model', 'year', 'condition', 'mileage']
They should have just called it, "forInAble."
I will write one line definition of ENUMERABLE
Enumerable: Specifies whether the property can be returned in a for/in loop.
var obj = {};
Object.defineProperties(obj, {
set1: {enumerable: true},
set2: {enumerable: false},
});
Object.keys(obj); // ["set1"]
Object.getOwnPropertyNames(obj); // ["set1", "set2"]
If you're having difficulty visualising "what does it mean to be enumerable?" why not ask yourself, what does it mean to be nonenumerable?
I think of it a bit like this, a nonenumerable property exists but is partially hidden; meaning that nonenumerable is the weird one. Now you can imagine enumerable as what is left - the more natural property we're used to encountering since we discovered Objects. Consider
var o = {};
o['foo'] = 0; // enumerable, normal
Object.defineProperty(o, 'bar', {value: 1}); // nonenumerable, weird
Now in a for..in, imagine it like pseudocode
for property in o:
if not property enumerable continue // skip non-enumerable, "bar"
else do /* whatever */ // act upon enumerable, "foo"
where the body of the loop you typed in JavaScript is in the place of /* whatever */
Built-in methods that objects inherit are not
enumerable, but the properties that your code adds to objects are enumerable unless explicitly stated
Think of the enum data type, just a structure of objects that correspond to different numbers. To declare something to as an enumerable is to declare that it corresponds to a specific number, allowing it to be given a place in a Dictionary that represents countable components of an object. To put it simply, making an object enumerable is the same as telling the compiler, "Hey, this property counts, I want to see this when I check for data on this object."
methods are not enumerable; or rather built in methods are not.. tho after searching on what enumerable means to java script; it just refers to a property attribute.. all created objects in ecma3 are enumerable, and ecma5 u can now define it....that's it.. :D lol took me a bit to find the answer; but I believe its talked about in David Flanagan's book.. so I guess it means "hidden", or not "hidden" in that methods are not shown in the for in loop, and thus are "hidden"

Object.defineProperties/create really needed?

Some projects use Object.create() or Object.defineProperties() function. I wonder is is recommended? Whats the difference between
x = Object.create(null);
vs
x = {}
And
x = {}
x.__proto__.hello = function() {
console.log("hello");
}
vs
x = Object.create(null);
Object.defineProperty(x, "hello", {
value: function() {
console.log("hello");
}
});
defineProperty/create seems very verbose and long to me. When/Why do I use them? Perhaps the good might be to enforce getters/setters/overriding properties?
There is a huge difference. Have a look at the docs!
Object.create does create an Object that inherits from the first argument, null in your case. In contrast, {} - or new Object() - creates a new object that inherits from Object.prototype.
__proto__ is non-standard and should not be used. However, in your case you just do Object.prototype.hello = function() {…};. Never extend that object with enumerable properties, never ever!
Object.defineProperty does define a property on an object with a special descriptor object. The enumerable, configurable and writable attributes default to false, which means that you wont be able to delete x.hello for example, or assign any other value.
Your first snippet creates a plain object, which inherits a hello method from Object.prototype, while your second snippet creates an object inheriting from nothing and having a non-editable hello property. I don't see much relatedness.

Categories

Resources