iteration an object and hasownproperty - javascript

I was looking at a clone object function here:
http://jsperf.com/cloning-an-object/2
the function is:
function clone(obj) {
var target = {};
for (var i in obj) {
if (obj.hasOwnProperty(i)) {
target[i] = obj[i];
}
}
return target;
}
and i was wondering why is the check
if (obj.hasOwnProperty(i))
needed ?
Because if property i is not in the object obj, it wouldn't be iterated in the first place in the for loop.
Am I missing something ?

The for...in construct also loops over inherited properties.
If you create an object with a constructor, for example like this :
var s = new String();
then you have all enumerable String functions listed among the properties, but not as own properties (i.e. direct properties). For example try this in the console :
for (var k in s) console.log(k)
You'll discover a few interesting functions that were probably indispensable to SO developers like formatUnicorn.
This check lets you clone direct enumerable properties without cloning the prototype properties.
See documentation on the MDN.

Because the original loop will also show properties from the prototype object, which you wouldn't want.

It's worth mentioning that as of JavaScript 1.8.5 you can use Object.keys(obj) to get an Array of properties defined on the object itself
(ones that return true for obj.hasOwnProperty(key)).
This is better (and readable) than using for-in loop.
Its supported on these browsers:
Firefox (Gecko): 4 (2.0)
Chrome: 5
Internet Explorer: 9
More info on
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys

Related

Can't list window.document properties - why?

Running this JavaScript lists in Firefox 60.2 only one property ("location"), but there are many others, like "document.title" etc.
window.console.log("List props: " + Object.keys(window.document).sort().join(' / '));
Why is it this way? Safety? How is this done technically?
How can I list all properties?
Object.keys(o) returns the own, enumerable properties of o.
Own: properties defined directly on o, not on its prototype chain:
Enumerable: a flag that controls if a given property is included when listing an object's properties.
In this case most of the keys you expect are defined on another object in in document's prototype chain:
document.hasOwnProperty("title"); // false
document.__proto__.__proto__.hasOwnProperty("title"); // true
document.__proto__.__proto__.propertyIsEnumerable("title"); // true
You can use a for... in loop to find the enumerable properties that are defined on an object or its prototype chain:
for (let key in window.document) {
console.log(key);
}
The reason is that Object.keys() returns returns an array of strings that represent all the enumerable properties of the given object.
Try this to see which properties of document are enumerable
for(let key in document){
let isEnumerable = document.propertyIsEnumerable(key);
console.log(`docment['${key}'] isEnumerable?:${isEnumerable}`);
}
However as the previous answer has stated you can use a for-in loop to get all properties in an array sort them and join them
I couldn't find an official reason for it not working with window.document but it seems you can reproduce this behavior with other variables as well.
The issue seems to be Object.keys() not returning everything from these variables.
If you're still trying to get all properties of document, you can still use
var props = [];
for(var prop in window.document) {
props.push(prop);
}
console.log("List props: " + props.sort().join('/'));
Which will do the exact same thing as your approach.

Why are Object.keys() and for ... in different?

I'm trying to do a bit of browser object discovery, figuring out browser built-ins etc...
I noticed different results when trying to get at the window object's properties (just FYI I'm using Chrome Version 41.0.2272.89 (64-bit)).
Object.keys(window).length;
returns 7 keys. From the docs Object.keys() returns the enumerable properties of an object.
But the docs also say that for ... in iterates over the enumerable properties of an object. However:
var i = 0;
for (var propertyName in window) {
i++;
}
returns a count of 177.
Why is this different? Shouldn't they both only be returning the count of enumerable properties?
for-in loops over the object's own enumerable properties and the enumerable properties of its prototype (and its prototype, etc.). Object.keys only lists the object's own enumerable properties.
So Object.keys builds an array something like this:
var keys = [];
var key;
for (key in object) {
if (object.hasOwnProperty(key)) { // But using an internal, non-overrideable
// operation, not literally the method
keys.push(key);
}
}
Note the hasOwnProperty check (it's not really a call to the method, it's an internal check that can't be tricked by replacing the method or similar).

IE8 for...in enumerator

So I am using this in IE8:
var hi=["hi", "lo", "foo", "bar"];
for(i in hi){console.log(i)};
//WTF is that indexOf i value?
LOG: 0
LOG: 1
LOG: 2
LOG: 3
LOG: indexOf
undefined
In chrome and others, I'll just get 0-3, no mysterious "indexOf" thing. Why and what's the fix?
Don't use for...in for arrays. It's best to use the traditional for loop in that case.
The reason is because for...in looks at the array as an object, and therefore properties like indexOf or length may be included in the loop. The normal for loop only deals with numeric keys, so this problem is avoided.
On a side note, unwanted properties could show up when iterating over plain objects as well (as others have noted, properties you add to the object's prototype will show up). You can get around this by writing your for...in loops this way:
var obj = { ... };
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
var item = obj[prop];
...
}
}
To be clear though: you still shouldn't use this method on arrays.
You're using the wrong type of loop for an array - for ... in ... will also include any enumerable properties of the object, which in your case includes the .indexOf() method.
Use this instead:
var i, n = hi.length;
for (i = 0; i < n; ++i) {
console.log(i, hi[i]);
}
Chrome and other up to date browsers implement ECMAScript 5, and correctly mark all built-in methods as non-enumerable properties.
This is happening because a script you are including on your page is adding the indexOf method to Array.prototype. This means all arrays inherit an indexOf method, which is good, since it means you can use that method even in IE8.
But, since there is no way to mark a property as non-enumerable in IE8, you will end up seeing it every time you enumerate over all the properties of the array, which is what you do in a for-in loop. You probably wanted a for loop instead.

