Getters and Setters of JavaScript for a Java Programmer

Getters and Setters of JavaScript for a Java Programmer

In this blog article I will discuss about an important feature of JavaScript – property Getters and Setters. Getters and Setters help you to achieve data encapsulation. The write up is not going to be a comprehensive tutorial but, will provide some insights to understand the world’s most popular programming language properly (from the stand point of a Java Developer – like myself 🙂 ).

Getters and Setters

JavaScript getters and setters work different than Java getter and setters though the purpose is the same – encapsulation. First I will brief about the Java getters and setters and then next I will discuss about JavaScript getters and setters.

A typical Java POJO looks like this.

 

    class Person {
        private String firstName;
        private String lastName;

        Person(String firstName, String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }

        public String getFirstName() {
            return firstName;
        }

        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }

        public String getLastName() {
            return lastName;
        }

        public void setLastName(String lastName) {
            this.lastName = lastName;
        }

        //The rest of the methods like equals(), toString() and hashCode() may go here.
    }

    

Here getters and setters are typical(mostly generated by using IDEs 🙂 )


        Person person = new Person("John", "Skeet");
        person.getLastName();//Skeet
        person.setLastName("Doe");//Access the method to set the lastName property
    

These methods get and set the member variables firstName and lastName.
The methods g[s]etXXX can be used to have control over the values of firstName and lastName. For more information on this pattern refer to

Why Getters and Setters?

Being a Java developer one may tend to write the same in JavaScript like the following.


        function Person(firstName, lastName) {
            this.firstName = firstName;
            this.lastName = lastName;

            //Function expression; all the vars are hoisted, but assignments are not hoisted.
            //Functions are defined as values. If the value of a var is a function then 
            //it is known as a method.

            //And in JS, private methods don't have access to the public 
            //variables(firstName and lastName)
            //Because inner functions don't share the context of the parent scope

            //Here all the vars(whether value or function) are scoped to the function.
            //They are undefined outside.

            var getFirstName = function() {
                return this.firstName;
            }

            var setFirstName = function(firstName) {
                this.firstName = firstName;
            }

            var getLastName = function() {
                return this.lastName;
            }

            var setLastName = function(lastName) {
                this.lastName = lastName;
            }
        }

        var person = new Person("John", "Skeet");

        //The following is a public method that is available to all the live objects and 
        //future objects of type Person. This method is inherited from an object called the
        //prototype of the Person constructor function

        Person.prototype.toString = function() {
            return this.getFirstName() + " " + this.getLastName();
        }

        document.writeln(person);

    

Or like this…


        function Person(firstName, lastName) {
            this.firstName = firstName;
            this.lastName = lastName;

            //All these are now privileged methods - as what they call -
            //These methods have access to all the vars(values and functions)
            //that are visible with in the function scope along with the properties
            //assigned to 'this' (values and functions) as they are defined on 'this'.

            this.getFirstName = function() {
                return this.firstName;
            }

            this.setFirstName = function(firstName) {
                this.firstName = firstName;
            }

            this.getLastName = function() {
                return this.lastName;
            }

            this.setLastName = function(lastName) {
                this.lastName = lastName;
            }
        }

        var person = new Person("John", "Skeet");

        Person.prototype.toString = function() {
            return this.getFirstName() + " " + this.getLastName();
        }

        document.writeln(person);
    

But, both these patterns are not the ways to write getters and setters in JavaScript. In fact, the first template will throw error.


    Uncaught TypeError: this.getFirstName is not a function(…)
    

Because getFirstName is a variable inside the Person constructor, which is a function that is not visible outside the scope. Because its scope is function scope.

The second template works fine without any errors and it gives the value ‘John Skeet’. But the methods are normal methods (like any other method) of the person object. And by the way, one can directly access the properties without the need of those methods.


        person.firstName = "Osama";//boom
    

So, the correct way of achieving the encapsulation in JavaScript is by using the Object.defineProperty() method.


        var Person = function() {
            var first_name,
                last_name,
                that;

            that = this; //Parent context for use by inner functions

            //Your private functions and other variables go here

            Object.defineProperty(this, "firstName", {
                get: function() {
                    //Here we are not returning firstName. We are returning first_name
                    return first_name;
                },
                set: function(value) {
                    if (typeof value === 'string' && value.length > 3) {
                        first_name = value;
                    }
                },
                configurable: true,
                enumerable: true
            });

            Object.defineProperty(this, "lastName", {
                get: function() {
                    //Here we are not returning lastName. We are returning last_name
                    return last_name;
                },
                set: function(value) {
                    if (typeof value === 'string' && value.length > 3) {
                        last_name = value;
                    }
                },
                configurable: true,
                enumerable: true
            });
        };

        var person = new Person();
        //See, No arguments. First time if you call person.firstName you get undefined
        person.firstName = "John";
        person.lastName = "Skeet";
        document.write(person.lastName); //Skeet
        document.write(person.firstName); //John
        person.lastName = "Doe"; 
        //Now setter is called, but it has rejected the value as Doe is only three characters
        document.write(person.lastName); //Skeet

    

Using the object literal way…….


        var person = {
            name: 'John Skeet',
            get getName() {
                return this.name;
            },
            set setName(name) {
                if (typeof name !== 'string' || name.length < 3) {
                    throw new Error("Person name can't be empty and 
                          should be more than 3 characters");
                }
                this.name = name;
            }
        }

        document.writeln(person.getName());//Expecting John Skeet?
        //Noo... You will get 
        //Uncaught TypeError: person.getName is not a function(…)
        //Because getName is a property of the object person, which invokes a function

        document.writeln(person.getName);//Expecting John Skeet? You got it. Its not a method
        //Now, don't do this...  person.setName("John Doe");
        //Do this

        person.setName = "John Doe";

        document.writeln(person.getName); //John Doe
    

Another alternative by using Java way of getName and setName is ….


        var Person = function(name) {
            var name,
                isValidName = function(name) {
                    if (typeof name !== 'string' || name.length < 3) {
                        throw new Error("Person name can't be empty 
                              and should be more than 3 characters");
                    }
                    return true;
                };

            if (isValidName(name)) {
                name = name;
            }

            this.getName = function() {
                return name;
            };

            this.setName = function(value) {
                if (isValidName(value)) {
                    name = value;
                }
            };
        };

        var john = new Person("Doe");

        document.writeln(john.getName());//Doe

        john.setName("");
        //Uncaught Error: Person name can't be empty and should be more than 3 characters(…)
    

Hope you have enjoyed learning the worlds’s most misunderstood but highly expressive language – JavaScript.
Wish you happy functional programming…

–Rajasekhar
Developer,
Helical IT Solutions.