JavaScript – Querying and Setting Properties

To obtain the value of a property, use the dot ( .) or square bracket ( []) operators described in Property Access Expressions. The left-hand side should be an expression
whose value is an object. If using the dot operator, the right-hand
must be a simple identifier that names the property. If using square
brackets, the value within the brackets must be an expression that
evaluates to a string that contains the desired property
name:

var author = book.author;      // Get the "author" property of the book.
var name = author.surname      // Get the "surname" property of the author.
var title = book["main title"] // Get the "main title" property of the book.

To create or set a property, use a dot or square brackets as you
would to query the property, but put them on the left-hand side of an
assignment expression:

book.edition = 6;                   // Create an "edition" property of book.
book["main title"] = "ECMAScript";  // Set the "main title" property.

In ECMAScript 3, the identifier that follows the dot operator
cannot be a reserved word: you cannot write o.for or o.class, for example, because for is a language keyword and class is reserved for future use. If an
object has properties whose name is a reserved word, you must use
square bracket notation to access them: o["for"] and o["class"]. ECMAScript 5 relaxes this
restriction (as do some implementations of ECMAScript
3) and allows reserved words to follow the dot.

When using square bracket notation, we’ve said that the
expression inside the square brackets must evaluate to a string. A
more precise statement is that the expression must evaluate to a
string or a value that can be converted to a string. In Chapter 7, for example, we’ll see that it is common to use
numbers inside the square brackets.

Objects As Associative Arrays

As explained above, the following two JavaScript expressions
have the same value:

object.property
object["property"]

The first syntax, using the dot and an identifier, is like the
syntax used to access a static field of a struct or object in C or
Java. The second syntax, using square brackets and a string, looks
like array access, but to an array indexed by strings rather than by
numbers. This kind of array is known as an associative array (or
hash or map or dictionary). JavaScript objects are associative
arrays, and this section explains why that is important.

In C, C++, Java, and similar strongly typed languages, an
object can have only a fixed number of properties, and the names of
these properties must be defined in advance. Since JavaScript is a
loosely typed language, this rule does not apply: a program can
create any number of properties in any object. When you use the
. operator to access a property
of an object, however, the name of the property is expressed as an
identifier. Identifiers must be typed literally into your JavaScript
program; they are not a datatype, so they cannot be manipulated by
the program.

On the other hand, when you access a property of an object
with the [] array notation, the
name of the property is expressed as a string. Strings are
JavaScript datatypes, so they can be manipulated and created while a
program is running. So, for example, you can write the following
code in JavaScript:

var addr = "";
for(i = 0; i < 4; i++)
    addr += customer["address" + i] + '\n';

This code reads and concatenates the address0, address1, address2, and address3 properties of the customer object.

This brief example demonstrates the flexibility of using array
notation to access properties of an object with string expressions.
The code above could be rewritten using the dot notation, but there
are cases in which only the array notation will do. Suppose, for
example, that you are writing a program that uses network resources
to compute the current value of the user’s stock market investments.
The program allows the user to type in the name of each stock she
owns as well as the number of shares of each stock. You might use an
object named portfolio to hold
this information. The object has one property for each stock. The
name of the property is the name of the stock, and the property
value is the number of shares of that stock. So, for example, if a
user holds 50 shares of stock
in IBM, the portfolio.ibm
property has the value 50.

Part of this program might be a function for adding a new
stock to the portfolio:

function addstock(portfolio, stockname, shares) {
    portfolio[stockname] = shares;
}

Since the user enters stock names at runtime, there is no way
that you can know the property names ahead of time. Since you can’t
know the property names when you write the program, there is no way
you can use the . operator to
access the properties of the portfolio object. You can use the [] operator, however, because it uses a
string value (which is dynamic and can change at runtime) rather
than an identifier (which is static and must be hardcoded in the
program) to name the property.

Chapter 5 introduced the for/in loop (and we’ll see it again
shortly in Enumerating Properties). The power of this
JavaScript statement becomes clear when you consider its use with
associative arrays. Here’s how you’d use it when computing the total
value of a portfolio:

function getvalue(portfolio) {
    var total = 0.0;
    for(stock in portfolio) {           // For each stock in the portfolio:
        var shares = portfolio[stock];  //   get the number of shares
        var price = getquote(stock);    //   look up share price
        total += shares * price;        //   add stock value to total value
    }
    return total;                       // Return total value.
}

Inheritance

JavaScript objects have a set of “own properties,” and they
also inherit a set of properties from their prototype object. To
understand this, we must consider property access in more detail.
The examples in this section use the inherit() function from Example 6-1 in order to create objects with specified
prototypes.

