Extending map objects - javascript

I don't know how to extend the map object with prototype and hope you can help.
I have something like this:
var map = {'one':1, 'two':2};
and I would like to have a method to check for the existence of a key:
if (map.containsKey('one')){...}
How would I extend the map object?

It's dangerous to modify Object.prototype, because it affects all objects and will usually break other libraries you may be using. In general, if you want to add methods to a dictionary-like object, you should create an object for your hash instances to inherit from, like the Prototype Hash object.
For this specific instance, you should really just use either if (key in map) or if (map.hasOwnProperty(key)).

There is already an operator to test the existence of a key inside an object.
(In JavaScript, objects are implemented as maps so actually there is no real map.)
if( 'one' in map )
{
alert(map['one']);
}

These are not "maps" they are objects. Building on Maurice's answer, if you want this to apply to all objects:
Object.prototype.containsKey = function(key) {
return this.hasOwnProperty(key);
};

The simplest way to do this is to add a function directly in the object:
map.containsKey = function(key) {
return this[key] ? true : false;
};

In Javascript, you do not need such method really.
if ( map['one'] ) {
// do something
}
should do it

As other's have said, extending Object.prototype might not be a good idea if your code has to play nice with code written by people ignorant of hasOwnProperty().
Anyway, there are three 'correct' ways I know of to check if a property is available:
obj.hasOwnProperty(name)
checks if a property with given name exists in the object.
name in obj
additionally includes properties inherited via an object's prototype chain.
typeof obj[name] !== 'undefined'
will additionally evaluate to false if the property is present but has been set to undefined.
Some non-JS object's (e.g. window.external in IE) might not implement hasOwnProperty(), so one of the other checks has to be used.

Related

How to deal with JavaScript Objects which have no constructor

Node.js querystring.parse() method returns what looks like to be an object, but one without a constructor. According to https://nodejs.org/api/querystring.html :
"... The object returned by the querystring.parse() method does not prototypically inherit from the JavaScript Object. This means that typical Object methods such as obj.toString(), obj.hasOwnProperty(), and others are not defined and will not work."
This easily causes bugs because typically you would assume that every Object understands some basic methods like toString() and that it has a "constructor" which more or less tells us its "type".
What's the best way to handle these rather incapable Objects? I tried:
let myOb = new Object(dumbObject);
But that produces a result which does not have the toString() -method either and does not have the property 'constructor'.
What's the best way to turn these dumb objects into ordinarily behaving ones? And, why would anybody want to create such objects in the first place?
I think (from the top of my head)
let newObject = JSON.parse(JSON.stringify(dumbObject))
should work.
If you want a more generic way to call toString() on an object, you can call it from Object.prototype using .call().
var s = Object.prototype.toString.call(smartObject);
But why? It's just going to give you "[object Object]" on any plain object. Why is it important to get that string?
var p = {};
var s = Object.create(null);
console.log(p.toString());
console.log(Object.prototype.toString.call(s));
typically you would assume
No, you wouldn't. If you make such assumptions, document them in your interface.
What's the best way to turn these dumb objects into ordinarily behaving ones?
Use Object.assign with an ordinary object as the target. Alternatively, you can also change the prototype by using Object.setPrototypeOf, but that's not recommended.
Or just create the properties like .toString or .constructor that you need. See this example.
And, why would anybody want to create such objects in the first place?
Because you need this safety when using objects instead of Maps. See this example.
Explicitly set the prototype of your dumb object with Object#setPrototypeOf
var dumbObject = Object.create(null);
Object.setPrototypeOf(dumbObject, Object.prototype);
dumbObject.toString() //runs fine
Thanks for all the answers, one of which contained a link to another question which really was my question as well, even if I didn't know that at first. That other question is: How to create a JS object with the default prototype from an object without a prototype?
From it I found the answer which I think is the simplest solution so far: Use Object.assign(). Like this:
let orphan = require('querystring').parse("{}") ;
// console.log ( "Got value: " + orphan ) ;
// Above causes the error:
// -> TypeError: Cannot convert object to primitive value
let oa = (x) => Object.assign({}, x);
console.log ("Got value: " + oa (orphan) ) ;
Note the issue is not particularly about "querystring" but with objects which have no prototype in general. Yes we should probably call these poor objects "orphans" instead of "dumb". But I think "dumb" is still quite good term as well. An object which has no prototype has very few (or no?) methods so it can answer very few if any questions we would like to ask it.

