A clientside javascript library I've developed uses objects as hashes in some areas. It loops through objects parsed from Json data with a for...in loop using the property name as a key. eg... (pseudo code)
var conversations = {'sha1-string':{name:'foo',messages:[]}}
for(var id in conversations){
console.log(id);
console.log(conversations[id].name);
}
Unfortunately MooTools (and Prototype, etc) add methods to the global namespaces, so my for...in loops now iterate through MooTools' additions (eg. limit, round, times, each), causing errors when it applies logic to them as if it were the data expected.
As it's a library, I have to expect that it will be used with MooTools, Prototype, etc. Is there an easy way around this problem? My current solution is just to pass the object to a method which strips out the MooTools specific entries and returns the clean object, but this means also checking what Prototype and all similar libraries out there add, and seems to be a backwards way of doing things.
My other solution is to stop relying on the property name as a key, and perform validation in the loops to ensure I'm looking at the data I want to. Before I do that rewriting though, I'm wondering if anyone has a better/existing solution?
Thanks :)
If your clientside objects are not inherited from other custom objects, you see if you could use the javascript's Object.hasOwnProperty method to find out if a certain property exists in the object itself and not up in the inheritance chain via the prototype object.
For browsers that don't support this method, you can write a wrapper around to check:
var hasOwnProperty = function(object, property) {
if(object.hasOwnProperty) {
return object.hasOwnProperty(property);
}
var prop = object[property];
return typeof(prop) !== "undefined" && prop !==
object.constructor.prototype[property];
}
How to use it:
for(var key in someObj) {
if(hasOwnProperty(someObj, key)) {
// we are good to go!
}
}
If I got it correctly, you are looking for the hasOwnProperty() method.
As "limit", "round", "times" and "each" are all additional methodes of the Array prototyp you're main failure is to use "for in" to iterate over arrays. You should check the type of the object before and use "for in" only for objects and "for" for arrays.
Btw. "for in" is the slowest method to iterate over arrays.
Using both MooTools and Prototype isn't a very good idea as they modify the base prototypes and can't co-exist peacefully. That said, none of them modify Object.prototype (not anymore) so you should not see any other properties besides your own.
Did you add a plugin which is perhaps adding these properties there? Could you give a more complete example that shows where these properties are getting listed and also the versions of these libraries that you are using?
hasOwnProperty checks will solve the prototype problem, but this shouldn't be needed in the first place, so maybe it's better to find out exactly which script is messing up.
MooTools has created a Hash type just because they didn't want to modify Object.prototype. Here's a quote from the docs:
Hash
A custom Object ({}) implementation which does not account for prototypes when setting, getting, or iterating. Useful because in JavaScript, we cannot use Object.prototype. Instead, we can use Hash.prototype!
Related
Incoming "noob" question:
Javascript has an includes method on arrays.
It looks like:
Array.includes()
But when I go to the Javascript Manual to understand this method, the heading on that page (for an appropriate technical reason, I realize) is:
Array.prototype.includes()
Similar things in the Javascript Manual have caused me to not like the manual at all (and alas, I rely on W3Schools more than the manual).
However, I really really want to learn to interpret the manual.
So, my question is: what's the significance of including the word .prototype in Array.prototype.includes() in the documentation, when the actual usage looks like: Array.includes()?
(Also, if anyone has suggestions on how I can improve my comprehension of the official Javascript Manual, I'd appreciate suggestions.)
So, my question is: what's the significance of including the word .prototype in Array.prototype.includes() in the documentation, when the actual usage looks like: Array.includes()?
The significance is that actual usage doesn't look like Array.includes():
Array.includes();
That will throw a TypeError: Array.includes is not a function because Array.includes doesn't exist. Accessing a non-existing property evaluates to undefined, so Array.includes evaluates to undefined and thus Array.includes() is trying to call undefined as if were a function.
You can see that in action here:
console.log(Array.includes);
undefined();
The includes() method is defined on the prototype of the Array global object so that you can call it on instances of Array:
[].includes();
You can see that [].includes is a function:
console.log([].includes);
Compare this to Array.from which is defined on the Array constructor, not on the Array prototype:
console.log(Array.from);
You can use it like this:
console.log(Array.from({ length: 10 }, (_, num) => num << 2));
If the documentation said Array.includes() you would literally type it like this (example):
Array.includes(1);
Instead it says Array.prototype.includes() which means it isn't called on the Array type itself, but on an instance of it. So in this case you would write:
const numbers = [1, 2, 3];
numbers.includes(1);
JavaScript is often described as prototype-based language, prototypes is simply how inheritance works in JavaScript.
What does prototype means ?
Both of us agree that almost everything in JavaScript is an object (I said "Almost" because primitives are not considered objects) cool?
Okay, now every Object in JS has an internal property called [[Prototype]] and by internal I mean that you can't access it directly the same way you access a JS object's property.
If we want to know the prototype of an object that we have created we either pass the instance of our object to Object.getPrototypeOf or through the __proto__ property of our object
For example:
let myArray = [1,2,3,4];
console.log(myArray.__proto__)
// Expected output: Array []
If you expand the resulting object you get from the little code snippet above you will find the includes method you were asking about and all of the methods available on any array you create in a JS code!
That's because myArray and all arrays in JavaScript are said to share the properties and methods defined on Array.prototype!
Now, if you look again at the methods of the resulting object which you have from the code snippet above you will notice a method called constructor, defined on the Array.prototype just like includes and the other methods
That's the function invoked when you create an instance of the JavaScript Array object!
What do we mean by the JavaScript Array object?
It's a global JavaScript object that is used in the construction of arrays, it's the Array in Array.prototype.includes() (you may call it a class for convenience buuuuut classes did not exist practically until the release of ES6...before then there was no such thing as class in JS)
So to keep it simple and wrap it up think of Array as the global object that alll JS arrays are instances of, and think of Array.proto as it's prototype which wraps the properties and methods that all of its instances share!
And regarding the documentation, being able to read the documentation and have a considerable understanding of what you read is actually something good so I believe you're just fine !
I'm going through the MDN docs on arrays and when we want to test whether or not an object is an array we use isArray(). However, it's usage is very different to most of the other methods. When you use the regular syntax an error pops up:
console.log([1,2,3].isArray()); // TypeError: [1, 2, 3].isArray is not a function
Whereas this does work:
console.log(Array.isArray([1,2,3]))
I don't understand why isArray() (and a couple of other methods) rely upon some global object rather than just being accessible via the object in question. Why do some methods require the global array object?
rather than just being accessible via the object in question.
Because the whole purpose of Array.isArray is to be called on unknown objects. You don't know whether it's an array, you wouldn't know whether the method was accessible on it. The static method even works with values like null, which would inevitably throw an exception when you tried to invoke a method on them.
Digression:
isArray being callable as an instance method can work. In languages where everything is an object, like Smalltalk, Ruby or Io, even things like nil can have a isNil method. This approach has other problems of course, as with dynamic method dispatch every arbitrary object could overwrite the method and claim to be an array - on the other hand, that's exactly what we want for duck typing.
We could even simulate this in JS using Object.prototype.isArray = () => false; and Array.prototype.isArray = () => true;. Apart from failing on null and undefined, it still wouldn't work with objects that don't inherit from (our realm's) Object.prototype. And JavaScript "properties" that mix data fields and methods don't help either (consider the object parsed from the JSON string {"isArray":"maybe"}). We would always have to expect an exception from either .isArray not being a function, or from it being overwritten with a method that throws.
If we want to go for duck typing in JS, checking whether an object has an integer .length property is usually the way to go. Or more advanced, trying to follow the symbol-based iteration protocol. (That's what Array.from uses, for example).
But since arrays are a bit special in JS (with their magic .length property), we want a built-in reliable way to detect them, and that's what Array.isArray does.
Regarding other static Array methods: Array.of is pretty obvious, it's a factory function (like a constructor) and it can't be an instance method because there is no instance to work with in the first place. For Array.from the situation is a bit more like with isArray, a duck-typing Object.prototype.toArray approach could have worked as well but was dismissed for practical and design reasons.
See also Why were ES5 Object methods not added to Object.prototype?, Why use Object.prototype.hasOwnProperty.call(myObj, prop) instead of myObj.hasOwnProperty(prop)? and Why is it Object.defineProperty() rather than this.defineProperty() (for objects)? for similar discussions.
.isArray() is a static method, it exists on the Array "class" but not on each particular instance. Static methods are called like this: Array.isArray(). Object.create() is another example.
Methods like .map or .slice are not static, so they exist on each instance of the Array "class".
Array (denoted with a captial "A") indicates the JavaScript native Array object. [...] represents an instance of an array. As with many languages, some properties/methods are static. That is, they don't exist on instances only the type. This is often done when the value of the property or behavior of the method doesn't vary from instance to instance, so there's no need for it to be "instance-specific". Figuring out if something is an array, doesn't change from instance to instance (and frankly, doesn't make a lot of sense to ask an array instance if it is an instance of an Array).
I have a simple object of objects:
window.abilities = {
migrate:{
name:"Migrate",
description:"Move your tribe to another area; generate all new resources. Takes one time unit.",
image:"migrate.png",
action:"Migrate",
unlocked:true
},
eradicate:{
name:"Eradicate species",
description:"Remove a troublesome plant or animal",
image:"migrate.png",
action:"Eradicate",
unlocked:false
}
}
I am using a for ... in ... loop to iterate over this object and generate UI elements:
for(ability in window.abilities){
if(ability.unlocked){
$("#abilities").append(genAbilityCard(ability.name,ability.image,ability.description,ability.action));
}
}
However, each ability variable is empty - it only has the key and not the properties (name, description, and so forth). These properties seem to be unenumerable - even though properties created this way should be enumerable by default!
How can I make these properties enumerable implicitly, without using Object.defineProperty or something unwieldy like that?
If you're able to use ES6 and the for... of loop, Alberto's answer is your best bet, and the cleanest option.
If you're limited to ES5, you can use for ... in as well. As you've found, for ... in only enumerates the keys (the property names), not the property values, but it's easy to get the property value once you know the property name:
var abilityName, ability;
for(abilityName in window.abilities){
ability = window.abilities[abilityName];
if(ability.unlocked){
$("#abilities").append(genAbilityCard(ability.name,ability.image,ability.description,ability.action));
}
}
The fact you're getting the property names means you ARE successfully enumerating the properties. Javascript is just a bit weird in how for ... in works.
Try iterating by using for..of and Object.values (ES6 only):
for (const ability of Object.values(window.abilities)){
if (ability.unlocked){
$("#abilities").append(genAbilityCard(ability.name,ability.image,ability.description,ability.action));
}
}
I'm making a small jQuery-like library, and one thing striking me odd is the behavior of $.each.
In javascript we have a for...in loop:
for (var key in obj) {
console.log(key + ': ' + obj[key]);
}
The problem with this, is that it will iterate over inherited properties as well, that is, properties coming from the object constructor's prototype.
One can know this using hasOwnProperty, for example. And jQuery could do that.
But, when you pass an object to $.each, it behaves exactly like a for...in, iterating over inherited properties as well. It also should be marginally slower, and requires a few more characters to type.
Check this fiddle to see it in action and look here for the source code of $.each.
So my question is, is there an object iteration method in jQuery that only includes own properties? If not, should a library behave like this?
Edit: Since jQuery does not do this, you can also answer if this is useful. I mean, I cannot see myself wanting to iterate over prototype properties, but maybe I'm missing something.
The jQuery behavior makes sense, as you can always choose to add the hasOwnProperty check in the loop - or not.
I can see that looping through inherited properties could be useful in some scenarios (cloning or sub-class or whatever you call it).
I frequently get an array of an objects keys using:
Object.keys(someobject)
I'm comfortable doing this. I understand that Object is the Object constructor function, and keys() is a method of it, and that keys() will return a list of keys on whatever object is given as the first parameter. My question is not how to get the keys of an object - please do not reply with non-answers explaining this.
My question is, why isn't there a more predictable keys() or getKeys() method, or keys instance variable available on Object.prototype, so I can have:
someobject.keys()
or as an instance variable:
someobject.keys
And return the array of keys?
Again, my intention is to understand the design of Javascript, and what purpose the somewhat unintuitive mechanism of fetching keys serves. I don't need help getting keys.
I suppose they don't want too many properties on Object.prototype since your own properties could shadow them.
The more they include, the greater the chance for conflict.
It would be very clumsy to get the keys for this object if keys was on the prototype...
var myObj: {
keys: ["j498fhfhdl89", "1084jnmzbhgi84", "jf03jbbop021gd"]
};
var keys = Object.prototype.keys.call(myObj);
An example of how introducing potentially shadowed properties can break code.
There seems to be some confusion as to why it's a big deal to add new properties to Object.prototype.
It's not at all difficult to conceive of a bit of code in existence that looks like this...
if (someObject.keys) {
someObject.keys.push("new value")
else
someObject.keys = ["initial value"]
Clearly this code would break if you add a keys function to Object.prototype. The fact that someObject.keys would now be a shadowing property breaks the code that is written to assume that it is not a shadowing property.
Hindsight is 20/20
If you're wondering why keys wasn't part of the original language, so that people would at least be accustomed to coding around it... well I guess they didn't find it necessary, or simply didn't think of it.
There are many possible methods and syntax features that aren't included in the language. That's why we have revisions to the specification, in order to add new features. For example, Array.prototype.forEach is a late addition. But they could add it to Array.prototype, because it doesn't break proper uses of Array.
It's not a realistic expectation that a language should include every possible feature in its 1.0 release.
Since Object.keys does nothing more than return an Array of an Object's enumerable own properties, it's a non-essential addition, that could be achieved with existing language features. It should be no surprise that it wasn't present earlier.
Conclusion
Adding keys to Object.prototype most certainly would break legacy code.
In a tremendously popular language like JavaScript, backward compatibility is most certainly going to be an important consideration. Adding new properties to Object.prototype at this point could prove to be disastrous.
I guess an answer to your question is "Because the committee decided so", but I can already hear you ask "why?" before the end of this sentence.
I read about this recently but I can't find the source right now. What it boiled down to was that in many cases you had to use Object.prototype.keys.call(myobject) anyway, because the likelihood of myobject.keys already being used in the object for something else.
I think you will find this archived mail thread interesting, where for example Brendan Eich discuss some aspects of the new methods in ECMAScript 5.
Update:
While digging in the mail-archive I found this:
Topic: Should Object.keys be repositioned as Object.prototype.keys
Discussion: Allen argued that this isn't really a meta layer operation
as it is intended for use in application layer code as an alternative
to for..in for getting a list of enumerable property names. As a
application layer method it belongs on Object.prototype rather than on
the Object constructor. There was general agreement in principle, but
it was pragmatically argued by Doug and Mark that it is too likely
that a user defined object would define its own property named "keys"
which would shadow Object.prototype.keys making it inaccessible for
use on such objects.
Action: Leave it as Object.keys.
Feel free to make your own, but the more properties you add to the Object prototype, the higher chance you'll collide. These collisions will most likely break any third party javascript library, and any code that relies on a for...in loop.
Object.prototype.keys = function () {
return Object.keys(this);
};
for (var key in {}) {
// now i'm getting 'keys' in here, wtf?
}
var something = {
keys: 'foo'
};
something.keys(); // error