Suppose you query the property x in the object o. If o
does not have an own property with that name, the prototype object
of o is queried for the property
x. If the prototype object does
not have an own property by that name, but has a prototype itself,
the query is performed on the prototype of the prototype. This
continues until the property x is
found or until an object with a null prototype is searched. As you can
see, the prototype attribute of an object
creates a chain or linked list from which properties are inherited.

var o = {}            // o inherits object methods from Object.prototype
o.x = 1;              // and has an own property x.
var p = inherit(o);   // p inherits properties from o and Object.prototype
p.y = 2;              // and has an own property y.
var q = inherit(p);   // q inherits properties from p, o, and Object.prototype
q.z = 3;              // and has an own property z.
var s = q.toString(); // toString is inherited from Object.prototype    
q.x + q.y             // => 3: x and y are inherited from o and p

Now suppose you assign to the property x of the object o. If o
already has an own (noninherited) property named x, then the assignment simply changes the
value of this existing property. Otherwise, the assignment creates a
new property named x on the
object o. If o previously inherited the property
x, that inherited property is now
hidden by the newly created own property with the same
name.

Property assignment examines the prototype chain to determine
whether the assignment is allowed. If o inherits a read-only property named
x, for example, then the
assignment is not allowed. (Details about when a property may be set
are in Property Access Errors.) If the assignment
is allowed, however, it always creates or sets a property in the
original object and never modifies the prototype chain. The fact
that inheritance occurs when querying properties but not when
setting them is a key feature of JavaScript because it allows us to
selectively override inherited properties:

var unitcircle = { r:1 };     // An object to inherit from
var c = inherit(unitcircle);  // c inherits the property r
c.x = 1; c.y = 1;             // c defines two properties of its own
c.r = 2;                      // c overrides its inherited property
unitcircle.r;                 // => 1: the prototype object is not affected

There is one exception to the rule that a property assignment
either fails or creates or sets a property in the original object.
If o inherits the property
x, and that property is an
accessor property with a setter method (see Property Getters and Setters), then that setter method is called
rather than creating a new property x in o.
Note, however, that the setter method is called on the object
o, not on the prototype object
that defines the property, so if the setter method defines any
properties, it will do so on o,
and it will again leave the prototype chain unmodified.

Property Access Errors

Property access expressions do not always return or set a
value. This section explains the things that can go wrong when you
query or set a property.

It is not an error to query a property that does not exist. If
the property x is not found as an
own property or an inherited property of o, the property access expression o.x evaluates to undefined. Recall that our book object has
a “sub-title” property, but not a “subtitle” property:

book.subtitle;    // => undefined: property doesn't exist

It is an error, however, to attempt to query a property of an
object that does not exist. The null and undefined values have no properties, and
it is an error to query properties of these values. Continuing the
above example:

// Raises a TypeError exception. undefined doesn't have a length property
var len = book.subtitle.length;

Unless you are certain that both book and book.subtitle are (or behave like)
objects, you shouldn’t write the expression book.subtitle.length, since it might raise
an exception. Here are two ways to guard against this kind of
exception:

// A verbose and explicit technique
var len = undefined;
if (book) {
    if (book.subtitle) len = book.subtitle.length;
}

// A concise and idiomatic alternative to get subtitle length or undefined
var len = book && book.subtitle && book.subtitle.length;

To understand why this idiomatic expression works to prevent
TypeError exceptions, you might want to review the short-circuiting
behavior of the &&
operator in Logical AND (&&).

Attempting to set a property on null or undefined also causes a TypeError, of
course. Attempts to set properties on other values do not always
succeed, either: some properties are read-only and cannot be set,
and some objects do not allow the addition of new properties.
Curiously, however, these failed attempts to set properties usually
fail silently:

// The prototype properties of built-in constructors are read-only.
Object.prototype = 0;  // Assignment fails silently; Object.prototype unchanged

This historical quirk of JavaScript is rectified in the strict
mode of ECMAScript 5. In strict mode, any failed attempt to set a
property throws a TypeError exception.

The rules that specify when a property assignment succeeds and
when it fails are intuitive but difficult to express concisely. An
attempt to set a property p of an
object o fails in these
circumstances:

  • o has an own property
    p that is read-only: it is
    not possible to set read-only properties. (See the defineProperty() method, however, for
    an exception that allows configurable read-only properties to be
    set.)

  • o has an inherited
    property p that is read-only:
    it is not possible to hide an inherited read-only property with
    an own property of the same name.

  • o does not have an own
    property p; o does not inherit a property p with a setter method, and o’s
    extensible attribute (see The extensible Attribute) is false. If p does not already exist on o, and if there is no setter method to
    call, then p must be added to
    o. But if o is not extensible, then no new
    properties can be defined on it.

Comments are closed.