M. David Green

Existence is a byproduct of semantics

Refactoring Javascript: Using Accessors Instead of Local Variables

Local variables and passed-in arguments are private to the constructor, function, or method that provides their scope. For convenience and readability, it is often most straightforward to access local variables and arguments directly by name. However using getters and setters instead allows the developer to structure access to these values consistently, and manage their values if they might need to be modified globally wherever they are referenced.

For example

Given a Person constructor with first_name and last_name attributes:

function Person(first_name, last_name) {
    var first_name = first_name,
        last_name = last_name;
    this.full_name = function() {
        var full_name = [];
        full_name.push(first_name);
        full_name.push(last_name);
        return full_name.join(" ");
    };
}

Which might be used this way:

var person = new Person("David","Green");
person.full_name(); // "David Green"

In this case, the first_name and last_name variables just create local copies of the arguments being passed in. They are then being used directly to generate the full_name. This is a good practice, since it creates a level of abstraction between the argument being passed in and the local value being used.

But sometimes arguments need to be sanitized, validated, or otherwise managed before they are safe to use in internal methods. Adding getter methods will allow this to happen for the full_name method and any other methods that need safe values from the arguments.

A Jasmine test to make sure this is working might look like this:

describe("a person", function() {
    it("should have a full name", function() {
        var person = new Person("David","Green");
        expect(person.full_name()).toEqual('David Green');
    });
});

Considerations:

Using accessor methods is useful when a local value may need to be modified or sanitized, and it should be considered mandatory to use them for arguments if they need to be modified.

Creating getters for each local variable can be taken to an extreme, and may then result in code that is less readable. For this reason, it is important to consider what the practical implications of using this technique will be in your particular situation. A local variable created within a method may be safe to access directly, while one based on a passed-in argument that requires validation may not be.

The highlighted code above could be refactored into:

function Person(first_name, last_name) {
    this.full_name = function() {
        var full_name = [];
        full_name.push(get_first_name());
        full_name.push(get_last_name());
        return full_name.join(" ");
    };
    function get_first_name() {
        return first_name;
    };
    function get_last_name() {
        return last_name;
    };
}

By following these steps:

  1. Identify a local variable which is being accessed directly, but may need to be sanitized, modified, or otherwise managed consistently across methods.
  2. If you are using test-driven development, this is when you would define your first test parameters with an expected return value, and create your first test.
  3. Create a local getter method that returns the appropriate value for the variable.
  4. Search your code for calls to the original local variable, and replace them with calls to the getter method
  5. Test to make sure things still work as expected.
  6. Delete the local variable and test again to make sure nothing broke as a result of the refactor.

A refactor like this may result in longer code, but having consistent naming for getter methods will make it clearer what they do and how they are used. Eliminating local variables also helps to decrease the memory impact of a constructor, since a copy of each local variable needs to be maintained for each instance of the constructor.