JavaScript Objects can be created using object constructor and object literal;
Using Object literal
var person1 = {
name: "shahzad"
};
person1.age = 34;
person1 //returns {name: "shahzad", age: 34}
using Object constructor
var person2 = new Object();
person2.name = "shahzad"
person2.age = 34
person2 //returns {name: "shahzad", age: 34}
JavaScript uses an internal method called [[Put]] to add a property to object. We can compare this to adding a key to a hash table for the first time. Internally JavaScript engine makes a [[Put]] call that results in the creation of an own property on the object. This means that the specific instance of the object owns that property. All operations on the property must be performed through that object.
When a new value is assigned to existing property, a separate operation called [[Set]] happens. Thee operation results in replacing current property value with a new one.
Detecting Properties
Properties can be added dynamically. If we want to check the existence of a property;
if (person1.age){
console.log("property exists") //returns “property exists“
}
This method of checking property works but it is not reliable. The if condition evaluates to true if the value is truthy (an object, a nonempty string, a nonzero number, or true) and false if the value is falsy (null, undefined, 0, false, NaN, or an empty string). In the example code, the object property can contain one of these falsy value and will return false negatives.
Let’s test this;
person1.age = 0
if (person1.age){
console.log("property exists") //returns false negative value “Undefined”
}
A more reliable way to test the existence of a property is with “in” operator.
console.log("name" in person1); //returns true
console.log("age" in person1); //returns true
console.log("title" in person1); //returns false
The “in” operator looks for a property in an object and returns true if exists otherwise false. We can also check the existence of a method using “in” operator.
var person = {
name: "shahzad",
sayName: function(){
console.log(this.name);
}
};
console.log("sayName" in person); //returns true
The “in” operator added benefit is to not evaluating the value of the property which affects function performance overall.
What If we want to check existence of a property only if it is an own property.
The “in” operator checks for own properties and prototype properties.
console.log("name" in person); //returns true
console.log("toString" in person); //returns true
The hasOwnProperty() method checks the presence of an own property in the object.
console.log(person.hasOwnProperty("name")); //returns true
console.log(person.hasOwnProperty("toString")); //returns false
Removing Properties
Setting a property to null doesn’t actually remove the property completely. The will make an operation call [[Set]] with a value of null. We need to use the delete operator to completely remove a property from an object. The delete operator works on a single object property. We can think of this operation as removing a key/value pair from hash table.
When delete operation is successful, it returns true;
var person = {
name: "shahzad"
};
console.log(person) //returns {name: "shahzad"}
console.log("name" in person) //returns true
delete person.name //returns true – no output
console.log("name" in person) //returns false
console.log(person.name) //returns undefined
Object Enumeration
All properties we add to an object are enumerable by default. We can iterate over them using for-in loop. Enumerable properties have their internal [[Enumerable]] attribute set to true. The for-in loop enumerates all enumerable properties on an object, assigning the property name to a variable.
//define object
var person1 = {
name: "shahzad",
age: 34
};
//enumerate this object
var property;
for (property in person1) {
console.log("Name: " + property);
console.log("Value: " + person1[property]);
}
//return values
Name: name
Value: shahzad
Name: age
Value: 34
If we just want to need a list of an object’s properties to use later in the program, ECMAScript 5 introduced the Object.keys() method. This retrieves an array of enumerable property names;
var properties = Object.keys(person1);
var i, len;
for (i=0,len=properties.length;i<len;i++){
console.log("Name: " + properties[i]);
console.log("Value: " + person1[properties[i]]);
}
//return values
Name: name
Value: shahzad
Name: age
Value: 34
The for-in-loop and Object.keys() behaves similar but there is a difference in underlying behavior. The for-in-loop enumerates own (instance) properties and prototype properties. Object.keys() returns only own (instances) properties.
Not all properties are enumerable. Most of the native methods on object have their [[Ienumerable]] attribute set to false. To check if a property is enumerable or not, use this;
Check with propertyIsEnumberable
console.log("name" in person1) //returns true
console.log(person1.propertyIsEnumerable("name")); //returns true
Check with Object.Keys
var properties = Object.keys(person1)
console.log("length" in properties) //returns true – native property
console.log(properties.propertyIsEnumerable("length")); //returns false
Types of properties
JavaScript has data properties and accessor properties. Data properties contain a value, like the name property from earlier example. The default behavior of the [[Put]] method is to create a data property.
Accessor properties don’t contain a value but instead define a function to call when the property is read (called a getter), and a function to call when the property is written to (called a setter). Accessor properties only required either a getter or a setter, though they can have both.
var person = {
_name: "shahzad",
get name() {
console.log("readming name");
return this._name;
},
set name(value) {
console.log("setting name to %s", value);
this._name = value;
}
};
console.log(person.name) //returns “readming name” then “shahzad”
person.name = "Ali"; //returns “setting name to Ali” then “Ali”
This example defines an accessor property called name. There is a data property called _name that contains the actual value for the property. The leading underscore is a common convention to indicate that property is considered to be private, though in reality it is still public.
The syntax for getter and setter looks a lot like function without function keyword. The special keyword get and set are used before accessor property name.
Property Attributes
Prior to ECMAScript 5, there was no way to access the internal attributes of a property at all. ECMAScript 5 changed this by introducing several ways of interacting with property attributes directly as well as introducing new attributes. It’s now possible to create properties that behave the same way as built-in JavaScript properties.
Common attributes
There are two property attributes shared between data and accessor properties. One is [[Enumerable]] which determines whether we can iterated over the property, and second is [[Configurable]] which determines whether the property can be changed. By default all properties on an object are both enumerable and configurable.
Object.defineProperty() method is used to change property attributes.
var person1 = {
name: "shahzad"
};
Object.defineProperty(person1, "name", {
enumerable: false
});
console.log("name" in person1); //returns true
console.log(person1.propertyIsEnumerable("name")); //returns false
This is alternative method for doing the same thing;
var properties = Object.keys(person1);
console.log(properties.length); //returns 0
Object.defineProperty(person1,"name",{
configurable:false
});
delete person1.name //try to delete the property, returns false
console.log("name" in person1); //return true
Data Property Attributes
There are two additional attributes in data property that accessor do not. The first is [[Value]], which holds the property value. This attribute is filled in automatically when we create a property on an object. The second is [[Writable]], which is Boolean value indicating whether the property can be written to. By default, all properties are writable unless we specify otherwise.
With these two additional attributes, we can define a data property using Object.defineProperty() even if the property does not already exist.
var person1 = {};
Object.defineProperty(person1, "name", {
value: "shahzad",
enumerable: true,
configurable: true,
writable: true
});
Object.defineProperty() will first check to see if the property exists, if it doesn’t it will create a new one. It is important to specify all the attributes because Boolean attributes default to false. For example;
Object.defineProperty(person1, "name", {
value: "shahzad",
});
enumberalbe, configurable and writable will be automatically false.
Accessor Property Attributes
There are two additional attributes, [[Get]] and [[Set]]. There is no value stored for getters/setters so there is no need for [[Value]] or [[Writable]] attributes.
Defining Multiple Properties
We can define multiple properties, If we use Object.defineProperties() instead of Object.defineProperty().
var person = {};
Object.defineProperties(person, {
_name: {
value: "shahzad",
enumerable:true,
configurable:true,
writable: true
},
//accessor property
name: {
get: function() {
console.log("reading name");
return this._name;
},
set: function(valule) {
console.log("Setting name to %s", value);
this._name = value;
},
enumerable: true,
configurable: true
}
});
Retrieving Property Attributes
Object.getOwnPropertyDescriptor() can be used to fetch property attributes. This method works only on own properties. For example, this code create a property and check its attributes;
var person = {
name: "shahzad"
};
var descriptor = Object.getOwnPropertyDescriptor(person, "name");
console.log(descriptor.enumerable); //returns true
console.log(descriptor.configurable); //returns true
console.log(descriptor.writable); //returns true
console.log(descriptor.value); //returns “shahzad”
Preventing Object Modification
[[Extensible]] attribute is a Boolean value indicating if the object can be modified. By default all objects are extensible, meaning new properties can be added.
To prevent extension, first method is to use Object.preventExtensions().
var person = {
name: "shahzad"
};
console.log(Object.isExtensible(person)); //returns true
Object.preventExtensions(person); //prevent extension
console.log(Object.isExtensible(person)); //returns false
//modify object
person.sayName = function(){
console.log(this.name);
};
console.log("sayName" in person); //return false, object is not modified
Attempting to add a property/method to a nonextensible object will throw an error in strict mode. In nonrestrict mode, the operation fails silently.
To prevent extension, second method is to use Object.seal().
var person = {
name: "shahzad"
};
console.log(Object.isExtensible(person)); //returns true
console.log(Object.isSealed(person)); //returns false
Object.seal(person) //Seal the object
console.log(Object.isExtensible(person)); //returns false
console.log(Object.isSealed(person)); //returns true
//try to modify object
person.sayName = function(){
console.log(this.name);
};
console.log("sayName" in person); //returns false
person.name = "Ali";
console.log(person.name); //returns “Ali”
delete person.name; //returns false
console.log("name" in person); //returns true
console.log(person.name); //returns “Ali”
var descriptor =
Object.getOwnPropertyDescriptor(person, "name");
console.log(descriptor.configurable); //returns false
Be sure to use strict mode with sealed objects so we’ll get an error when someone tries to use the object incorrectly.
The last way to create a nonextensible object is to freeze it. In essence, a frozen object is a sealed object where data properties are also read-only. Frozen objects can not become unfrozen so they remain in the state when they become frozen. We can use Object.freeze() to freeze an object and Object.isFrozen() to determine if an object is frozen.
var person = {
name: "shahzad"
};
console.log(Object.isExtensible(person)); //returns true
console.log(Object.isSealed(person)); //returns false
console.log(Object.isFrozen(person)); //returns false
Object.freeze(person); //freeze object, data properties becomes read-only
console.log(Object.isExtensible(person)); //returns false
console.log(Object.isSealed(person)); //returns true
console.log(Object.isFrozen(person)); //returns true
//try to modify object
person.sayName = function(){
console.log(this.name);
};
console.log("SayName" in person); //returns false
person.name = "Ali";
console.log(person.name); //returns shahzad
delete person.name; //returns false
console.log("name" in person); //returns true
console.log(person.name); //returns shahzad
var descriptor =
Object.getOwnPropertyDescriptor(person, "name");
console.log(descriptor.configurable); //returns false
console.log(descriptor.writable); //returns false