An Object is not a Hash
devthought.com
devthought.com
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.
Can you explain the integer math thing? What exactly can't it do?
$ 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
trueI'm curious as to why this discounts a tool meant for performing IO and various web development tasks in general.
Has anyone run into a integer math related issue when using node (that was a deal-breaker)?
[0] http://stackoverflow.com/questions/8663298/json-transfer-of-...
[1] http://stackoverflow.com/questions/6320908/twitter-json-in-r...
[2] http://stackoverflow.com/questions/209869/what-is-the-accept...
If you're dancing on the edge of the limits of numerical representation then you need to write code to protect against bad things. If you don't write said code, your program is going to fail to work correctly, no matter what language you use.
>>> int('0x7fffffff', 16)
2147483647
>>> int('0x7fffffff', 16) + 1
2147483648LA couple weeks ago I was writing a php script, and it actually felt awkward to query a database and then do something "immediately" after with the result.
It isn't terrible, it's just different. And if you aren't used to it, it'll feel weird at first. This goes away with exposure and experience.
My main takeaway is, unless I absolutely have to use async code I won't. It's almost never cleaner than queuing onto a thread-pool.
In the case of highly parallel IO, yes, a thousand times yes async is great. But the great lie about node is that people need to carry over the async from the IO layer to the app logic layer. In node it's all just mashed together.
For instance, the problem a websocket server is trying to solve is multiplexing M connections onto N available cores. Async works great for the connections layer, and a thread-pool well for the logic layer.
The trick about async is that your server can handle 10,000 connections that are idle, but only a handful active at a given time since you only have a few cores. Thread scheduling works just fine.
None of these ideas are novel, in fact, they are decades old. The real problem is the faddishness of ideas.
I've used Node.js to good effect. It's a nice piece of software for a whole lot of reasons, but I think Go proves that there is a better way: http://golang.org/doc/effective_go.html#goroutines
There are plenty of reasons to use JS, like "I already know it" if you're a front-end dev, "It's our only option on the client-side and using the same language everywhere is a good idea", or "V8 is heavily optimized so I can not care about performance and usually my code will run fast enough."
Actually that last one is nice. If you don't do anything too stupid, and break up work into lots of little pieces that are called exactly when they're needed, and share as little state as possible, your code runs /much/ faster and is much easier to reason about. node.js gives you those last two properties by default.
Perhaps "JS is inordinately popular for how bad of a language it is", and "Only erlang has the same concurrency model" are related. You can write something that runs as fast in another language or environment, but you won't be able to write it nearly as easily.
They have nearly nothing in common.
JS uses callback-based stuff, cooperative scheduling, and has a single process to drive everything.
Erlang has message-passing to mailboxes, preemptive scheduling, and many fully isolated processes.
Cooperative vs. preemptive scheduling is only about whether you trust the coroutines to do the right thing. Erlang cares about recovering from errors so it must be preemptive. Node.js trusts the programmer so it uses a much simpler cooperative scheduler. This does not change the concurrency model though, only the assumptions of the environment.
Erlang is (in my opinion at least) better than node.js, and also has the advantage being able to enforce no-shared-memory between actors, so it automatically distributes erlang processes across all your cpu cores. Node.js requires that you spawn child processes yourself if you wish to take advantage of multiple cores, but ones they're spawned the way you handle concurrency doesn't change.
I don't know about you, but mailboxes and callbacks look pretty close to the same thing to me. In both, your code waits for an event to happen and then reacts to that event, potentially sending off more events. In both, once you do anything that would require blocking, your coroutine lets others run while it's waiting.
The erlang model certainly has more power but they're more similar to each other than they are to anything else. The way you think is the same, although the way you write it might be a little different.
For example, an overloaded Erlang node will favour the processes that are being swamped over the other ones in an attempt to try and rebalance things. This is especially efficient during short overload bursts. Cooperative scheduling cannot explicitly do the same, or give any indication of how frequently or how much work you want to let a work unit do.
Regarding mailboxes and continuations through callbacks, not exactly. One difference is that Erlang has selective receives, whereas callbacks will be handled no matter what. This means that in Erlang, I can choose to only care about a subset of the possible events and leave the rest for later, waiting and blocking my execution until I get the right circumstances. In callback-based code, I have to think of all possibilities because callbacks can't block.
This is to say the event matrix of callback-based code will need to consider all options, while Erlang's will only need to handle a restrictive subset of them. Ulf Wiger gave a full talk on it, which is summarized here: http://dm3.github.com/2010/08/01/death-by-accidental-complex...
This also impacts how easy to maintain code, reason about it, model it, etc. You don't have to think the same because you don't have to consider nearly as many possibilities, event interleavings, or worrying about blocking stuff and killing your application (an irresponsive app is as good as dead) because of it.
And I'm not even getting into the need of callback-based code to break everything into continuations.
Let us not single out JS specifically as a 'special kind of delusion'. We've all seen 'delusion' in every, single, programming, language, flamewar, on the internet. Seriously.
Everything is a tool, and you choose the right tool for the job. JS adds to the discussion.
If your trying to push JS as a tool to rule them all, that is, thee tool that _must_ do it all well, that is delusional. You'll be prematurely optimizing while(true).
Your not saying that? Then why critique it as if you were?
* while there are blocking calls in node, they are by far the exception. It is not multi-paradigm like Java
edit: To expand, the alternative is to fire off threads for each connection so you can write 'synchronous' code... the whole point of nginx and node is so that you don't need the overhead of creating a separate thread for each connection, asynchronous code is the natural result of this decision.
JS, like most languages, is good at certain things, not at others. The edge cases are unfortunate but functionality can be augmented by libraries. Given the momentum behind JavaScript it's fairly likely that these edge cases will disappear in the long term.
By the time it happens, it won't actually mean anything because something else, something equally despised and impure will have taken its place.
Perhaps a better question might be why are such languages more popular than their more ideologically pure cousins? What need are they filling that "better" languages cannot? Be careful if your answer is "they're beginner's tools for mediocre programmers" -- the JS community is home to some extremely talented programmers who are all there by choice alone.
The JS community is not an argument for JS I feel, because it does not have a track record of providing reliable software. Most libraries are new and a lot of devs are very lax with security or code robustness. If you use a library/tool you're not sure what you're getting - see the semicolon situation or the npm directory deletion.
This scope trick (and really, the whole JS language situation) always reminded me of the "closures vs. objects" koan:
http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/m...
var mariner= {};
mariner.new = function(){
var self = {};
var maxhp = 200;
var hp = maxhp;
self.heal = function(deltahp) {
if (hp + deltahp < maxhp){
hp = hp + deltahp;
} else {
hp = maxhp;
}
}
self.sethp = function(newhp) {
if (newhp < maxhp){
hp = newhp;
} else {
hp = maxhp;
}
}
self.gethp = function(){
return hp;
}
return self;
}
var m1 = mariner.new();
m1.sethp(100);
m1.heal(13);
alert(m1.gethp());
(shamelessly stolen from http://lua-users.org/wiki/ObjectOrientationClosureApproach)That has a pretty simple answer, it's because it was the only language available in the browser (and no, Java applets never really took off).
Had it been Ruby (unlikely given the timeline) or Python or even Perl instead, the situation would probably be much better today.
_.has(object, key)
or _(object).has(key)
http://underscorejs.org/#has- 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."
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?
Having said that, use underscore for the safe references.
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. var x = 'foo'
{x: 1}
Also, where have you seen that class pattern so much? That seems very odd. I almost always find dicts, sometimes __slots__ classes for CPython performance. 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.
> 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. > var hash = [];
undefined
> hash
[]
> hash['length']
0
> // Hey! That should have been undefined, not 0!
> hash['length'] = 'test'
RangeError: Invalid array length
> // And I can't set it either!
Also, the part where you're doing hash[hash] doesn't do at all what you think. When you write this code: hash[hash] = whatever;
What you're really doing is: hash[ hash.toString() ] = whatever;
And with hash being an array, hash.toString() is highly browser dependent. In Chrome, if you don't have any numeric array keys, then hash.toString() is "" regardless of your non-numeric keys (at least in the version I'm testing). So you may well be really doing this in Chrome: hash[""] = whatever; // Oops
If you do want to use a native JavaScript type as a hash, the one to use is Object, not Array. Simply change the first line of your test to: var hash = {};
and go from there.This still doesn't solve the name collisions, but is plenty useful anyway.
If you do want a more fool-proof hash that lets you use any string as a key without fear, I'm pretty sure you need to write some code like the Hash class I posted. You still couldn't do the hash[hash] - or hash.get(hash) - because of the dependence on .toString(), but any string key would work. You could even get fancy and give the Hash class some kind of useful .toString() method so you could use hash[hash] - having your own code here opens up those kinds of possibilities.
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.
object.set(key, value)
vs
object.key = value
if (!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"?This will virtually never happen. If you're worried about it, put an early return (if (name === 'hasOwnProperty') return). End of story.
Allow what you want to allow and disallow everything else.
This is like saying "oh, but nobody will ever insert SQL in our forms!"
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.
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.