Back to Home

ECMAscript 5: Objects and Properties

ECMAScript 5 · JavaScript

ECMAscript 5: Objects and Properties

Original author: John Resig
  • Transfer
ECMAScript 5 goes its own way. Resurrected from the ashes of ECMAScript 4, which was squeezed back to ECMAScript 3.1, which was then again called ECMAScript 5 (in more detail ) - it comes with a new layer of functionality built on the basis of our beloved ECMAScript 3.

Announced several new APIs included in the specification, but the most interesting functionality lies in the code of objects and properties. This new code makes it possible to significantly influence how users can interact with objects, allowing you to provide getters and setters, prevent listing, manipulation, or deletion, and even prevent the addition of new properties. In short: You will be able to replicate and extend your existing set of JavaScript APIs (e.g. DOM) using JavaScript (without using anything else).

Very good news: These features should appear in all major browsers. All major browser vendors have worked on this specification and agreed to implement it in their products. The exact dates are not yet clear, but the likelihood of an early implementation is high.

Apparently, there is no full implementation of ES5 for today, but several projects are already in the process of implementation. At the same time, you can read the ECMAScript 5 specification (PDF - in this article I discuss pages 107-109), or watch the latest discussions of the ECMAScript team on Google.
Note: I show some simple, sample, implementations of these methods to illustrate how they might function. Almost all of them require other, new methods for correct operation - and they are not implemented in accordance with the specification 100% (for example, there are no error checks).

Objects


A new feature of ECMAScript 5 - the extensibility of objects can now be switched. Disabling extensibility can prevent the addition of new properties to the object.

ES5 provides two ways to manipulate and verify the extensibility of objects:

Object.preventExtensions (obj), Object.isExtensible (obj)

preventExtensionsblocks the object and prevents the creation of any new properties of the object in the future. isExtensible- the ability to determine whether we are currently expanding the object or not.

Usage example:
var obj = {};
obj.name = "John";
print( obj.name );
// John

print( Object.isExtensible( obj ) );
// true

Object.preventExtensions( obj );

obj.url = "http://ejohn.org/"; // Exception in strict mode

print( Object.isExtensible( obj ) );
// false

* This source code was highlighted with Source Code Highlighter.

Properties and Descriptors


The properties have been completely revised. They are no longer the simple meanings associated with an object - you now have full control over how they can act. Greater strength, however, is associated with increased complexity.

Properties of objects are divided into two parts.

