Javascript defaultdict without library - javascript

I'd like something like Python's defaultdict in Javascript, except without using any libraries. I realize this won't exist in pure Javascript. However, is there a way to define such a type in a reasonable amount of code (not just copying-and-pasting some large library into my source file) that won't hit undesirable corner cases later?
I want to be able to write the following code:
var m = defaultdict(function() { return [] });
m["asdf"].push(0);
m["qwer"].push("foo");
Object.keys(m).forEach(function(value, key) {
// Should give me "asdf" -> [0] and "qwer" -> ["foo"]
});
I need this to work on recent versions of Firefox, Chrome, Safari, and ideally Edge.
Again, I do not want to use a library if at all possible. I want a way to do this in a way that minimizes dependencies.
Reasons why previous answers don't work:
This answer uses a library, so it fails my initial criteria. Also, the defaultdict it provides doesn't actually behave like a Javascript object. I'm not looking to write Python in Javascript, I'm looking to make my Javascript code less painful.
This answer suggests defining get. But you can't use this to define a defaultdict over collection types (e.g. a map of lists). And I don't think the approach will work with Object.keys either.
This answer mentions Proxy, but it's not obvious to me how many methods you have to implement to avoid having holes that would lead to bad corner cases later. Writing all of the Proxy methods certainly seems like a pain, but if you skip any methods you might cause painful bugs for yourself down the road if you try to use something you didn't implement a handler for. (Bonus question: What is the minimal set of Proxy methods you'd need to implement to avoid any such holes?) On the other hand, the suggested getter approach doesn't follow standard object syntax, and you also can't do things like Object.keys.

You really seem to be looking for a proxy. It is available in the modern browsers you mention, is not a library, and is the only technology allowing you to keep the standard object syntax. Using a proxy is actually quite simple, all you need to overwrite is the get trap that should automatically create non-existing properties:
function defaultDict(createValue) {
return new Proxy(Object.create(null), {
get(storage, property) {
if (!(property in storage))
storage[property] = createValue(property);
return storage[property];
}
});
}
var m = defaultDict(function() { return [] });
m["asdf"].push(0);
m["qwer"].push("foo");
Object.keys(m).forEach(console.log);

Related

Javascript - Create Own Condition/Control Flow

is it possible to create your own condition/control flow syntax in js? for example:
when( condition ) {
// execute code
}
this would add some sort of listener to a variable/object and execute whenever the condition is true.
I may just have to create my own programming language.
This is actually two questions:
Can syntax be added to JavaScript directly?
Can I set up code that runs when a condition changes?
The answer to the first is no. You can use preprocessing like sweet.js macros to accomplish that but its non-trivial.
The answer to 2 is yes, you can accomplish this in any ES 5 compliant environment (IE 9+):
var condition = {val: null};
Object.defineProperty(condition, "isTrue", {
set: function(val) {
if (val && !this.val) {
runSomeCodeYouWantRun();
}
this.val = val;
},
get: function() {
return this.val;
}
});
So whenever any code changes condition.isTrue the specified code will be run if the change is truthy. But for situations like this I prefer less ad-hoc approach. ES 2015 Proxy traps make this much cleaner (IMHO) but support isn't quite there yet. What you really are looking for here to get the job done today is an Observable.
Also note that writing a language to solve a problem like this is roughly equivalent to building a car out of spare parts to drive to the store for groceries. Just buy a car.
Welcome to the wonderful world of JavaScript transpilers.
JavaScript in and of itself does not have any utilities for creating your own syntax. In response to this, many tools exist online and in the NPM repositories that add just this sort of feature to JS by translating it to browser-compatible JavaScript. Here's a small sampling:
Babel.js adds ES6 syntax to ES5 JavaScript
Browserify adds Node.JS's require() functionality
Uglify.JS compresses JS into the smallest form possible that will still execute the same way
TypeScript (while more technically its own language) adds static type-checking to JavaScript
All of these transpilers, however different, all work the same way: they parse the source file(s) to an abstract syntax tree (or AST), run some transformations on that tree, and then spit out the resulting JavaScript file. If you wanted to create your own special syntax in JavaScript, you would do more or less the same thing. (Uglify.JS stands out as being particularly customizable in this regard).
All of that said, none of these transpilers fundamentally change the way that JavaScript works - you still have to translate whatever fancy syntax you want to use into plain, browser-executable JavaScript. This means that although you will be able to write your when(){} block syntax, the expression inside the parentheses cannot be a simple Boolean expression, but must involve something like an Observable object that inserts a callback (Knockout.JS does have a tool for doing this out of an expression built from its Observables).

Implementing prototype method

If I implemented a method x on a String like :
String.prototype.x = function (a) {...}
And then the new version of javascript actually implements the x method, but on the another way, either returning something different than my implementation or function with more/less arguments than my implementation. Will this break my implementation and override it?
You'll overwrite the default implementation.
Any code that uses it will use yours instead.
There was a proposal for scoped extension methods and it was rejected because it was too expensive computationally to implement in JS engines. There is talk about a new proposal (protocols) to address the issue. ES6 symbols will also give you a way around that (but with ugly syntax).
However, that's not the punch - here's a fun fact no one is going to tell you.
No one is ever going to implement a method called x on String.prototype
You can implement it and get away with it. Seriously, prollyfilling and polyfilling is a viable, expressive and interesting solution to many use cases. If you're not writing a library I think it's acceptable.
No, you'll be overriding the default implementation of said function, from the point at which you've declared/defined it. The "new" implementation will function in its native behavior, until your implementation's defined.
var foo = 'some arbitrary string';
console.log(foo.indexOf('s')); // logs [0]
String.prototype.indexOf = function(foo, bar) { return 'foo'; };
console.log(foo.indexOf()); // logs [foo]
Illustration: http://jsfiddle.net/Z4Fq9/
Your code will be overriding the default implementation.
However if the interface of your method is not compatible with the standard one the libraries you may use could depend on the standard behavior so the program as a whole could break anyway with newer versions of the libraries.
In general is a bad idea doing something that could break if others do the same: what if another library thinks it's a good idea to add a method x to the standard string object prototype? Trying to avoid conflicts is a must for libraries but it's also good for applications (and if an application is written nicely then a lot of its code is probably quite similar to a library, and may evolve in a library later).
This kind of "patching" makes sense only to provide the a standard method for broken or old javascript implementations where that method is absent. Patching standard prototypes just because you can is a bad idea and will make your code a bad neighbor with which is difficult to share a page.
If the implementation of x is from a new version of Javascript, it's part of the core, therefore when you write String.prototype.x... it will be already there, and you will overwrite it.
Best practice in this kind of things is to write
if( !String.prototype.x ){
String.prototype.x = function ...
//your

Chrome performance: "standard" property names vs. non-standard

So this is an interesting one... While I was testing the performance of setAttribute vs. normal property set on an element, I found an odd behavior, which I then tested on regular objects and... It's still odd!
So if you have an object A = {},
and you set its property like A['abc_def'] = 1, or A.abc_def = 1, they are basically the same.
But then if you do A['abc-def'] = 1 or A['123-def'] = 1 then you are in trouble. It goes wayyy slower.
I set up a test here: http://jsfiddle.net/naPYL/1/. They all work the same on all browsers except chrome.
The funny thing is that for "abc_def" property, chrome is actually much faster than Firefox and IE, as I expected. But for "abc-def" it's at least twice as slow.
So what happens here basically (at least from my tests) is that when using "correct" syntax for properties (legal C syntax, which you can use with dot properties) - It's fast, but when you use syntax that requires using brackets (a[...]) then you're in trouble.
I tried to imagine what implementation detail would distinguish in such a way between the two modes, and couldn't. Because as I think of it, if you do support those non-standard names, you are probably translating all names to the same mechanics, and the rest is just syntax which is compiled into that mechanic. So . syntax and [] should be all the same after compilation. But obviously something is going the other way around here...
Without looking at V8's source code, could anyone think of a really satisfying answer? (Think of it as an exercise :-))
Here's also a quick jsperf.com example
Thanks to NDM for the jsperf example!
Edit:
To clarify, of course I want also a concrete answer from the real code
(which I already found) or to be more precise - the reason behind that
specific implementation. That is one of the reasons I asked you to
look at it "as an exercise", to look behind the technical
implementation and try to find the reason.
But I also wanted to see how other people's minds work in cases like these.
This may sound "vague" to some of you - but it is very useful to try and think
like other people from time to time, or take their point of view. It
enhances your own ways of thinking.
So JS objects can be used for two conflicting purposes. They can be used as objects but they can be used as hash tables too. However what is fast and makes sense
for objects is not so for hash tables, so V8 tries to guess what a given object is.
Some signs the user can give that he wants a dictionary are deleting a property or giving a property a name that cannot be accessed using dot notation.
Some other heuristics are also used, I have made a gist https://gist.github.com/petkaantonov/6327915.
There is however a really cool hack that redempts an object from hash table hell:
function ensureFastProperties(obj) {
function f() {}
f.prototype = obj;
return obj;
}
See it in action: http://jsperf.com/property-dash-parformance/2.
The redempted object is not as fast as the original because the properties are stored in the external properties array rather than in-object. But that's still far better than hash table. Note that this is still pretty broken benchmark, do not think for a second that hash tables are only 2x slower than inobject properties.

How can I prove that my JavaScript files are in the scope of a specific JS or ECMA version?

Let's say you would get a bunch of .js files and now it is your job to sort them into groups like:
requires at least JavaScript 1.85
requires at least E4X (ECMAScript 4 EX)
requires at least ECMAScript 5
or something like this.
I am interested in any solution, but especially in those which work using JavaScript or PHP. This is used for creation of automated specifications, but it shouldn't matter - this is a nice task which should be easy to solve - however, I have no idea how and it is not easy for me. So, if this is easy to you, please share any hints.
I would expect something like this - http://kangax.github.com/es5-compat-table/# - just not for browsers, rather for a given file to be checked against different implementations of JavaScript.
My guess is, that each version must have some specifics, which can be tested for. However, all I can find is stuff about "what version does this browser support".
PS: Don't take "now it is your job" literally, I used it to demonstrate the task, not to imply that I expect work done for me; while in the progress of solving this, it would be just nice to have some help or direction.
EDIT: I took the easy way out, by recquiring ECMAScript 5 to be supported at least as good as by the current FireFox for my projekt to work as intendet and expected.
However, I am still intereseted in any solution-attemps or at least an definite answer of "is possible(, with XY)" or "is not possible, because ..."; XY can be just some Keyword, like FrameworkXY or DesignPatternXY or whatever or a more detailed solution of course.
Essentially you are looking to find the minimum requirements for some javascript file. I'd say that isn't possible until run time. JavaScript is a dynamic language. As such you don't have compile time errors. As a result, you can't tell until you are within some closure that something doesn't work, and even then it would be misleading. Your dependencies could in fact fix many compatibility issues.
Example:
JS File A uses some ES5 feature
JS File B provides a shim for ES5 deficient browsers or at least mimics it in some way.
JS File A and B are always loaded together, but independently A looks like it won't work.
Example2:
Object.create is what you want to test
Some guy named Crockford adds create to Object.prototype
Object.create now works in less compatible browsers, and nothing is broken.
Solution 1:
Build or find a dependency map. You definitely already have a dependency map, either explicitly or you could generate it by iterating over you HTML files.
Run all relevant code paths in environments with decreasing functionality (eg: ES5, then E4X, then JS 1.x, and so forth).
Once a bundle of JS files fail for some code path you know their minimum requirement.
Perhaps you could iterate over the public functions in your objects and use dependency injection to fill in constructors and methods. This sounds really hard though.
Solution 2:
Use webdriver to visit your pages in various environments.
Map window.onerror to a function that tells you if your current page broke while performing some actions.
On error you will know that there is a problem with the bundle on the current page so save that data.
Both these solutions assume that you always write perfect JS that never has errors, which is something you should strive for but isn't realistic. This might; however, provide you with some basic "smoke testing" though.
This is not possible in an exact way, and it also is not a great way of looking at things for this type of issue.
Why its not possible
Javascript doesn't have static typing. But properties are determined by the prototype chain. This means that for any piece of code you would have to infer the type of an object and check along the prototype chain before determining what function would be called for a function call.
You would for instance, have to be able to tell that $(x).bind() o $(x).map are not making calls to the ecmascript5 map or bind functions, but the jQuery ones. This means that you would really have to parse out the whole code and make inferences on type. If you didn't have the whole code base this would be impossible. If you had a function that took an object and you called bind, you would have no idea if that was supposed to be Function.prototype.bind or jQuery.bind because thats not decided till runtime. In fact its possible (though not good coding practice) that it could be both, and that what is run depends on the input to a function, or even depends on user input. So you might be able to make a guess about this, but you couldn't do it exactly.
Making all of this even more impossible, the eval function combined with the ability to get user input or ajax data means that you don't even know what types some objects are or could be, even leaving aside the issue that eval could attempt to run code that meets any specification.
Here's an example of a piece of code that you couldn't parse
var userInput = $("#input").val();
var objectThatCouldBeAnything = eval(userInput);
object.map(function(x){
return !!x;
});
There's no way to tell if this code is parsing a jQuery object in the eval and running jQuery.map or producing an array and running Array.prototype.map. And thats the strength and weakness of a dynamically typed language like javascript. It provides tremendous flexibility, but limits what you can tell about the code before run time.
Why its not a good strategy
ECMAScript specifications are a standard, but in practice they are never implemented perfectly or consistently. Different environments implement different parts of the standard. Having a "ECMAScript5" piece of code does not guarantee that any particular browser will implement all of its properties perfectly. You really have to determine that on a property by property basis.
What you're much better off doing is finding a list of functions or properties that are used by the code. You can then compare that against the supported properties for a particular environment.
This is still a difficult to impossible problem for the reasons mentioned above, but its at least a useful one. And you could gain value doing this even using a loose approximation (assuming that bind actually is ecmascript5 unless its on a $() wrap. Thats not going to be perfect, but still might be useful).
Trying to figure out a standard thats implemented just isn't practical in terms of helping you decide whether to use it in a particular environment. Its much better to know what functions or properties its using so that you can compare that to the environment and add polyfills if necessary.

Extending native elements in JavaScript via prototype?

Would you consider extending the native elements via the prototype dangerous? I see some frameworks such as Prototype doing this so I have started to wonder if I dare to do that too.
I am worried about implementing things like addClassName and make it collide in the future in a way that I can't resolve any other way than rewriting the software and asking module authors to do the same.
I wouldn't because IMHO it might definitely make collisions soon or later and create a potential bug very difficult to be spot out.
I anyway do extend some basic simple native Javascript objects like String.trim, I'm anyway careful to always test to see if it already exists by using a simple if test:
if(!String.prototype.trim)
String.prototype.trim = function() { return this.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); }
You could do the same with addClassName.
The difference is that doing it with simple function like String.trim, it's difficult that might lead to problems in future, because even if a browser engine has got String.trim (actually FF has it) well such a function is going exactly to do what my String.trim does, so you won't see differences in your web application workflow ever.
A more complex function like overriding querySelectorAll might lead to differences between how the browser implements it and your implementation. For example: the order of the returned elements might be different, the browser function returns a collection while your one an array, and other issues. So when you run your webapp on browser that does implement the querySelectorAll it might lead to having your webapp not working anymore as expected, and there try finding out the bug!!!
Maybe querySelectorAll is not the best example, but I hope I explained the concept.

Categories