Book Image

Object-Oriented JavaScript - Second Edition

Book Image

Object-Oriented JavaScript - Second Edition

Overview of this book

JavaScript is the behavior, the third pillar in today's paradigm that looks at web pages as something that consists of clearly distinguishable parts: content (HTML), presentation (CSS) and behavior (JavaScript). Using JavaScript, you can create not only web pages but also desktop widgets, browser and application extensions, and other pieces of software. It's a pretty good deal: you learn one language and then code all kinds of different applications. While there's one chapter specifically dedicated to the web browser environment including DOM, Events and AJAX tutorials, the rest is applicable to the other environments Many web developers have tried coding or adopting some bits of JavaScript, but it is time to "man up" and learn the language properly because it is the language of the browser and is, virtually, everywhere. This book starts from zero, not assuming any prior JavaScript programming knowledge and takes you through all the in-depth and exciting futures hidden behind the facade. Once listed in the "nice to have" sections of job postings, these days the knowledge of JavaScript is a deciding factor when it comes to hiring web developers. After reading this book you'll be prepared to ace your JavaScript job interview and even impress with some bits that the interviewer maybe didn't know. You should read this book if you want to be able to take your JavaScript skills to a new level of sophistication.
Table of Contents (19 chapters)
Object-Oriented JavaScript Second Edition
Credits
About the Authors
About the Reviewer
www.PacktPub.com
Preface
Built-in Functions
Regular Expressions
Index

Object


Object() is a constructor that creates objects, for example:

> var o = new Object();

This is the same as using the object literal:

> var o = {}; // recommended