How can I test whether this Javascript object has any properties?

I have some code that adds properties to an object like this:
var MyObject = new Object();
if (....) { MyObject.prop1 = .... ; }
if (....) { MyObject.prop2 = .... ; }
if (....) { MyObject.prop3 = .... ; }
After going through all these if statements, I want to test and see whether MyObject has properties. I don't want to test if it has prop1 || prop2 || prop3 because there are 12 properties that can be added.
How do I test to see if it has at least one? I tried if (MyObject) but of course that doesn't work because that only tests the existence of the object. Is these something like a simple one-liner like (MyArray.length) for objects?
If you're working in a pre-ECMAScript5 environment, it's easy enough with a small loop:
var found = false, name;
for (name in MyObject) {
if (MyObject.hasOwnProperty(name)) {
found = true;
break;
}
}
Or since you're using var MyObject = new Object(); to create it (BTW, you can just use var MyObject = {};), you don't actually need the hasOwnProperty call:
var found = false, name;
for (name in MyObject) {
found = true;
break;
}
for..in loops through the enumerable properties of an object, both its own and ones it inherits from its prototype; hasOwnProperty tells you whether the property comes from the object itself or its prototype. All of the properties you're adding will be enumerable, so they'll show up in the loop. Raw objects ({}) have no enumerable properties initially unless someone has been mucking about with Object.prototype (which is a Really Bad Idea), hence the second loop above.
ECMAScript5 (released a couple of years ago, not supported by older browsers and support varies a bit even in more recent ones) adds a couple of functions you could use for this. Probably the most relevant is Object.keys, which returns an array of the names of the object's "own" properties (not properties it inherits from its prototype). That would mean you wouldn't need the loop:
if (Object.keys(MyObject).length) { // Only on ES5-compliant browsers
// Yes it has at least one
}
Try
Object.getOwnPropertyNames(MyObject).length;
This will give you number of object's own properties.
See documentation.
Object.getOwnPropertyNames returns an array whose elements are strings corresponding to the enumerable and non-enumerable properties found directly upon obj.
If you want to support older browsers that don't have Object.getOwnPropertyNames use this:
var getPropertyNames = Object.getOwnPropertyNames || function(obj) {
var propNames = [];
for (var propName in obj) {
if(obj.hasOwnProperty(propName)) {
propNames.push(propName);
}
}
return propNames;
}
getPropertyNames(MyObject).length; // number of own properties

Javascript - Prototyped Function Name Returned As A Key Of Associative Array

I'm wanting to add a prototyped function to javascript's array object.
The problem I'm having is that when I step the keys of an array, the prototyped function's name appears in the array's keys.
Here's a sample to illustrate the problem:
<html>
<head>
<script type="text/javascript">
Array.prototype.foo = function () {
// do something
}
function keys() {
var fruit = ["apples","pears","bannanas"];
for (key in fruit) alert(key); // alerts: 0, 1, 2, foo
}
</script>
</head>
<body onload="keys();">
</body>
</html>
Any ideas on how I can get around this?
I don't have to prototype the function but, from a readability point of view, I'd prefer to.
EDIT: Just to be clear, the real world implementation of the above example iterates through an associative array, hence the for..in loop
EDIT: Thanks for the replies guys but I think you're missing what I'm after. What I'm wanting to know is whether I can add a prototyped function to javascript's array object (or declare the function in a different way that would have the same effect) without having the function name pitch up while iterating through an array via a for..in loop. Take the indexOf function for example. Javascript seems to define it internally in such a way that it doesn't appear in the keys of the object/array (right?). Can this same behaviour be reproduced at runtime?
You need to check whether fruit.hasOwnProperty(key) in the loop.
To avoid inherited enumerable properties with for..in, use a hasOwnProperty test:
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
// p is a property of obj and not inherited
}
}
Using for..in with arrays is not recommended - if you want an object, use an Object.
Arrays are normally used where the members are accessed in order, e.g. using a counter in a loop. Using for..in the order in which properties are returned is implementation dependant and differs between browsers, so you can't guarantee order.
As James says use rather than...
function keys() {
var fruit = ["apples", "pairs", "bananas"];
for(var key in fruit) {
alert(key);
}
}
Instead do the following...
function keys() {
var fruit = ["apples", "pairs", "bananas"];
for(var i = 0; i < fruit.length; ++i) {
alert(i);
}
}
As the length property will only update with the indexed array items and not items added to the prototype.
To iterate trough an associative array with for in and get what you want i would use typeof to type-check the propriety and restrict to non-functions. You can use
number,
string,
boolean,
object,
function
and undefined
with typeof
http://msdn.microsoft.com/en-us/library/259s7zc1%28v=vs.94%29.aspx
I appreciate it isn't always available but you can avoid the whole mess by using Array#forEach or if you're using a JavaScript library (quite likely and highly recommended) then you have something like Prototype's Enumerable#each(), Sugar's Array#each() or jQuery's $.each().

Categories