7 ms·
An Object is not a Hash
- mcot2 14y agoIt sounds like this will all be fixed when js gets proper map types in ES6.
- just2n 14y agoWhen I first saw this post, it didn't include the following snippet: Object.create(null); I disagreed adamantly that a hash with pre-defined names is not a hash. The language already provides a means to get a hash without the pre-defined names, and any clever person can hack around naming collisions regardless. He now notes these things at the end of the article, but then the entire argument doesn't imply that a hash is not a hash. He does a good job pointing out a mistake ALMOST EVERYONE makes, and I applaud that, but the title is horribly misleading, even when given the proper context. Also, @mcot2, I didn't intend to post as a reply to you, but since I have, to answer your question, collections (Maps, Sets, and WeakMaps) are available in V8 and consequently Node already (via --harmony flags). Because the implementations of these with get/set is not tied to the object's properties, these don't have the same problem.
- comex 14y agoIsn't the whole point of Maps that their get/set methods are completely independent from object properties?
- Xcelerate 14y agoIt's unfortunate these new map types are so verbose: object.set(key, value) vs object.key = value
- dbbolton 14y agoContrary to this Stack thread: http://stackoverflow.com/questions/1143498/difference-between-an-object-and-a-hash http://stackoverflow.com/questions/1143498/difference-betwee...
- charliesome 14y agoI feel like you missed the point of the article.
- dbbolton 14y agoI feel like you misjudged the tone of my comment.
- philfreo 14y agoAs a shortcut, in underscore.js you can: _.has(object, key) or _(object).has(key) http://underscorejs.org/#has http://underscorejs.org/#has
- alanh 14y agoI love that the underscore doc for `has` links to the submitted article.
- hakaaak 14y agoOther than lists which can be represented by JSON arrays, hashes do fine jobs at describing most data and most "objects". This post goes off the rails almost immediately with "Normally this would work fine, but let’s consider that the user could pick any of the keys that are present in any JavaScript object as a name." WTF? The author almost immediately is confusing his/her current situation with the request body and what's in it. Then we find ourselves quizzically with "We therefore change our code to leverage hasOwnProperty". Umm, I don't think most developers would find themselves "therefore" doing this.
- alanh 14y agoI think you are missing something. If I understood the article’s example, he’s suggesting the (perfectly plausible) use case of a new blog post named, e.g., `hasOwnProperty`. Then they would want to set a key named `hasOwnProperty` in the `posts` object, but `hasOwnProperty` is already defined (via inheritance, since all objects have it) as a function (well, native code)… so now you have either (a) prevented your blog from allowing a reasonable blog post title, or (b) seriously mucked up your blog by accepting a blog post title that has the side effect of redefining a basic object method. The author is not confused.
- andrewvc 14y agoThe fact is, it's 2012. We all appreciate V8 making the web fast. It's stuff like this, however, that makes me question the sanity of those still promoting node.js The fact of the matter is that JS is bad at all the things a programming language is supposed to be good at. JS can't even perform integer math. It's also fairly alone in being async by default (and no, comparisons to erlang do not count). Does JS work? Sure, but believing JS is good is a special kind of delusion. We have decades of language research to work with, and once again we see the pull of the lowest common denominator.
- redidas 14y ago> JS Can't even perform integer math Can you explain the integer math thing? What exactly can't it do?
- snprbob86 14y agoJavaScript's Number type is double floating point: $ python Python 2.7.2 (default, Jun 20 2012, 16:23:33) [GCC 4.2.1 Compatible Apple Clang 4.0 (tags/Apple/clang-418.0.60)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> x = 1000000000000000001 >>> x 1000000000000000001 >>> x == x + 1 False $ irb irb(main):001:0> x = 1000000000000000001 1000000000000000001 irb(main):002:0> x == x + 1 false $ clj Clojure 1.4.0 user=> (def x 1000000000000000001) #'user/x user=> x 1000000000000000001 user=> (= (inc x) x) false $ node > x = 1000000000000000001 1000000000000000000 > x === x + 1 true
- jtchang 14y agoIs there some type of injection vulnerability here? Is it possible to attack a node.js server that creates objects via some API and give it a name that overwrites an internal javascript property? Your content would be the javascript injected code that might get run when the property is used internally?
- kansface 14y agoI suppose its possible, but the most likely case is just an unhandled exception. JSON does not support string literal notation for values which means in practice any function you replace won't be callable. I suppose someone more clever than I or more likely better motivated could combine this attack with another to do some harm. Having said that, use underscore for the safe references.
- drostie 14y agoThat's what the original poster is saying, yes. JS does not support a formal separation between the 'attributes' and 'keys' of a hash, so it's a security risk waiting to happen. In some cases the JS approach is very nice and useful: in Python you see gobs and gobs of classes used to hold data structures, mostly because people don't seem to like dicts in general. I remember a talk, but not the speaker or title of it, where it was pointed out that your Python class is probably a dict plus a function if it has exactly two methods, one of which is called __init__. Python being what it is, you may also extend Python to do what JS does, but slightly more intelligently: class obj(dict): def __init__(self, vals={}): for key in vals: self[key] = vals[key] def __setattr__(self, name, value): self[name] = value def __getattribute__(self, name): try: return dict.__getattribute__(self, name) except AttributeError: if name in self: return self[name] else: raise AttributeError("'obj' has no key '%s'" % name) This lets you use dotted setters/getters with Python without accidentally having x['keys'] = 1 override the obj.keys() method (which it inherits from dict). It's a serious problem in real Python code I've read and written; you create 'classes' which are only instantiated once (and are therefore semantically dicts). So the JS model really has some advantages for easy, clear namespacing (and packaging and enumeration) of identifiers, but yes, there is a potential security question if you allow people to set or get arbitrary JS attributes.
- mun2mun 14y agoI think this problem can be easily solved by encapsulating access/creation methods of posts objects in a object say postStore where posts variable is private property and the object have savePost(slug) and getPost(slug) methods where access/creation methods are defined by yourself. If posts object is that much mission critical than it should not be exposed to outer world.
- kansface 14y agoInstead of writing code around the problem, its better to just use a safe reference (ie, use Underscore's has method which will call Object.prototype.hasOwnProperty). This way code stays both simple and safe.
- breck 14y ago> Our program, however, is still susceptible to potential misbehaving. Let’s say our user decides to call his blog post "hasOwnProperty". This will virtually never happen. If you're worried about it, put an early return (if (name === 'hasOwnProperty') return). End of story.
- mtts 14y agoDownvoted because "this will virtually never happen" is, uhm, for want of a better term, incredibly stupid. Allow what you want to allow and disallow everything else.
- breck 14y agoSorry, this will happen 00.0001% of the time. Orders of magnitude less than dozens of other more pressing problems.
- Groxx 14y agoUnless you run a blogging platform which is used by JS programmers.
- Stratoscope 14y agoAw gee, you want a real hash in JavaScript, how hard can it be? Here's a quick and dirty one I whipped up in five minutes. It worked the first time with only one bug - I forgot to remove the prefix in the forEach method at first. function Hash() { this.hash = {}; } Hash.prefix = '#hash#prefix#_'; Hash.prototype.get = function( key ) { return this.hash[ Hash.prefix + key ]; }; Hash.prototype.set = function( key, value ) { return this.hash[ Hash.prefix + key ] = value; }; Hash.prototype.del = function( key ) { delete this.hash[ Hash.prefix + key ]; }; Hash.prototype.forEach = function( callback ) { for( var key in this.hash ) { if( key.indexOf(Hash.prefix) == 0 ) { callback.call( this, this.hash[key], key.slice(Hash.prefix.length) ); } } }; // Q&D test // Let's try to break things by extending Object.prototype! Object.prototype.spindizzy = true; var hash = new Hash; console.log( hash.get( 'a' ), hash.get( 'b' ) ); hash.set( 'a', 'AA' ); console.log( hash.get( 'a' ), hash.get( 'b' ) ); hash.set( 'b', 'BB' ); hash.forEach( function( value, key ) { console.log( key, value ); }); hash.del( 'a' ); console.log( hash.get( 'a' ), hash.get( 'b' ) ); hash.del( 'b' ); console.log( hash.get( 'a' ), hash.get( 'b' ) ); I'm sure I missed something, but this doesn't seem like rocket science. Of course, you do lose the syntactic sugar of [] dereferencing so it looks like part of the language, but life is full of tradeoffs! :-) And I suppose there is still a way to break it, if you extend Object.prototype with something that happens to match Hash.prefix. I guess you could use hasOwnPrototype to prevent this. Ah well, maybe I do have a bug after all, but at least it's a good start.
- anonymous 14y agoHere, let me make this easier for you > var hash = [] undefined > hash [] > hash['a'] undefined > hash['a'] = 33 33 > hash [ a: 33 ] > hash[3] = 3 3 > hash [ , , , 3, a: 33 ] > hash[2] undefined > hash[hash] undefined > hash[hash] = hash [ , , , 3, a: 33, ',,,3': [Circular] ] > hash[0] = 'zero' 'zero' > hash['0'] 'zero' JavaScript already has a perfectly well functioning associative array that can store whatever keys you like with the only gotcha being that strings containing only numbers act like numbers, when used as keys.
- 14y ago
- mercurial 14y agoIt's a pretty good illustration of why associative arrays in Javascript are a design failure, by conflating different notions in the same implementations: - objects are not hashes - hashes are not arrays It sounds like somebody reading the classic quote by Saint Exupéry, "A designer knows he has achieved perfection not when there is nothing left to add, but when there is nothing left to take away.", and concluding: "Well, if I mash all the elements to a paste, there won't be anything to take away."
- cousin_it 14y agoif (!posts.hasOwnProperty(req.body.slug)) { posts[req.body.slug] = new Post(req.body); res.send(200); } Maybe I'm missing something, but doesn't that code also happily assign to properties like "constructor", not just "hasOwnProperty"?
- deleted 14y ago[deleted]