Notes of Maks Nemisj

Experiments with JavaScript

Some interesting aspects of “call” method in JavaScript

Hi all, today is another javascript experiment where I use Array’s iteration methods forEach and map to look how call function is implemented and how we can use call to implement something ‘not-standard’.

You know often I use forEach or map on an array to execute one method of the instances in this array, e.g.,

var json = arr.map(function (obj) {
    return obj.serialize();
});

Today I thought: “hey, but actually, that would be cool to execute "serialize" method not directly in a anonymous function, but pass it as a parameter to the map method in a more declarative style”. Something like this one:

// arr is an instance of Array field with Zork's
var json = arr.map(obj.serialize);

To say frankly, one of the reasons why I love javascript so much, is because there is always a way to implement something in an obscure manner and I think this case is not any different.

So, let’s start our small journey and imagine that objects in the arr variable are of the type Zork.

When I first thought about how would I achieve this, my first thought was: “Ok, that’s nice, but where do I take the reference to the serialize method from if it’s on every obj, which is available at the moment of iteration? But it’s avaliable also on the prototype of the obj instance, on the prototype of the Zork“, pseudo-code:

var json = arr.map( Zork.prototype.serialize );

But, we all know that it wont work, since map will pass instance of every object as a first argument and scope of serialize method would be wrong.

Luckily, javascript has the ability to change scope of any function/method to the desired one. This can be achieved by using call or apply method which is a member of any normal function in javascript.

Call” method receives scope as the first argument, this means that passing any Zork instance to the call will change scope and execute serialize with a correct scope. Here the example code:

var json = arr.map(function (obj) {
    return Zork.prototype.serialize.call(obj);
});

Check, that’s exactly what I need. So, theoretically this means, that I can also pass “call” function directly to the map

var json = arr.map( Zork.prototype.serialize.call );

When I tried to execute the above function Firfox gave me an error saying:

TypeError: Function.prototype.call called on incompatible undefined

This is not very explanatory message, but after making a couple of tests the answer was a bit shocking for me. I will show you this tests later on, but first my conclusion: “It appears that call function is using the same context resolution principles as for normal “method” calls, but instead call uses its ‘this’ variable for resolving referenced function and not associated object.”

Before I continue let’s remain the basics how javascript resolves this variable for functions.

Function can be called at least in five different ways: “invoke as method”, “invoke as baseless function”, “invoke using “Function.protoype.call”, “function.prototype.apply” and “invoke a constructor using new”. Currently we are interested in two of them: “invoke as method” and “invoke as baseless function”.

When we are caching method and invoking it separatly we are dealing with “invoke as baseless function”. At this moment, execution context ( with other words this variable ) is resolved to the global. Here is example code:

var myObject = {
    some_method: function () {
        return this;
    };
};
//
var cached = myObject.some_method;
cached();

In this code this variable doesn’t point to the myObject object, but instead points to the global something. This is due to the fact, that called function is not called as property of myObject, but instead as anonymous baseless function.

But when function is invoked as a property of the object (“invoke as method”), this variable will point to that object. To say with other words: when function is called “this variable always points to the object to which this function belongs as a property at the moment of the execution. Example to make it clear:

var myObject = {
    some_method: function () {
        return this;
    };
};
//
var second_object = {};
second_object.newMethod = myObject.some_method;
second_object.newMethod();

In the code above “this” variable will point to the second_object, because at the moment of the execution newMethod function was property of second_object object.

Now, after we remembered how context resolution works in javascript, let’s return to our call story.

As I stated, call function is using the same resolution principles and doesn’t have tight reference to it’s associated function and uses this variable to execute associated function. Interesting, isn’t it? In the normal flow execution this variable points to the object, but in our case it points to the function. In pseudo-code you could write call implementation something like this:

var call = function () {
    this();
}

To test my thoughts I’ve created a test snippets which will show below. My snippets uses Zork prototype which is defined as follows:

function Zork(name) {
    this.name = name;
}
Zork.prototype.serialize = function () {
    console.log(this.name);
    return 'My name is ' + this.name;
}

I propose to run call function as anonymous (baseless) function and ensure that it throws the same error as above:

var serializeCall = Zork.prototype.serialize.call;
serializeCall({
    name: 'Nemisj'
});

Console gave back exactly the same error as above.

TypeError: Function.prototype.call called on incompatible undefined

Now let’s execute “call” as a property of “serialize” method, so that we can see whether “call” function is using serialize method as its execution context:

var serialize = Zork.prototype.serialize;
serialize.call({
    name: 'Nemisj'
});

After executing that code it has printed “Nemisj” which proved that “call” is not bound to the referenced function, but defines it at the moment of the execution. If my assumption is correct it also means that I can invoke call function by using its own call method, only instead of passing object to the second call I will pass serialize method.

var serializeCall = Zork.prototype.serialize.call;
serializeCall.call( Zork.prototype.serialize, { name: 'Nemisj' });

This might look a bit weired, but it’s still working :)

This information led me to the next conclusion: In normal situation, if I want this variable to point to the correct object, I use “bind“. But does this mean, that I can use bind function to preserve the associated method for call function? Let’s try it out:

var serializeCallBinded = Zork.prototype.serialize.call.bind( Zork.prototype.serialize );
serializeCallBinded({
    name: 'Nemisj'
});

And the answer is: YES :) After executing this code, it still prints “Nemisj”. Great, now we know how to execute call as anonymous function, let’s move on with our achievement. First implementation with bind:

var serializeCallBinded = Zork.prototype.serialize.call.bind( Zork.prototype.serialize );
var json = arr.map( serializeCallBinded );

But, any iteration method of an Array supports second parameter, which defines the SCOPE of the executed function and since call uses it’s scope to reference the parent function it means, that I can pass the “serialize” method as the second argument and have one nice one-liner:

var json = arr.map( Zork.prototype.serialize.call, Zork.prototype.serialize );

Run…and…check ( http://jsfiddle.net/vNRjd/2 )…code is working :) Well, mission is accomplished I guess.

Except that it does look a bit weird. Passing the same function twice, writing the long Zork.prototype twice..mhee…not nice :) Let’s add some sugar to it.

Because “call” function is isolated and is not bound to its parent function until the invocation, we can pass any native call method to the map as the first argument:

var json = arr.map( Function.prototype.call, Zork.prototype.serialize );

It’s not necessary to give the call method of the prototype of a function, it also can be any call method from any method/function, even the evil “eval” one:

var json = arr.map( eval.call, Zork.prototype.serialize );

But the Function constructor also has a direct “call” method, which simplifies our invocation even more:

var json = arr.map( Function.call, Zork.prototype.serialize );

This looks much prettier and almost human readable: map an array by calling the serialize of Zork :)

I don’t advocate using this code in your production environment. It’s not very self explanatory and it’s a bit slow :) For those who like graphs here is one http://jsperf.com/combine-foreach-and-call. There are more reasons why you shouldn’t write such unreadable code, but I leave it up to you. By making such obscure implementations I understand the working of javascript more and more and I hope you do too.

Have a happy coding.

, , , , , , ,

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>