Recently I was writing some script when I bumped into a problem with this
in javascript. I wanted execute function once the user select the file to be processed.
1 |
document.getElementById('files').addEventListener('change', Controller.OnNewFilesAdded, false); |
And then the Controller, simulating class used this
keyword
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function Controller() { this.myData = "Controller data"; this.DoSomething = function(txtData){ console.log("hello"); } this.OnNewFilesAdded = function(evt) { var files = evt.target.files; // FileList object for (var i = 0, f; f = files[i]; i++) { this.doSomething(files[i]); } } } |
However calling doSomething was resulting in error
14:24:23.401 TypeError: this.doSomething is not a function
The reason for that is that when function OnNewFilesAdded
is called, the this
context was setted up as I originally expected. The this
keyword doesn’t reference the object in which it’s declared, but the object which called this function.
Solution to this problem is to use .bind(obj)
method to manually set the context / object referenced by this
.
After changing the addEventListener to following
1 |
document.getElementById('files').addEventListener('change', Controller.OnNewFilesAdded.bind(Controller), false); |
So using the bind
method we explicitly said that the this
execution context should have the value of Controller instead of the object returned by getElementById(‘files’) as the executor of OnNewFilesAdded.
One more simple demonstration of this concept can be seen in this example :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<html> <head> <script type="text/javascript"> function MyClass(){ this.myprop = "hello" this.thisMyClass = "myclass"; this.Hello = function(evt) { console.log("hello: Contex properties " + Object.getOwnPropertyNames(this)) ; } } function Proxy(){ this.reference = null; this.thisIsProxy = "proxy"; this.call = function() { console.log("reference call, so far, context has props : " + Object.getOwnPropertyNames(this)) this.reference() } } proxy = new Proxy() obj = new MyClass() proxy.reference = obj.Hello obj.Hello() proxy.call() </script> |
Which then prints
Hello(): this contex properties myprop,thisMyClass,Hello
Proxy.Call(), this context properties : reference,thisIsProxy,call
Hello(): this contex properties reference,thisIsProxy,call
When we are calling obj.Hello()
we can see this
is referencing the object of MyClass as expected.
However, in second case the call to Hello()
function is indirect through proxy object. And because the function obj.Hello()
is called from Proxy object, it’s context is preserved also inside the call of Hello()
function.
If we would want to fix this problem, again, we can use .bind method. Change the original
1 |
proxy.reference = obj.Hello |
to
1 |
proxy.reference = obj.Hello.bind(obj) |
and now the output of the script will be
Hello(): this contex properties myprop,thisMyClass,Hello
Proxy.Call(), this context properties : reference,thisIsProxy,call
Hello(): this contex properties myprop,thisMyClass,Hello
Here as we can see, the this in second call is correctly (assuming this is the functionality we wanted) referencing the MyClass object, instead of Proxy object.