ikerhurtado.com
You're in
Iker Hurtado's pro blog
Developer | Entrepreneur | Investor
Software engineer (entrepreneur and investor at times). These days doing performant frontend and graphics on the web platform at Barcelona Supercomputing Center

Going deeply into Javascript: Objects

15 May 2015   |   iker hurtado  
Share on Twitter Share on Google+ Share on Facebook
In this post I write down some notes on the broad topic of object handling in JavaScript. It's not intended to be an exhaustive or complete resource about this issue.

In JavaScript, Objects are basically maps from strings to values. A pair (key,value) in an object is called a property. The key is always a text string and the value can be any JavaScript value. When the value is a function this property is called method.

In addition to normal properties (or named data properties) there are two more kinds of properties:

- Accessors (or named accessor properties): They are special methods whose invocations look like reading or writing properties but allow to compute the values. They are virtual properties, a kind of getters and setters.

- Internal properties: They are implementation properties and are not directly accessible from JavaScript, but there might be indirect ways of accessing them. The specification writes their keys in brackets ([[Key]]).

Object literals allow to directly create plain objects (direct instances of Object).

Accessing Properties

The dot operator provides a compact syntax for accessing properties. The property keys must be identifiers (consult “Legal Identifiers” on page 60). If you want to read or write properties with arbitrary names, you need to use the bracket operator

In contrast to the dot operator, the bracket operator allows to refer to a property via an expression. In addtion, it's worthy to note that the bracket operator converts its value to string.

var key = 100;
obj[key]  // -> obj['100']
>'hundred'

The delete operator

The delete operator allows to remove a property (the whole key-value pair) from an object. This way:

delete obj.pos // or 
delete obj['pos']

delete returns false if the property is its own, but cannot be deleted. It returns true in all other cases.

The this keyword in methods

When it comes to define methods in an object, we can use this inside the method to refer to the object itself. this is an implicit parameter. It's the receiver of the method call.

In function nested inside methods the this keyword will be shadowed. In normal functions (no method) the value of this is undefined.

JavaScript Object Oriented Programming is prototype-based

JavaScript is a prototype-based language that contains no class statement. Instead, JavaScript uses functions as classes.

The inheritance between two objects in JavaScript is implemented by the prototype relationship: an object have another object as its prototype. This means that the former object inherits all of its prototype’s properties. This is a powerful feature: it lets you modify something's prototype at any time, which means you can add extra methods to existing objects at runtime. In addition, this can be used to add things to the prototype of built-in JavaScript objects.

An object links its prototype via the internal property [[Prototype]]. Every object has this property; if an object inherits no properties then [[Prototype]] is null . The chain of objects connected by the [[Prototype]] property is called the prototype chain.

When an object property is accessed, JavaScript starts the search for it in that object and continues with its prototype, the prototype’s prototype, and so on. The same mechanics is used when a method is called: the value of this is always the object where the search for the method began, not where the method was found. That allows the method to access all of the properties of the prototype chain.

An inherited property in an object can be overridden by an own object property. In the prototype chain search, the closest property is found first. It hides the latter property, which can’t be accessed anymore.

The common pattern in JavaScript prototypal inheritance is this: the data resides in the first object of a prototype chain, while methods reside in later objects.

In practice

The simpler way to create a new object whose prototype is other is this:

var obj = Object.create(protoObj); // Creates obj inheriting the props pf protoObj
obj.newKey = 'value';  // We can add a new own property

Also, it's very easy to get the prototype of an object:

Object.getPrototypeOf(obj);  // returns protoObj
Some JavaScript engines have a special property for accessing the prototype of an object: __proto__ . It brings direct access to [[Prototype]] to the language.

Own properties vs inherited property

An own property is stored directly in the object. In contrast, an inherited property is stored in some of its prototypes.

While getting a property considers the complete prototype chain of an object, setting and deleting ignores inheritance and affects only own properties. It makes sense: the prototype properties can be shared by several objects.

The function to check if an object has an own property:

obj.hasOwnProperty('newKey');

Property listing

There are Object methods to facilitate the own property listing: Object.getOwnPropertyNames(obj) returns the keys of all own properties and Object.keys(obj) returns the keys of all enumerable own properties.