using a prototype on potentially empty parameters in JS

I'm updating a script that checks parameters like this:
if (config.param) {//do...}
This has lead to some issues in truthiness. I'd implemented a truthy() function as a prototype so as to be able to do the following:
if (config.param.truthy()) {//do...}
That seemed fine until I remembered that the config object only contains params being explicitly set. So config.value is usually undefined as opposed to false.
I'd added the prototype within an appropriate scope via String/Number/Boolean.prototype.truthy.
Is it possible to implement the prototype on the config object such that it will be able to return false on non-defined parameters?
Edit: My question is not about the truthiness (I can simply use a function), but instead how to implement a prototype on an object such that it is inherited by its properties, which may be different types or non-existent.
if ("param" in config && !!config.param) {//...
Or if you want to use a default value if undefined then
config.param = config.param || defaultValue
The whole problem with prototyping in this case is that falsey values will not get autoboxed to objects and so attempting to call methods of them will result in error.
Assuming that your params will all be different types of data, then your approach of adding prototype methods for Strings/Booleans/etc could work well. Then you could simply see if that property exists by doing
if (config.hasOwnProperty("param") && config.param.truthy() ) {//do...}
or if you're using dynamic property names
if( config.hasOwnProperty(param) && config[param].truthy() ) {//do...}

Use cases of Object.create(null)?

If you create a regular javascript object using say var obj = {}; it will have the object prototype. Same goes for objects created using var obj = new MyClass(); Before Object.create was introduced there was no way around this. However nowadays it's possible to create an object with no prototype (respectively null as its prototype) using var obj = Object.create(null);.
Why is this important? What advantages does it bring? Are there any real world use cases?
It's a completely empty object (nothing inherited from any .prototype including Object.prototype), so you can be guaranteed that any property lookup will only succeed if the property was explicitly added to the object.
For example, if you want to store some data where you won't know the keys in advance, there's a possibility that a provided key will have the same name as a member of Object.prototype, and cause a bug.
In those cases, you need to do an explicit .hasOwnProperty() check to rule out unexpected inherited values. But if there's nothing inherited, your testing can be simplified to a if (key in my_object) { test, or perhaps a simple truthy test if (my_object[key]) { if appropriate.
Also, with no prototype chain, I would imagine that lookups of properties that turn out to not exist would be faster, since only the immediate object would need to be checked. Whether or not this pans out in reality (due to optimizations) would be determined with performance testing.
The only difference here between creating an object with {} and Object.create(null) is that the Object prototype will not be inherited from.
This means that any of the methods we typically assume we can call on any object will not exist, such as toString and valueOf. A list of these can be found here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype
From a performance perspective, creating an object simply by {} is actually much faster, so unless you specifically cannot have the functions under the Object prototype you should not create objects in that manner.
http://jsperf.com/null-vs-empty-object-performance

obj.hasOwnProperty(key) vs directly referencing using obj[key]

Lets say I want to check whether a property exists within an Object or not. I was looking at two methods:
if(object.hasOwnProperty(key)) { /* do this */ }
OR
if(object[key]) { /* do this */ }
Assuming that we'll never have object = { key: false };
What are the tradeoffs of using one over the other ?
object[key] checks the entire object chain, including the prototype chain.
object.hasOwnProperty(key) checks to see if the key property is assigned directly to the object and does not look in the prototype chain.
So, which one to use depends upon which result you want.
See a simple demonstration using the hasOwnProperty property: http://jsfiddle.net/jfriend00/6zhv87rk/ which is on the prototype for an object.
You would typically use object.hasOwnProperty(key) when your key can be any arbitrary string and you want to make absolutely sure that it will never conflict with a built-in property on the Object prototype. In fact, if you really want to protect yourself, you even use this Object.prototype.hasOwnProperty.call(object, key) so even the hasOwnProperty property could be used with your object. Cases like this would be when you're using a Javascript object as a hash lookup or dictionary-type object with arbitrary key values.
You would typically use object[key] when you are not concerned about such a conflict because you know what types of keys will be on your object and you know they won't conflict because this version is just shorter and a bit easier to read.

Is there an easy way to remove mootools namespace pollution?

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!

Categories

Resources