You can pass anything to the constructor and it will try to guess what it is and use a more appropriate constructor. For example, passing a string to new Object() will be the same as using the new String() constructor. This is not a recommended practice (it's better to be explicit than let guesses creep in), but still possible.

> var o = new Object('something');
> o.constructor;
function String() { [native code] }
> var o = new Object(123);
> o.constructor;
function Number() { [native code] }

All other objects, built-in or custom, inherit from Object. So, the properties and methods listed in the following sections apply to all types of objects.

Members of the Object constructor

Have a look at the following table:

Property/method

Description

Object.prototype

The prototype of all objects (also an object itself). Anything you add to this prototype will be inherited by all other objects, so be careful.

> var s = new String('noodles');
> Object.prototype.custom = 1;
1
> s.custom;
1

The Object.prototype members

Have a look at the following table:

Property/method

Description

constructor

Points back to the constructor function used to create the object, in this case, Object.

> Object.prototype.constructor === Object;
true
> var o = new Object();
> o.constructor === Object;
true

toString(radix)

Returns a string representation of the object. If the object happens to be a Number object, the radix parameter defines the base of the returned number. The default radix is 10.

> var o = {prop: 1};
> o.toString();
"[object Object]"
> var n = new Number(255);
> n.toString();
"255"
> n.toString(16);
"ff"

toLocaleString()

The same as toString(), but matching the current locale. Meant to be customized by objects, such as Date(), Number(), and Array() and provide locale-specific values, such as different date formatting. In the case of Object() instances as with most other cases, it just calls toString().

In browsers, you can figure out the language using the property language (or userLanguage in IE) of the navigator BOM object:

> navigator.language;
"en-US
"

valueOf()

Returns a primitive representation of this, if applicable. For example, Number objects return a primitive number and Date objects return a timestamp. If no suitable primitive makes sense, it simply returns this.

> var o = {};
> typeof o.valueOf();
"object"
> o.valueOf() === o;
true
> var n = new Number(101);
> typeof n.valueOf();
"number"
> n.valueOf() === n;
false
> var d = new Date();
> typeof d.valueOf();
"number"
> d.valueOf();
1357840170137

hasOwnProperty(prop)

Returns true if a property is an own property of the object, or false if it was inherited from the prototype chain. Also returns false if the property doesn't exist.

> var o = {prop: 1};
> o.hasOwnProperty('prop');
true
> o.hasOwnProperty('toString');
false
> o.hasOwnProperty('fromString');
false

isPrototypeOf(obj)

Returns true if an object is used as a prototype of another object. Any object from the prototype chain can be tested, not only the direct creator.

> var s = new String('');
> Object.prototype.isPrototypeOf(s);
true
> String.prototype.isPrototypeOf(s);
true
> Array.prototype.isPrototypeOf(s);
false

propertyIsEnumerable(prop)

Returns true if a property shows up in a for-in loop.

> var a = [1, 2, 3];
> a.propertyIsEnumerable('length');
false
> a.propertyIsEnumerable(0);
true

ECMAScript 5 additions to Object

In ECMAScript 3 all object properties can be changed, added, or deleted at any time, except for a few built-in properties (for example, Math.PI). In ES5 you have the ability to define properties that cannot be changed or deleted—a privilege previously reserved for built-ins. ES5 introduces the concept of property descriptors that give you tighter control over the properties you define.

Think of a property descriptor as an object that specifies the features of a property. The syntax to describe these features is a regular object literal, so property descriptors have properties and methods of their own, but let's call them attributes to avoid confusion. The attributes are:

  • value – what you get when you access the property

  • writable – can you change this property

  • enumerable – should it appear in for-in loops

  • configurable – can you delete it

  • set() – a function called any time you update the value

  • get() – called when you access the value of the property

Further, there's a distinction between data descriptors (you define the properties enumerable, configurable, value, and writable) and accessor descriptors (you define enumerable, configurable, set(), and get()). If you define set() or get(), the descriptor is considered an accessor and attempting to define value or writable will raise an error.

Defining a regular old school ES3-style property:

var person = {};
person.legs = 2;

The same using an ES5 data descriptor:

var person = {};
Object.defineProperty(person, "legs", {
  value: 2,
  writable: true,
  configurable: true,
  enumerable: true
});

The value of value if set to undefined by default, all others are false. So, you need to set them to true explicitly if you want to be able to change this property later.

Or, the same property using an ES5 accessor descriptor:

var person = {};
Object.defineProperty(person, "legs", {
  set: function (v) {this.value = v;},
  get: function (v) {return this.value;},
  configurable: true,
  enumerable: true
});
person.legs = 2;

As you can see property descriptors are a lot more code, so you only use them if you really want to prevent someone from mangling your property, and also you forget about backwards compatibility with ES3 browsers because, unlike additions to Array.prototype for example, you cannot "shim" this feature in old browsers.

And the power of the descriptors in action (defining a nonmalleable property):

> var person = {};
> Object.defineProperty(person, 'heads', {value: 1});> person.heads = 0;
0
> person.heads;
1
> delete person.heads;
false
> person.heads;
1

The following is a list of all ES5 additions to Object:

Property/method

Description

Object.getPrototypeOf(obj)

While in ES3 you have to guess what is the prototype of a given object using the method Object.prototype.isPrototypeOf(), in ES5 you can directly ask "Who is your prototype?"

> Object.getPrototypeOf([]) === 
    Array.prototype;
true

Object.create(obj, descr)

Discussed in Chapter 6, Inheritance. Creates a new object, sets its prototype and defines properties of that object using property descriptors (discussed earlier).

> var parent = {hi: 'Hello'};
> var o = Object.create(parent, {
    prop: {
      value: 1
    }
  });
> o.hi;
"Hello"

It even lets you create a completely blank object, something you cannot do in ES3.

> var o = Object.create(null);
> typeof o.toString;
"undefined"

Object.getOwnPropertyDescriptor(obj, property)

Allows you to inspect how a property was defined. You can even peek into the built-ins and see all these previously hidden attributes.

> Object.getOwnPropertyDescriptor(Object.prototype, 'toString');
Object
configurable: true
enumerable: false
value: function toString() { [native code] }
writable: true

Object.getOwnPropertyNames(obj )

Returns an array of all own property names (as strings), enumerable or not. Use Object.keys() to get only enumerable ones.

> Object.getOwnPropertyNames(Object.prototype);
["constructor", "toString", "toLocaleString", "valueOf",...

Object.defineProperty(obj, descriptor)

Defines a property of an object using a property descriptor. See the discussion preceding this table.

Object.defineProperties(obj, descriptors)

The same as defineProperty(), but lets you define multiple properties at once.

> var glass = Object.defineProperties({}, {
    "color": {
      value: "transparent",
      writable: true
    },
    "fullness": {
      value: "half",
      writable: false
    }
  });

> glass.fullness;
"half"

Object.preventExtensions(obj)

Object.isExtensible(obj)

preventExtensions() disallows adding further properties to an object and isExtensible() checks whether you can add properties.

> var deadline = {};
> Object.isExtensible(deadline);
true
> deadline.date = "yesterday";
"yesterday"
> Object.preventExtensions(deadline);
> Object.isExtensible(deadline);
false
> deadline.date = "today";
"today"
> deadline.date;
"today"

Attempting to add properties to a non-extensible object is not an error, but simply doesn't work:

> deadline.report = true;
> deadline.report;
undefined

Object.seal(obj)

Object.isSealed(obj )

seal() does the same as preventExtensions () and additionally makes all existing properties non-configurable.

This means you can change the value of an existing property, but you cannot delete it or reconfigure it (using defineProperty() won't work). So you cannot, for example, make an enumerable property non-enumerable.

Object.freeze(obj)

Object.isFrozen(obj)

Everything that seal() does plus prevents changing the values of properties.

> var deadline = Object.freeze(
    {date: "yesterday"});
> deadline.date = "tomorrow";
> deadline.excuse = "lame";
> deadline.date;
"yesterday"
> deadline.excuse;
undefined
> Object.isSealed(deadline);
true

Object.keys(obj)

An alternative to a for-in loop. Returns only own properties (unlike for-in). The properties need to be enumerable in order to show up (unlike Object.getOwnPropertyNames()). The return value is an array of strings.

> Object.prototype.customProto = 101;
> Object.getOwnPropertyNames(
    Object.prototype);
["constructor", "toString", ..., "customProto"]
> Object.keys(Object.prototype);
["customProto"]
> var o = {own: 202};
> o.customProto; 
101
> Object.keys(o);
["own"]