An object can store data in one of two ways: as a property or as a variable. The method that you choose will have substantial ramifications on data visibility. Object properties are created using the “this.” prefix. The name that follows the dot will identify a new property to add to the object’s properties collection. That makes it a poor choice for storing private data. Variables, on the other hand, are created using the “var” keyword. It is subject to the rules of variable scoping and are more private for that reason. The following Person object contains a number of properties, accessible via setter and getter functions:
function Person() {
//properties/fields
this.name = "Rob Gravelle";
this.height = 68;
this.weight = 170;
this.socialInsuranceNumber = "555 555 555";
//methods/actions
this.setHeight = function(height) {
this.height = height;
};
this.getHeight = function() {
return this.height;
};
this.setWeight = function(weight) {
this.weight = weight;
};
this.getWeight = function() {
return this.weight;
};
this.setName = function(name) {
this.name = name;
};
this.getName = function() {
return this.name;
};
this.setSocialInsuranceNumber = function(socialInsuranceNumber) {
this.socialInsuranceNumber = socialInsuranceNumber;
};
return this;
}
//instanciate the Person class
var aPerson = new Person();
var myName = aPerson.getName(); //myName now contains "Rob Gravelle"
aPerson.setName("mud"); //change the name
var myName = aPerson.getName(); //aPerson's name is now "mud"
var sinNo = aPerson.getSocialInsuranceNumber(); //will also throw an exception. No getter implemented for that field!
We define these function by prepending the this keyword which makes them accessible from the outside (see Encapsulation). Notice that the functions have full access to the properties.
to try calling the fields directly:
//instanciate the Person class
var aPerson = new Person();
var myName = aPerson.name; //this works
//as does this:
aPerson.name = "whatever.";
The lesson here is that object member variables are public, while those that are stored as variables are private. Our second example uses variables instead of properties to hide all the data fields:
function Person() {
//properties/fields
var name = "Rob Gravelle";
var height = 68;
var weight = 170;
var socialInsuranceNumber = "555 555 555";
//methods/actions
this.setHeight = function(newHeight) {
height = newHeight;
};
this.getHeight = function() {
return height;
};
this.setWeight = function(newWeight) {
weight = newWeight;
};
this.getWeight = function() {
return weight;
};
this.setName = function(newName) {
name = newName;
};
this.getName = function() {
return name;
};
this.setSocialInsuranceNumber = function(newSocialInsuranceNumber) {
socialInsuranceNumber = newSocialInsuranceNumber;
};
return this;
}
//instanciate the Person class
var aPerson = new Person();
var myName = aPerson.name; //this no longer works
var myName = aPerson.getName(); //this does
Notice that the setters’ argument is named differently than the underlying variable. That’s to avoid duplicate variables within the same scope.
Encapsulation
Encapsulation is the ability of an object to be a container (or capsule) for its member properties, including variables and methods. As a fundamental principle of object oriented programming (OOP), encapsulation plays a major role in languages such as C++ and Java. However, in scripting languages, where types and structure are not actively enforced by the compiler or interpreter, it is all-too-easy to fall into bad habits and write code that is brittle, difficult to maintain, and error-prone.
We create objects with methods, properties, and attributes.
When we make them bundled inside an object it’s known as encapsulation. When these methods and attributes are abstracted from other objects, this is known as abstraction.
JavaScript is designed on a simple object-based paradigm. An object is a collection of properties, and a property is an association between a name (or key) and a value. A property’s value can be a function, in which case the property is known as a method. In addition to objects that are predefined in the browser, you can define your own objects.
Two principles with OOP in JS are:
Object Creation Pattern (Encapsulation)
Object Reuse Pattern (Inheritance)
Private member variables in objects.
All properties or let us say member variables defined with the “this” keyword inside a function object is of public type, which mean that they can be accessed from outside of a created object.
function Person(n) {
// name and getName ar both public
this.name = n
this.getName = function() {
return this.name
}
}
All variables, which are defined with the “var” keyword (and NOT ‘this’) and all the methods that a constructor function is private and therefore NOT accessible from outside of a created object. The same applies to all function arguments.
– One of the most important things in object-oriented programming (OOP) is data encapsulation, which means to make private properties and then define public access methods to change these.
-
All public methods, which are defined inside a function object, have access to all defined private member variables and private methods in a created object.
-
Public methods defined on the outside of a function object can not access private member variables as they are not defined in the same scope as those.
function Rectangle(h, w) {
var width = w // Both the 'width' and 'w' is private
var height = h // Both the 'height' and 'h' is private
this.setWidth = function(w) {
width = w
}
this.setHeight = function(h) {
height = h
}
this.getWidth = function() {
return width
}
this.getHeight = function() {
return height
}
this.constructor.prototype.getDiagonal = function() {
return Math.sqrt(height * height + width * width)
}
}
Rectangle.prototype.getArea = function() {
// We must use accessors in a prototype kind of method,
// then these methods can not access the private members
// of a created object.
return this.getWidth() * this.getHeight()
}
var rect = new Rectangle(60, 70)
rect.setHeight(20)
console.log(rect.getArea()) // => 1400
Private methods (in the below example the born() method) have no direct access to properties that are defined to be public with “this” keyword in a function object.
To achieve this, one can define a variable that has the same reference as “this” refers to. And thats exactly whey I have to create the var thisObj in the below Person()
function Person(n, y) {
var name = n
var year = y
// Set a variable to the same as this
var thisObj = this
this.setName = function(n) {
name = n
}
this.getName = function() {
return name
}
this.setYear = function(y) {
year = y
}
this.getYear = function() {
return year
}
var born = function() {
var nYear = new Date().getFullYear()
// Use the thisObj variable which is the same as this refer to.
return nYear - thisObj.getYear()
// If I just used 'this' directly here, will get error -
// this.getYear is not a function
// The "this" keyword inside this function refers to an object
// created by the constructor function of this function object.
}
this.getBornYear = function() {
return born()
}
}
var person = new Person("Nikita", 60)
console.log(person.getName()) // => Nikita
console.log(person.getBornYear()) // => 1959
console.log(person.born()) // => person.born is not a function
This is an example of “class-free” object oriented programming
const MyObj3 = initVal => {
let myVal = initVal;
return {
get: function() {
return myVal;
},
set: function(val) {
myVal = val;
}
};
};
Just like the approach above, we can use it to create an object like this:
const x = MyObj3(0)
The myVal variable is essentially private, which means we can no longer access it using x.myVal like in the other implementations above. Instead we have to call the getter function:
x.get()
Closures enable a strong contract
The variable is conceptually “saved” inside the object returned from the function thanks to Javascript closures. This means that we can set the value with the setter method (i.e. x.set(2)), but attempting to directly change the field (i.e. x.myVal = 2) won’t do anything.
The greatest part of having these explicit setters and getters is that the object now has a strong contract/interface with the outside world. The state is fully encapsulated and the only ways in and out of the object is through the setters and getters.
One drawback about using closures is the issue of performance. While there doesn’t seem to be any difference when creating an object, method calls on the object using closures were about 80% slower.