Skip to content

Instantly share code, notes, and snippets.

@Ghanshyam-K-Dobariya
Last active September 15, 2018 05:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Ghanshyam-K-Dobariya/33e0b5f163f9c94e6e7808daeac0b50d to your computer and use it in GitHub Desktop.
Save Ghanshyam-K-Dobariya/33e0b5f163f9c94e6e7808daeac0b50d to your computer and use it in GitHub Desktop.

Ever wondered why setting array.length = 0 makes an array empty or array.length = 'some string' throws exception ? Code example.

screen shot 2018-09-14 at 10 16 50 
am

So now you have curiosity to know what happens in the background when command array.length = 0 takes place ?

Let's start this journey with below requirement.

Create an object circle with properties area & radius, perform basic validations while assigning value to radius property and calculate area automatically.

Formula of area is = PI * radius * radius; value of PI is 3.14

By reading this requirement what we think first is to create below getters and setters methods to an object circle

1) getRadius()
2) setRadius(radius)
3) getArea()

We will use following helper method to validate radius property.

  function isANumber(num) {
    return typeof num === 'number'
   }

Typical implementation will be like below

circle = {
    radius: 0,
    area: 0,
    getRadius() {
	    return this.radius
	},
    getArea() {
	    return this.area
	},
    setRadius(r) {
	    if (!isANumber(r)) {
		    throw('Invalid radius passed to setRadius()');
		} else {
		    this.radius = r;
		    this.area = 3.14 * r * r;
	    }
    }
}

Tests

circle.getRadius() // 0
circle.getArea() // 0

circle.setRadius(10);

circle.getRadius(); // 10
circle.getArea(); // 314

circle.setRadius('abc'); // 'Invalid radius passed to setRadius()'

circle.getRadius(); // 10
circle.getArea(); // 314

Are we done ?

It seems yes by looking @ output of test cases - as all went as expected.

Wait... wait... wait.... but did you find any problem here ?? There are actually.

  1. This approach is not scalable - think when there are multiple properties on which you need to make validation before updating ? code will really big & ugly.

  2. You need to remember all method names (getters and setters).

(Even if you don't find any problem in above approach then there is no harm in knowing other alternative way - later we can decide when to use what ?)

Approaching better way

In situation like above one javascript's in house getters and setters can be used.

It is as simple as -

  • When you assign value to property - it's setter will be called internally.
  • When you access (read) property - it's getter will be called internally.

Example.

circle = {
    _radius: 0,
    _area: 0,
    get radius() {
	    return this._radius
	},
    get area() {
	    return this._area
	},
    set radius(r) {
	    if (!isANumber(r)) {
		    throw('Invalid radius');
		} else {
		    this._radius = r;
		    this._area = 3.14 * r * r;
	    }
    }
}

Note

You can not return same key from it's getter() , that is why we are returning _radius from get radius().

I know right now you have curiosity to know what happen if we do that - don't worry it is covered @ end of this doc.

You may think that above code is complex and a bit boring as well, but actual benefit you will see in accessing or modifying values (radius & area) - no more method calls like older approach - see code below.

Tests

/* initial values */
circle.radius; // 0
circle.area; // 0

/* setting radius to 10 */
circle.radius = 10;

/* getting radius and area */
circle.radius; // 10
circle.area; // 314

/* setting invalid value */
circle.radius = 'abc'; 'Invalid radius'

/* getting radius and area after trying to set invalid values to radius */

circle.radius; // 10
circle.area; // 314

You can use Object.defineProperty to make code cleaner.

circle = {
	_radius: 0,
	_area: 0
}

Object.defineProperty(circle, 'radius', {
    get: function() {
        return this._radius
    },
    set: function(r) {
        if (!isANumber(r)) {
			    throw('Invalid radius');
		} else {
		    this._radius = r;
		    this._area = 3.14 * r * r;
	    }
    }
});
Object.defineProperty(circle, 'area', {
    get: function() {
        return this._area
    }
});

You can perform modification on one or more member of an object from setter of other member(s). (Like example of an array given @ beginning)

When you return same key from it's getter - infinite calls to getter() will take place and eventually you will get RangeError: Maximum call stack size exceeded

screen shot 2018-09-14 at 10 15 10 am

Now you can understand that what happens when you set array.length = n , if n is less than length of an array than all the values whose index is greater or equal to n will be removed from an array and array.length will be set to n, on the other side if n is greater than actual length then remaining indexes in array will be filled with empty

screen shot 2018-09-14 at 10 12 55 am

More on this operations, you can read here

So craft your getters and setters carefully, not all the places you will need such getters and setters - think twice and cut once.

More on getters, setters & Object.defineProperty.

Learned from internal tech talks in my company Tekion Corp.

Thanks to my mentor (& presenter of this topic) : Nitish Kumar

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment