loading...

JavaScript – Property Getters and Setters

We’ve said that an object property is a name, a value, and a set
of attributes. In ECMAScript 5[9] the value may be replaced by one or two methods, known
as a getter and a setter.
Properties defined by getters and setters are sometimes known as
accessor properties to distinguish them from
data properties that have a simple
value.

When a program queries the value of an accessor property,
JavaScript invokes the getter method (passing no arguments). The
return value of this method becomes the value of the property access
expression. When a program sets the value of an accessor property,
JavaScript invokes the setter method, passing the value of the
right-hand side of the assignment. This method is responsible for
“setting,” in some sense, the property value. The return value of the
setter method is ignored.

Accessor properties do not have a writable
attribute as data properties do. If a property has both a getter and a
setter method, it is a read/write property. If it has only a getter
method, it is a read-only property. And if it has only a setter
method, it is a write-only property (something that is not possible
with data properties) and attempts to read it always evaluate to
undefined.

The easiest way to define accessor properties is with an
extension to the object literal syntax:

var o = {
    // An ordinary data property 
    data_prop: value,

    // An accessor property defined as a pair of functions
    get accessor_prop() { /* function body here */ },
    set accessor_prop(value) { /* function body here */ }
};

Accessor properties are defined as one or two functions whose
name is the same as the property name, and with the function keyword replaced with get and/or set. Note that no colon is used to separate
the name of the property from the functions that access that property,
but that a comma is still required after the function body to separate
the method from the next method or data property. As an example,
consider the following object that represents a 2D Cartesian point. It
has ordinary data properties to represent the X and Y coordinates of
the point, and it has accessor properties for the equivalent polar
coordinates of the point:

var p = {
   // x and y are regular read-write data properties.
   x: 1.0,
   y: 1.0,

   // r is a read-write accessor property with getter and setter.
   // Don't forget to put a comma after accessor methods.
   get r() { return Math.sqrt(this.x*this.x + this.y*this.y); },
   set r(newvalue) {
     var oldvalue = Math.sqrt(this.x*this.x + this.y*this.y);
     var ratio = newvalue/oldvalue;
     this.x *= ratio;
     this.y *= ratio;
   },

   // theta is a read-only accessor property with getter only.
   get theta() { return Math.atan2(this.y, this.x); }
};

Note the use of the keyword this in the getters and setter above.
JavaScript invokes these functions as methods of the object on which
they are defined, which means that within the body of the function
this refers to the point object. So
the getter method for the r
property can refer to the x and
y properties as this.x and this.y. Methods and the this keyword are covered in more detail in
Method Invocation.

Accessor properties are inherited, just as data properties are,
so you can use the object p defined
above as a prototype for other points. You can give the new objects
their own x and y properties, and they’ll inherit the
r and theta properties:

var q = inherit(p);  // Create a new object that inherits getters and setters
q.x = 1; q.y = 1;    // Create q's own data properties
console.log(q.r);    // And use the inherited accessor properties
console.log(q.theta);

The code above uses accessor properties to define an API that
provides two representations (Cartesian coordinates and polar
coordinates) of a single set of data. Other reasons to use accessor
properties include sanity checking of property writes and returning
different values on each property read:

// This object generates strictly increasing serial numbers
var serialnum = {
    // This data property holds the next serial number.
    // The $ in the property name hints that it is a private property.
    $n: 0,

    // Return the current value and increment it
    get next() { return this.$n++; },

    // Set a new value of n, but only if it is larger than current
    set next(n) {
        if (n >= this.$n) this.$n = n;
        else throw "serial number can only be set to a larger value";
    }
};

Finally, here is one more example that uses a getter method to
implement a property with “magical” behavior.

// This object has accessor properties that return random numbers.
// The expression "random.octet", for example, yields a random number
// between 0 and 255 each time it is evaluated.
var random = {
    get octet() { return Math.floor(Math.random()*256); },
    get uint16() { return Math.floor(Math.random()*65536); },
    get int16() { return Math.floor(Math.random()*65536)-32768; }
};

This section has shown only how to define accessor properties
when creating a new object from an object literal. The next section
shows how to add accessor properties to existing objects.


[9] And in recent ECMAScript 3 versions of major browsers other
than IE.

Comments are closed.

loading...