Friday, January 27, 2012

Encapsulation in Javascript

This may be basic JavaScript:TGP, but I'm just getting the hang of it. I asked my JS peeps the other day, "If I have two functions and I they each need access to one internal function, how do I encapsulate that?"

If I need to define one function that uses a nested function, all is well and good:

function validateSomeField() {
function internallyNeededFunction() {
...
}
.... stuff that uses internallyNeededFunction ..
return result;
}

$.validator.addMethod("handyValidation", validateSomeField)

However, if I want to define one more function that needs the same internal function, without exposing the internal method to the rest of the world, it gets a lot more complicated:

var thingThatKnowsHowToValidateSomeField = function() {
var internallyNeededFunction = function() {
...
};
return {
validateSomeField: function() {
.... stuff that uses internallyNeededFunction ..
return result;
},
produceErrorMessage: function() {
.... stuff that uses internallyNeededFunction ..
return result;
}
}
}()

$.validator.addMethod("handyValidation", thingThatKnowsHowToValidateSomeField.validateSomeField)
... and later I can pass the second useful function ...
messages: {
handyValidation: thingThatKnowsHowToValidateSomeField.produceErrorMessage
}

So, in order to encapsulate that internal method in a place where two functions could access it, I had to store the result of an anonymous function that returns an object that contains the two functions I want to publicly expose. That way the internal function can be local to the anonymous function.

... and then, Jay Harris showed me how to do this correctly.

function SomeFieldValidationClass() {
if (!(this instanceof SomeFieldValidationClass)) {
return new SomeFieldValidationClass();
}

var privateMethod = function() {
....
}

this.validateSomeField = function() {
.... stuff that uses internallyNeededFunction ...
return result;
};

this.produceErrorMessage = function() {
.... stuff that uses internallyNeededFunction ...
return result;
};

return this;
};

var thingThatKnowsHowToValidateSomeField = new SomeFieldValidationClass();

$.validator.addMethod("handyValidation", thingThatKnowsHowToValidateSomeField.validateSomeField)
... and later I can pass the second useful function ...
messages: {
handyValidation: thingThatKnowsHowToValidateSomeField.produceErrorMessage
}

This creates a class. In Javascript a class is a function, which acts as a constructor when you call it with "new". Private methods defined within the constructor will only be accessible from within privileged methods, which are defined on "this" in the constructor. A better way to add methods to the class is to add them to the prototype, but then they wouldn't have access to the secret stuff in the constructor. The prototype technique also didn't work because the validator framework calls my methods using ".call(...)" and passing in a "this" which is not an instance of my class.

No comments:

Post a Comment