Just JavaScript - How Functions Become Methods |
Written by Ian Elliot | ||||
Thursday, 15 May 2014 | ||||
Page 2 of 3
The Solution - thisThe solution to the problem of how to reference the object that the method is a property of is a very simple one - the call context. When you evaluate a Function object that is a property of an object the built in variable "this" is set to reference the same object. That is, when you write:
this is set to reference the same object as myObject by the system. If you write:
this is set to reference the same object as myOtherObject no matter what that object might be. So what this means is you can now write the getSize method as:
Now when you write:
this is set to the same object as myObject and the method is equivalent to:
and all without actually using the myObject variable in the code. When you write:
the method is equivalent to:
Simple, elegant and it works - as long as you understand it and use it properly. In chapter 1 is was stated that outside of a function this references the Global object and now you can see that this is logical. Outside of a function the Global object is the object that can be considered to call all the other functions. When a Function object is called as the property of another object then this is set to reference that object. When a Function object is called not as a property of another object then this is set to the global object. There is an important subtly here. Function objects are late boundA Function object exists in its own right and not as part of some other object. When you create a Function object as a property of another object the Function object exists as an independent entity and how it is evaluated depends on how it is used. A Function object can have many references to it and some of these might be as properties of another object and some might be global. Whether a Function object is treated as a function or a method depends on how you reference it. For example:
The Function object the getSize property references is created when the object that myObject references is created - but it is just a normal object that happens to be a Function object. The second statement of the program sets myFunction to reference that Function object. Now if you call the function using
this references the same object that myObject references and so the function works correctly as a method bound to myObject. If you call the function using:
this will be set to reference the Global object and in this case the function will be evaluated as if all its variables were global. In this case, of course, the function will not work because there is no global variable called mySize. The point is that Function objects exist independently of other objects and as a result a given Function object can be a method bound to an object when called in one way and not a bound method when called in another. Yet another way of putting this is that Function objects are late bound to any objects that might use them as methods i.e. what this references is only determined when the function is evaluated and it can change dynamically. This is often quoted as a defect of JavaScript because in other languages functions are always methods that are bound to instances of classes. They always belong to a particular object and this always, well nearly always, means the same object for all time. Now consider the situation with JavaScript. A Function object always has an independent existence from any other object. This means that it can be evaluated in its own right and this will be set to the global object in this case. It can also be evaluated as a property of any other object and in this case this is set to reference that object. A Function object can also be a method for lots of different objects at the same time. Call and ApplyAs discussed earlier a method is a function that is supplied with a default parameter - the object that it is bound to. An alternative to regarding the function as a method is to simply explicitly supply the parameter and you can do this in JavaScript using the call and apply methods of the Function object. If you use
then the function will be evaluated with this set to thisarg and the parameters set to the values specified as the comma separated list args. As we are currently interested in methods and functions we can igore the args as they are optional. For example, if you define a Function object:
you can use it as an ad-hoc method for any object using call. For example:
evaluates getSize with this set to myObject. In other words, you get the same result as if getSize was a method of myObject i.e. it will try to access myObject.mySize. You can also use apply in the same way the only difference is that after the thisarg you can supply an array of values to be used as arguments for the functions parameters. The two methods call and apply allow you to borrow methods from other objects by calling them with set values for this. Early And Late Binding - the bind methodJavaScript's late binding of Function objects isn't usually a problem - except when it is. The fact that Functions are objects means that you can pass a function as a parameter, assign it as a property and so on. However all of these actions result in you passing or assigning a function object - not a method and sometimes we want to manipulate a method. For example, earlier we created a method:
but when the method is assigned to another variable:
it reverts to being just a Function object when called as
Can we do this in a way that makes myFunction a method bound to myObject? In other words can we arrange to early bind the Function object to myObject and the answer is yes. If you are using ECMA 5 and an up-to-date browser then the simplest way is to use the bind method of the Function object.
This creates a limited copy of the Function object with this permanently set to the object you specify. If you are not using ECMA 5 there are various well known "polyfills" that do the same job using the apply method. For example:
creates a copy of getSize with this set to myObject for all time. That is this is bound to myObject well before the function is evaluated i.e. this is early binding. Now if you evaluate myFunction
you will get the result 99 as determined by the state of myObject. That is myFunction is actually a reference to a method of myObject. One subtlety to be aware of, although it rarely causes any problems, is that the myFunction references a new Function object, which evaluates the original unbound function using apply. The object it is bound to, e.g. the new Function object referenced by myFunction, is something like:
This means that the new Function object doesn't have any of the properties of the original Function object, but this usually doesn't matter as most Function object don't have any additional custom properties. In most of the examples you will come across, bind is used in an ad-hoc manner, to convert a function into a method, as in the example above. You can also use the bind method to create early bound methods as properties of objects. That is. objects can have late and early bound methods. For example:
This creates a method early bound to myObject right from its definition. Notice that you can't define the function in the object literal as previously done because myObject, which you need to specify in the bind, isn't defined until the literal is fully defined. That is, you can't use myObject within the definition of myObject. With this slight change you can now do things like:
and when you evaluate myMethod() it will return the value in myObject. This idiom of creating early bound methods as object properties is preferable to converting a late bound method into an early bound reference because there is only one Function object involved, complete with any properties you create.
|
||||
Last Updated ( Wednesday, 13 September 2017 ) |