2

I am reading through John Resig's Secrets of Javascript ninja and was trying out one of the examples on currying and parital functions. The code is as follows:

<html>
<body>
<button id="test">Click Me!</button>
</body>


<script type="text/javascript">
    Function.prototype.curry = function() {
        var fn = this,
        args = Array.prototype.slice.call(arguments);

       return function() {
           return fn.apply(this, args.concat(
               Array.prototype.slice.call(arguments)));
           }; 
       };


    var elem = document.getElementById("test"); 
    var bindClick = elem.addEventListener.curry("click");
    bindClick(function(){   console.log("OK"); });
</script>
</html>

However, the following code seems to generate an error Uncaught TypeError: Illegal invocation on the apply function.

I cant seem to figure out the reason as it all seems to make sense. bindClick will return an anonymous function that calls the function elem.addEventListener with window as the function context (this) and the arguments will be ["click", function() {console.log("OK"); }]

2
  • Actually, this is not currying/schönfinkeling but partial application Commented Mar 22, 2013 at 9:54
  • hmm. I read up a bit on the difference.. But am still a bit confused. Partial Application returns a function which acts like the function you pass in but which takes fewer arguments, the others having been "bound in". So, this is indeed partial application. However I don't understand why is it not currying? It seems like John Resig in "Secrets of JS Ninja" and Douglas Crawford in "JS: The Good parts" define the same function and named it "curry".. Commented Mar 22, 2013 at 10:15

1 Answer 1

3

The problem is that you've lost the context of the element. The addEventListener method has to be called on an element, but you're calling it on a function:

// Here, `this` refers to a function, not an element
return fn.apply(this, args.concat(Array.prototype.slice.call(arguments)));

You would need to pass in the element to your new function. For example:

Function.prototype.curry = function () {
    var fn = this,
    args = Array.prototype.slice.call(arguments);

     return function (context) {
         return fn.apply(
             context,
             args.concat(Array.prototype.slice.call(arguments, 1))
         );
     }; 
};

Here's a working example. Notice the addition of a context argument to the returned function, and also notice the addition of the second argument to the slice call - that's needed to remove the new context argument and only apply any following arguments.

Sign up to request clarification or add additional context in comments.

4 Comments

Oh okie. Thanks.. :) Just a clarification though, you mentioned that the this refers to a function. I guess that is because it is invoked by the fn method? Thus, in this cause, since fn refers to the addEventListener function, this would refer to the addEventListener method even though the anonymous function's this (invoked by bindClick() function) refers to the window object?
@henrybai - Correct. But you need this to refer to an element, otherwise addEventListener won't have anything to actually apply to.
@henrybai: Yes, in curry the this refers to the addEventListener function - and is assigned to fn because we won't be able to use this in the anonymous function any more. Btw, it would be easier to use bind: var bindClick = elem.addEventListener.bind(elem, "click");
@JamesAllardice @Bergi It seems like the this does refer window object as I did in my experiment here. Am I doing something wrong? jsfiddle.net/ya2hM

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.