Attila Herbert Attila Herbert - 3 months ago 41
Javascript Question

Are function-arguments not necessarily objects?

I'm learning functional programming and node.js, and I came across this odd problem when using

Function.prototype.apply
and
.bind
.

function Spy(target, method) {
var obj = {count: 0};
var original = target[method]
target[method] = function (){//no specified arguments
obj.count++
original.apply(this, arguments)//only arguments property passed
}
return obj;
}
module.exports = Spy


This code works, it successfully spies on
target.method
.




//same code here
target[method] = function (args){//args specified
obj.count++
original.apply(this, args)//and passed here
}
//same code here


This code, however, does not. It gives an error message:
TypeError: CreateListFromArrayLike called on non-object
.




And then the biggest surprise is, this method works perfectly fine.

//same code here
target[method] = function (args){
obj.count++
original.bind(this, args)
}
//same code here


So why exactly do I get this error? Is it because function arguments are not necessarily objects? Or is it because apply has a stricter description than bind?

Answer

In this version:

target[method] = function (args){//args specified
        obj.count++
        original.apply(this, args)//and passed here
    }

Here you are not taking all the arguments but just one, named args. Since apply expects an array like object you cannot use args since it is only the first argument passed to the original target.

You can change it to:

target[method] = function (arg){   //only one argument specified
        obj.count++
        original.apply(this,[arg]) //one argument passed here
}

Now it works, but you can only spy on one argument functions. Using call would be better since you only have one extra argument:

target[method] = function (arg){ //only one argument specified
        obj.count++
        original.call(this,arg)  //one argument passed here
}

Now bind is a totally different animal. It curries functions, thus return functions. Imagine you need to send a callback that takes no arguments but calls a function with some arguments you have when making it. You see code like:

var self = this;
return function() {
  self.method(a, b);
}

Well. bind does this for you:

return this.method.bind(this, a, b);

When calling either of these returned functions the same happens. The method method is called with the arguments a and b. So calling bind on a function returns a curried version of that function and does not call it like call or apply does.

Comments