I'm working through The Principles of Object-Oriented Javascript and am confused with Zakas's use of a named key inside an array (as opposed to inside an object). See comment:
function EventTarget() {}
EventTarget.prototype = {
constructor: EventTarget,
addListener: function(type, listener) {
if (!this.hasOwnProperty("_listeners")) {
// Why isn't this: `this._listeners = {};`
this._listeners = [];
}
if (typeof this._listeners[type] === "undefined") {
this._listeners[type] = [];
}
this._listeners[type].push(listener);
},
// more stuff
}
var target = new EventTarget();
target.addListener("message", function(event) {
console.log("Message is " + event.data);
});
His code works fine (as it does if you substitute the array for an object literal), but my understanding has been that you should use an object if you want to access the contents by name. From the array article on w3schools:
Many programming languages support arrays with named indexes.
Arrays with named indexes are called associative arrays (or hashes).
JavaScript does not support arrays with named indexes.
In JavaScript, arrays always use numbered indexes.
Is there a good reason that Zakas used an array like this? Can you please explain it? Alternatively, is this something I should submit to the errata?
I'm working through The Principles of Object-Oriented Javascript and am confused with Zakas's use of a named key inside an array (as opposed to inside an object). See comment:
function EventTarget() {}
EventTarget.prototype = {
constructor: EventTarget,
addListener: function(type, listener) {
if (!this.hasOwnProperty("_listeners")) {
// Why isn't this: `this._listeners = {};`
this._listeners = [];
}
if (typeof this._listeners[type] === "undefined") {
this._listeners[type] = [];
}
this._listeners[type].push(listener);
},
// more stuff
}
var target = new EventTarget();
target.addListener("message", function(event) {
console.log("Message is " + event.data);
});
His code works fine (as it does if you substitute the array for an object literal), but my understanding has been that you should use an object if you want to access the contents by name. From the array article on w3schools:
Many programming languages support arrays with named indexes.
Arrays with named indexes are called associative arrays (or hashes).
JavaScript does not support arrays with named indexes.
In JavaScript, arrays always use numbered indexes.
Is there a good reason that Zakas used an array like this? Can you please explain it? Alternatively, is this something I should submit to the errata?
Share Improve this question edited Sep 13, 2015 at 15:43 Henry Marshall asked Sep 13, 2015 at 15:35 Henry MarshallHenry Marshall 8702 gold badges8 silver badges21 bronze badges 8 | Show 3 more comments4 Answers
Reset to default 9The only reason I can see is : confusing people. JavaScript doesn't enforce anything, really, and since everything is an object, you can do pretty much whatever you want. This guy is using an array to store named properties, but he could very well have used a function or anything else !
Edit : almost everything is an object (it would have occured to me that someone could try to set a property on undefined
, since one of the most common errors in JavaScript is TypeError : undefined is not an object
. sighs JavaScript, why are you doing this to us ?
Arrays are just special objects....
a=[]
a[7]=9
a["abc"]=20
a["xxx"]=30
for (i in a) console.log("value",i,a[i])
Output;
value 7 9
value abc 20
value xxx 30
Is there a good reason that Zakas used an array like this?
From the quoted code, I can't think of one, no. He's not using the fact that _listeners
refers to an array. It frankly just looks like a typo. Since it works (because JavaScript's normal arrays are objects), the typo wasn't caught. Well, until you caught it. :-)
Unless there's some code you haven't quoted which is then adding array enries* to that array, there's no reason to use an array there (and arguably a couple of reasons not to).
* "array entry" = property whose key is an array index. So what's an "array index"? "A String property name P is an array index if and only if ToString(ToUint32(P))
is equal to P and ToUint32(P)
is not equal to 232−1
." (spec)
I'm inclined to say it's a typo but in the off-chance it was intended, then the following is the only functional use-case I can come up with... (warning: may divide opinion).
Given that an observer/event pattern is a generic concept that could be applied to any object and the "private" (denoted by the underscore) _listeners
property will likely never need to be known about by the target - then it's arguable that the data it contains is superfluous to the object itself.
That is to say, it's probably not desirable to transmit this kind of data should the target object be serialized. The following example illustrates how the JSON serializer ignores the non-numeric array properties in foo.baz
- similarly in your own example, all attached event data would be removed:
var foo = {
bar: {},
baz: []
};
foo.bar['p1'] = foo.baz['p1'] = 1;
foo.bar['p2'] = foo.baz['p2'] = 2;
console.log( JSON.stringify(foo) ); // {"bar":{"p1":1,"p2":2},"baz":[]}
type
is integer – hindmost Commented Sep 13, 2015 at 15:41Object.getOwnPropertyNames(this._listeners)
and thenObject.keys(this._listeners)
.this._listeners
should be an object, not an array. – rgajrawala Commented Sep 13, 2015 at 15:42type
isn't an integer (and it seems pretty likely it won't be, much more likely to be a string like"click"
). – T.J. Crowder Commented Sep 13, 2015 at 15:43"length"
fortype
. Then exciting things happen. – Raymond Chen Commented Sep 13, 2015 at 15:44Map
. – T.J. Crowder Commented Sep 13, 2015 at 15:48