The for-in loop iterates over the keys of all enumerable properties, including inherited ones; this way:

for (var x in obj)

Enumerability

As for enumerability, the general rule is that properties created by the system are nonenumerable, while properties created by users are enumerable.

The operations affected by enumerability are: the for-in loop, and the functions Object.keys() and JSON.stringify().

Property attributes and descriptors

The property state (data and metadata) is stored in attributes. They are store in a similar way as properties in objects are. Attribute keys are often written in double brackets. Attributes matter for normal properties and for accessors.

The normal properties have four attributes:

  • [[Value]] (default undefined): Store the property value.
  • [[Writable]] (default false): Holds a boolean indicating whether the value of a property can be changed.
  • [[Enumerable]] (default false): Holds a boolean. If the value is false, the property is hidden for some operations.
  • [[Configurable]] (default false): Holds a boolean. If the value is false, it's not possible to delete this property, change any of its attributes (except [[Value]]), or convert it from a data property to an accessor property or vice versa. In other words, it controls the writability of a property metadata.

Property descriptors are objects that encodes the attributes of a property. They are useful for working programmatically with attributes. Example:

{
value: 45,
writable: true,
enumerable: true,
configurable: false
}

We can get and set property descriptors to an object by this Object methods: Object.getOwnPropertyDescriptor(obj, propKey) and Object.defineProperty(obj, propKey, propDesc).

In javaScript, there are three levels of protecting an object based in property attributes [[Writable]] and [[Configurable]]: preventing extensions (makes it impossible to add properties to obj), sealing (in addition, it makes all properties unconfigurable) and freezing (in addition, it makes all properties nonwritable).

Accessors (getters and setters)

Since ECMAScript 5, JavaScript allows to write methods whose invocations look like getting or setting properties. That means that properties are not real, they don't have storage space.

var obj = {
  get name() {
    return 'Name';
  },
  set name(value) { // Really it doesn't set anything
    console.log('setter: '+value);
  }
};

The call to this methods is like property access:

obj.name; // run get name() 

obj.name= 'Name'; // run set name(value)

Accessors are inherited from prototypes.

Accessors have the [[Get]] and [[Set]] attributes storing functions instead of [[Value]][[Writable]]. They share [[Enumerable]] and [[Configurable]] with normal properties.

Constructors

Constructors are special functions devoted to produce objects with similar meaning and properties. They are factories of instances; they correspond to classes in other languages.

Going practical, a constructor is a function that is invoked via the new operator. By convention, the names of constructors start with uppercase letters.

The new operator works this way:

• First the behavior is set up: a new object is created and its prototype is set to the prototype property of the constructor.

• Then, the data is set up: the constructor receives that object as the implicit parameter this and adds instance properties.

The constructor does not return a value but merely modifies the this object. It's new that returns the this object to the calling site.

With this in mind, in order to define a constructor we have to provide a way to set the instance properties, for example:

function Sport(pointsPerWin) {
   this.pointsPerWin = pointsPerWin;
}

and a way to add the behavior (by methods):

Sport.prototype.totalPoints = function (wins) {
   return this.pointsPerWin * wins;
};
Different but related to the prototype relationship (inheritance) between objects, the value (object) of the property prototype in a constructor becomes the prototype of all instances of it. Sometimes this is called instance prototype.

By default, each function contains an instance prototype whose property constructor points back to the constructor. Therefore, it's possible to use it to get the constructor of an instance (the constructor property is inherited from the prototype by each instance).

var obj = new ObjClass();
obj.constructor;
In most cases, it's not recommendable to put data properties in objects prototype.

JavaScript does not have language means to label object data (properties) as private or protected. This can be achieved by means of patterns that working around that limitation.

More on this topic in Chapter 17 / Keeping data private of the book Speaking JavaScript.

Neither JavaScript does not have a language implementation for inheritance between constructors. Therefore, we will have to do manual work in order to create a 'subconstructor' that have all of the properties of 'superconstructor'.

More on this topic in Chapter 17 / Layer 4: Inheritance Between Constructors of the book Speaking JavaScript.

A generic method is a method that can be used for objects not inheriting of the prototype that defines it. There are a lot of useful generic methods in prototypes of basic constructors such as Object, Array, String or Date.

POST A COMMENT: