I was attempting to extend Javascript's Object type with a function that retrieves all of the property names that are part of that object (basic开发者_运维百科ally what PHP's array_keys()
function does to associative arrays).
Object.prototype.keys = function() {
var a = [];
for (var property in this) {
a.push(property);
}
return a;
};
var a = {a:1,b:2,c:3,d:4};
alert(a.toSource());
var b = a.keys();
alert(b.toSource());
When the variable b
was alerted, I was expecting to see ["a","b","c","d"]
, instead I'm seeing ["a","b","c","d","keys"]
.
It appears the for-in loop is including the prototyped keys()
functions.
Is it possible to avoid this behaviour whilst still prototying, or should I just avoid prototyping all together?
Ahh the joys of extending the built-ins. for..in
checks up the entire prototype chain of the object being iterated.
What you need (and I believe this is de-facto now) is a check with hasOwnProperty
in your loop:
for (var property in this) {
if (this.hasOwnProperty(property)) {
a.push(property);
}
}
hasOwnProperty
ensures that you only get properties that are directly defined on your object (i.e. not up the prototype chain).
For most situations, you should avoid extending the native Object.prototype
. When you extend it, ALL objects receive those new properties.
Looping over an object properties is a really common task, and as you noted, you were experiencing an unexpected behavior.
I have not seen many people who always use the hasOwnProperty
function in their code, I would say that under "controlled environments", maybe being a solo developer working on a project, you could manage those issues, but when the things get bigger, and you have more developers involved, this can cause a lot of problems.
What about making a static function on Object?
Object.keys = function(obj) {
var a = [];
for (var property in obj) {
if (obj.hasOwnProperty(property)) {
a.push(property);
}
}
return a;
};
var a = {a:1,b:2,c:3,d:4};
alert(a.toSource());
var b = Object.keys(a);
alert(b.toSource());
The for...in statement iterates over all enumerable properties of an object that are keyed by strings (ignoring ones keyed by Symbols), including inherited enumerable properties. MDN
So you can define non-enumerable properties.
Object.defineProperty(Array.prototype, 'foo', {
value: 'bar',
enumerable: false // Defaults to false.
});
const arr = [1,2,3];
for(const k in arr) console.log(k); // 0, 1, 2
console.log(arr.foo); // bar
This also works:
var items = [
{
id: 1, name: 'Item 1'
},
{
id: 2, name: 'Item 2'
},
{
id: 3, name: 'Item 3'
}
];
items.forEach(function(item, index) {
console.log(item);
});
精彩评论