Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at CFUNITED 2009 (Lansdowne, VA) with:

JavaScript Method Context With Circular Invocation In Conjunction With Call() Or Apply()

Posted by Ben Nadel

In JavaScript, you can change part of the execution context using the call() and apply() methods. These allow you to explicitly define the binding of "this" at the time of method invocation. While I use these functions all the time, I realized recently that I was unsure what would happen if you created a circular invocation chain using an overridden method. To ease my mind, I created a small experiment:

  • <!DOCTYPE html>
  • <html>
  • <head>
  • <title>Testing JavaScript Context</title>
  •  
  • <script type="text/javascript">
  •  
  •  
  • // For this test, we are going to look at how circular
  • // method invocations work in the context of a method
  • // that has been invoked with either call() or apply().
  • var testHarness = {
  •  
  • one: function(){
  • console.log( "Real Context!" );
  • },
  •  
  • two: function(){
  • this.one();
  • }
  •  
  • };
  •  
  •  
  • // -------------------------------------------------- //
  • // -------------------------------------------------- //
  •  
  •  
  • // To test the circular invocation and this-based context,
  • // we need to override the "one" method, and have it invoke
  • // the "two" method so we can then see what version of "one"
  • // it will turn around and invoke.
  • function one(){
  •  
  • console.log( "FAKE Context!" );
  •  
  • // Create circular invocation.
  • this.two();
  •  
  • }
  •  
  •  
  • // -------------------------------------------------- //
  • // -------------------------------------------------- //
  •  
  •  
  • // Invoke "fake" ONE in the context of the test harness.
  • one.call( testHarness );
  •  
  •  
  • </script>
  • </head>
  • <body>
  • <!-- Left intentionally blank. -->
  • </body>
  • </html>

Here, we have a test object with two methods: one() and two(). In its natural state, two() invokes one() but, one() never invokes two(). Then, we create a completely separate function which overrides the "one" method and invokes two(). Does this create a circular reference problem?

When we run the above code, we get the following console output:

FAKE Context!
Real Context!

When we run the the override method, we clearly get the fake (ie. explicitly changed) context. But, when that function invokes two(), which subsequently invokes one(), the "second" invocation of one() reveals that its context is the original context.

No circular references were created.

When I saw this, I suddenly had clarity - I realized that my previous mental model of call() and apply() was completely wrong. I kept thinking about it as some sort of temporary, runtime method injection magic. I kept thinking about it as overriding part of the target context's object definition. In reality, it's nothing more than an overriding of the given method's "this" binding. There's nothing more to it. There's no relationship between the method being invoked and the context that is being applied.

In retrospect, this should have been completely obvious. I guess at some point, I had just created a false mental model of how things worked and never stopped to question it. Clarity for the win!




Reader Comments

@Loic,

I think part of my distortion came from the fact that my primary programming language - ColdFusion - doesn't have a call() or apply() equivalent. As such, in that language, you actually DO have to fine way to use method injection rather than overriding the "this" binding. So, while I know how call() and apply() work, it was being colored improperly by my understanding of a different language altogether.

Reply to this Comment

Post A Comment

?
You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.