Last Updated: February 25, 2016
·
3.617K
· bzdzb

Python style @property methods in JavaScript

If you have a value you want to make private and accessible as a class attribute, or if you want to implicitly call a custom get/set function when referencing or assigning to an attribute, Python provides the @property decorator.

Python Example

class Foo(object):
    def __init__(self):
        self._bar = 0

    @property
    def bar(self):
        return self._bar

    @bar.setter
    def bar(self, val):
        self._bar = val

Usage

foo = Foo()
print(foo.bar)  # 0
foo.bar = 42
print(foo.bar)  # 42
foo.bar += 1
print(foo.bar)  # 43

You can achieve the same behavior in JavaScript by defining getter/setter pseudo-properties using get and set operators in your constructor.

JavaScript Example

function Foo() {
  var that = Object.create(Foo.prototype);
  that._bar = 0;
  return that;
}
Foo.prototype =  {
  get bar() { return this._bar; },
  set bar(value) { return this._bar = value; }
};

Usage

foo = Foo();
console.log(foo.bar);  // 0
foo.bar = 42
console.log(foo.bar);  // 42
foo.bar += 1;
console.log(foo.bar);  //  43

JavaScript Validation Example

For example, you could use this to validate values on assignment.

function LivingPerson() {
  var that = Object.create(LivingPerson.prototype);
  that._age = undefined;
  that._name = "";
  that.minAge = 1;
  that.maxAge = 130;
  return that;
}
LivingPerson.prototype = {
  // age property get/set methods
  get age() { return this._age; },
  set age(yrs) {
    if (typeof yrs !== 'number' || isNaN(yrs) || yrs % 1 !== 0) {
      throw new TypeError('year must be a whole number');
    }
    if (yrs < this.minAge || yrs > this.maxAge) {
      throw new RangeError(
          'year must be between ' + this.minAge + ' & ' + this.maxAge);
    }
    return this._age = yrs;
  },
  // name property get/set methods
  get name() { return this._name; },
  set name(fullName) {
    if (typeof fullName !== 'string' || 
        fullName.split(' ').length < 2) {
      throw new TypeError(
          'name must be a string containing first and last name.');
    }
    return this._name = fullName;
  }
};

Usage

bob = LivingPerson();
console.log(bob.age);  // undefined
console.log(bob.name); // ''
bob.name = 'Bob';
// TypeError: name must be a string containing first and last name.
bob.age = 0;
// RangeError: year must be between 1 & 130
bob.age = 6.5;
// TypeError: year must be a whole number
bob.age = 6;
console.log(bob.age);
// 6
bob.name = 'Bob Bobson';
console.log(bob.name);
// Bob Bobson