Neuron Workout Solutions #1
As promised, here comes the first edition of Neuron Workout Solutions™, a series that answers the questions and challenges at the end of many chapters in the book. I did not start earlier in order not to spoil the challenge for recent readers, but I think now the time comes.
So here we start, with the first chapter featuring Neuron Workouts: chapter 4, “Regular JavaScript on Steroids,” that discusses how Prototype augments native JavaScript objects such as strings, numbers and functions.
What’s the technical reason a for...in loop on an array will go berserk when you’re using Prototype?
That’s because for…in is designed to iterate through all of the object’s properties, and Prototype augments these properties quite a bit.
Specifically, for…in is not intended for array iteration, but because Array’s native properties and methods (such as length, indexOfand slice, to name a few) are all marked internally as non-enumerable, they do not show up in a for…in loop. Alas, we JavaScript authors do not have a mechanism to mark our own additions as non-enumerable: whenever we augment a native type’s prototype to improve this type, our extensions will show up in for…in loops on any instance of this type.
1 2 3 4 5 6 7 8 | var data = ['hello', 'world']; // WRONG: for…in is not intended for array iteration for (var item in data) ... // item will be 0, 1, then any Prototype extension, e.g. each, eachSlice… // RIGHT: proper manual loop iteration for (var index = 0, len = data.length; index < len; ++index) ... // index will only be 0 then 1. |
Do note that on Arrays, each relies on this latter, proper integer-indexed iteration.
What happens to this when you attempt to bind an already-bound function?
Ha! Two answers to chose from: does it go with the latest binding attempt, or does it obey the original binding?
Well, let’s try:
1 2 3 4 5 6 7 8 9 | var obj1 = { name: 'John', sayName: function() { return this.name; } }; var obj2 = { name: 'Jim' }; var fx1 = obj1.sayName.bind(obj1); var fx2 = fx1.bind(obj2); fx1(); // => 'John' fx2(); // => 'John' - Ah ha! |
The original binding is preserved. How come? Well, a bound function essentially says: “whatever the context from now on, I’ll be bound to object A.” So a further attempt at binding is just an attempt at changing the context, but the original function does not care anymore: it has already decided which object to use as this. Indeed, if we take a glimpse at the code behing Function.prototype.bind, here’s what we find:
207 208 209 210 | var __method = this, args = $A(arguments), object = args.shift(); return function() { return __method.apply(object, args.concat($A(arguments))); } |
Note how we grab the scope object (here named object) from the arguments, and return a function that calls apply with it. What is, therefore, a doubly-bound function? Just a bound call to this anonymous function, which however does not use this: it is unaffected by the binding, and retains its original object value.
Takeaway point: a binding is final. Attempting to re-bind a bound function has no effect.
When should you use curry() instead of bind()?
When you don’t need to bind: you just need to pre-fill arguments (what is technically referred to as partial application).
1 2 3 4 5 6 7 8 9 10 11 | function getFullName(lastName, firstName) { return (firstName + ' ' + lastName).strip(); } // WRONG: you don't need binding here! var gF2 = getFullName.bind(null, 'Smith'); // RIGHT: you're just pre-filling var getSmithie = getFullName.curry('Smith'); getSmithie('Mary'); // => 'Mary Smith' |
When, at the earliest, will a delay()ed function run? Is this also true of defer()red calls? Why?
When the current function completes. That is because delay() is based on a setTimeout call, and all JavaScript implementations in the dominant browsers are single-threaded, so functions cannot execute in parallel, including timeouts.
And yes, this is also true of defer, because defer is just a curried version of delay (with a 1/100th of a second as delay). So yes, in essence, deferring a function just means “execute this code block once the current function completes, or as soon as possible after that.”
What situations (outside of debugging) can you think of that would find argumentNames() very useful?
Essentially anything AOP-like. Aspect-Oriented Programming lets you add custom functionality before, after or around a series of methods, based on whatever criteria you want. In this context, you could very well dynamically add behavior based on the names of arguments.
For instance, when a class is created based on another one (simulating inheritance), Prototype looks for methods whose first argument is named $super and makes sure they get invoked with this first argument set to the inherited version of the method. This is achieved through wrapping, using Prototype’s wrap extension to functions.
As an exercise (admittedly a fairly advanced one), say you want to implement some weird version of “varargs” so that when your method has a final argument called “anythingElse”, it gets passed an array of the remaining actual arguments passed. For instance, your original method would look like:
var obj = { // ... someMethod: function(a, b, anythingElse) { alert([a, b, anythingElse].inspect()); } // ... };
After having gone through your system, the following call should work:
obj.someMethod(1, 2, 3, 4, 5) // => Alerts "[1, 2, [3, 4, 5]]", as arguments 3 to 5 were grabbed into the anythingElse array.
I’ll post a solution to this in a short while.
Why can we write Math.PI.toFixed(4) but must add parentheses in (240).toColorPart()?
Because lexical analyzers for JavaScript are slightly dumb, and when they see a period after an integer, they go for floating-point syntax, not for method invocation (contrary to, say, Ruby). “Math.PI” is a regular variable, so there’s no problem with adding “.toFixed“. But “240″ is an integer, and adding a period triggers floating-point syntax, not method syntax.
When we surround the integer with parenthese, we use the shortest syntax disambiguisation to be done with number parsing, and giving the period character its “member access” meaning back, which then lets us invoke a method on it.
After a call to stripTags(), do we still have the contents of <script>s in the string?
Yes. stripTags removes the tags themselves, not their contents (otherwise you’d probably end up with an empty string). Do not mistake it with stripScripts, which leaves most tags alone, but strips out <script>s and their contents.
What happens if we pass sub() a RegExp with a global flag (for example, /test/g)?
It depends on what the regex is, and what the text you’re calling sub on is. But often you’ll end up with an infinite loop, so you should never, never use a global-flag regex with sub. If you’re so keen on learning why, I suggest you go dive into the source code for sub and try to figure it out in the comments: I’d love to see some braing cranking going on there, and I’ll be watching and guiding.
How could we use without() to strip from an array all the values from another one without resorting to a form of loop?
The thing is, without takes individual arguments, and what you have here is an Array. What mechanism do we know that lets us treat an array of arguments as individually-passed arguments? JavaScript’s native apply method on functions! So here you go:
var words = $w('there shalt be no old-fashion word in thy speech, o thou treacherous harbinger'); var oldies = $w('shalt thy thou'); var alright = words.without.apply(words, oldies);
And there you go, no extra looping!
Well, that’s about it for this time, folks!
I’ll look forward to any questions, comments or wild guesses you may have. Stay tuned for a new edition!
1 Comment so far
Leave a reply



[...] in the first Neuron Workout solutions, I suggested an exercise for implementing varargs-like functionality using two of Prototype’s [...]