The content of a property can be defined in two ways: value (property-data are traditional properties that we know and love in ECMAScript 3) or getter and setter (properties are “accessors” - we know them from some modern browsers, such as WebKit and gecko.
  • Value Stores the value of a property.
  • Get. This function is called every time a property value is accessed.
  • Set. This function is called every time the property value changes.

In addition, the properties may be ...
  • Mutable. If false, then the value of this property cannot be changed.
  • Configurable. If false, any attempts to delete a property or change its attributes (writability, configurability, or enumeration) will fail.
  • Enumerated. If true, then the property will be iterated when the user does for (var prop in obj)(or something like that).

In total, these various attributes make up the property descriptor. For example, a simple descriptor might look something like this:
{
 value: "test",
 writable: true,
 enumerable: true,
 configurable: true
}

* This source code was highlighted with Source Code Highlighter.

Three attributes (writable, enumerable and configurable) are optional and all are true by default. Thus, for a property, you definitely need to provide only either a value, or a getter and setter.

You can use the new method Object.getOwnPropertyDescriptorto obtain this information for an existing property of an object.

Object.getOwnPropertyDescriptor (obj, prop)

This method allows access to the property descriptor. It is the only way to get this information (in other words, the descriptors are not at the disposal of the user - they do not exist as visible property attributes, they are stored internally in the ECMAScript engine).

Usage example:
var obj = { foo: "test" };
print(JSON.stringify(
 Object.getOwnPropertyDescriptor( obj, "foo" )
));
// {"value": "test", "writable": true,
// "enumerable": true, "configurable":true}

* This source code was highlighted with Source Code Highlighter.


Method Object.defineProperty (obj, prop, desc)

This method allows you to define a new property for an object (or change the handle of an existing property). This method takes a property descriptor and uses it to initialize (or update) the property.

Usage example:
var obj = {};
Object.defineProperty( obj, "value", {
 value: true,
 writable: false,
 enumerable: true,
 configurable: true
});

(function(){
 var name = "John";
 
 Object.defineProperty( obj, "name", {
  get: function(){ return name; },
  set: function(value){ name = value; }
 });
})();

print( obj.value )
// true

print( obj.name );
// John

obj.name = "Ted";
print( obj.name );
// Ted

for ( var prop in obj ) {
 print( prop );
}
// value
// name

obj.value = false; // Exception if in strict mode

Object.defineProperty( obj, "value", {
 writable: true,
 configurable: false
});

obj.value = false;
print( obj.value );
// false

delete obj.value; // Exception

* This source code was highlighted with Source Code Highlighter.


Object.defineProperty is the base method of the new version of ECMAScript. Virtually all other important features depend on the existence of this method.

Object.defineProperties (obj, props)

Defines several properties for an object at a time (instead of defining each individually).

Sample implementation:
Object.defineProperties = function( obj, props ) {
 for ( var prop in props ) {
  Object.defineProperty( obj, prop, props[prop] );
 }
};

* This source code was highlighted with Source Code Highlighter.


Usage example:
var obj = {};
Object.defineProperties(obj, {
 "value": {
  value: true,
  writable: false
 },
 "name": {
  value: "John",
  writable: false
 }
});

* This source code was highlighted with Source Code Highlighter.

Property descriptors (and related methods) is perhaps the most important new feature of ECMAScript 5. This gives developers the ability to very precisely control their objects, prevent unwanted modifications, and also support a single web-compatible API.

New opportunities



Some interesting new features have been introduced into the language using the extensions described above.

The following two methods are very useful for collecting arrays of all the properties of an object.

Object.keys (obj)

Returns an array of strings representing the names of all enumerated properties of the object. Its behavior coincides with the behavior of the method from the Prototype.js library.

Sample implementation:
Object.keys = function( obj ) {
 var array = new Array();
 for ( var prop in obj ) {
  array.push( prop );
 }
 return array;
};

* This source code was highlighted with Source Code Highlighter.


Usage example:
var obj = { name: "John", url: "http://ejohn.org/" };
print( Object.keys(obj).join(", ") );
// name, url


* This source code was highlighted with Source Code Highlighter.


Object.getOwnPropertyNames (obj)

Almost identical to Object.keys, but returns the names of all properties of the object (and not just enumerated ones).

An example implementation is not possible in ECMAScript 3, since non-enumerable properties cannot be listed. The return value and usage are identical to Object.keys.

Object.create (proto, props)

Creates a new object, the prototype of which is proto, and whose properties are set using Object.defineProperties (props).

A simple implementation would look like this (requires a new Object.defineProperties method):
Object.create = function( proto, props ) {
 var obj = new Object();
 obj.__proto__ = proto;
 if ( typeof props !== "undefined" ) {
  Object.defineProperties( obj, props );
 }
 
 return obj;
};

* This source code was highlighted with Source Code Highlighter.


Note: The above code uses the Mozilla-specific _proto_ property. This property gives you access to the internal prototype of the object - and also allows you to set its value. In ES5, the Object.getPrototypeOf method allows you to access this property, but does not allow it to be installed - therefore, the method above cannot be implemented in a general manner compatible with ES.

I discussed Object.getPrototypeOf earlier , so I will not discuss it here again.

Usage example:
function User(){}
User.prototype.name = "Anonymous";
User.prototype.url = "http://google.com/";
var john = Object.create(new User(), {
 name: { value: "John", writable: false },
 url: { value: "http://google.com/" }
});

print( john.name );
// John

john.name = "Ted"; // Exception if in strict mode

* This source code was highlighted with Source Code Highlighter.


Object.seal (obj), Object.isSealed (obj)

Sealing an object prevents the code from deleting or changing the descriptors of any properties of the object - and prevents the addition of new properties.

Implementation Example:
Object.seal = function( obj ) {
 var props = Object.getOwnPropertyNames( obj );
 
 for ( var i = 0; i < props.length; i++ ) {
  var desc = Object.getOwnPropertyDescriptor( obj, props[i] );
  
  desc.configurable = false;
  Object.defineProperty( obj, props[i], desc );
 }
 
 return Object.preventExtensions( obj );
};

* This source code was highlighted with Source Code Highlighter.


You would prefer to seal the object if you want the set of its existing properties to remain intact, without providing new additions, but at the same time allowing the user to edit their values.

Object.freeze (obj), Object.isFrozen (obj)

Freezing an object is almost identical to sealing, but with the addition that the properties become unchanged.

Implementation Example:
Object.freeze = function( obj ) {
 var props = Object.getOwnPropertyNames( obj );
 
 for ( var i = 0; i < props.length; i++ ) {
  var desc = Object.getOwnPropertyDescriptor( obj, props[i] );
  
  if ( "value" in desc ) {
   desc.writable = false;
  }
  
  desc.configurable = false;
  Object.defineProperty( obj, props[i], desc );
 }
 
 return Object.preventExtensions( obj );
};

* This source code was highlighted with Source Code Highlighter.


Freezing an object is the ultimate form of locking. After an object has been frozen, it cannot be thawed - it cannot be changed in any form. This is the best way to make sure that your objects will remain exactly as you left them - for an indefinite period.

Together, these changes are very interesting, they give you an unprecedented level of control over the objects that you create. The great aspect is that you can use these features to create larger and more complex functions in pure ECMAScript (such as building new DOM modules, or moving most of the browser APIs into pure JavaScript). And since all browsers have JavaScript on board, this is absolutely what we look forward to.

- translator's note: descriptor is translated as a descriptor, although it can be translated as a descriptor. getter and setter do not have the same short and capacious definitions in Russian (the recipient of the value and the installer of the value), therefore, the jargon getter and setter are used.

Read Next