A walk in JavaScript
DAY 3
- Objects explained
- Objects, the big picture
- Syntax
- Object properties attributes (accessors, descriptors)
- Prototype
- Behavior Delegation
- Exotic Objects
- Object built-in methods
- Standard built-in objects
Objects, the big picture
First we went through an introduction of the language, then we jumped into the syntax, grammar and types where we realized that “not everything in javascript is an object!”, Primitives are not objects and they’re immutable! and we also noted the Object type in JS has many flavors ( aka sub-types ), but … what’s an object? what can ww do with them? what’s the use?
Let’s try to grab some insight from the documentation available online starting with the first occurrence on the ECMA2015 spec.
Even though ECMAScript includes syntax for class definitions, ECMAScript objects are not fundamentally class-based such as those in C++, Smalltalk, or Java. Instead objects may be created in various ways including via a literal notation or via constructors which create objects and then execute code that initializes all or part of them by assigning initial values to their properties. Each constructor is a function that has a property named “prototype” that is used to implement prototype-based inheritance and shared properties.
Source: ECMA International
That one was kinda harsh and confusing as the first mention, ain’t it? Even though it has some very important information we’ll see later on related to the prototype but we’re not quite there yet!!
Let’s try with the second mention
An object is a collection of properties and has a single prototype object. The prototype may be the null value.
Source: ECMA International
That’s a little better, some light starts raising in our horizon, but again, not very clear.
What about the third one?
An Object is logically a collection of properties. Each property is either a data property, or an accessor property:
A data property associates a key value with an ECMAScript language value and a set of Boolean attributes.
An accessor property associates a key value with one or two accessor functions, and a set of Boolean attributes. The accessor functions are used to store or retrieve an ECMAScript language value that is associated with the property.
Properties are identified using key values. A property key value is either an ECMAScript String value or a Symbol value. All String and Symbol values, including the empty string, are valid as property keys. A property name is a property key that is a String value. … Property keys are used to access properties and their values. There are two kinds of access for properties: get and set, corresponding to value retrieval and assignment, respectively. The properties accessible via get and set access includes both own properties that are a direct part of an object and inherited properties which are provided by another associated object via a property inheritance relationship. Inherited properties may be either own or inherited properties of the associated object. Each own property of an object must each have a key value that is distinct from the key values of the other own properties of that object.
All objects are logically collections of properties, but there are multiple forms of objects that differ in their semantics for accessing and manipulating their properties. Ordinary objects are the most common form of objects and have the default object semantics. An exotic object is any form of object whose property semantics differ in any way from the default semantics.
Source: ECMA International
Now we’re talking!!!
Great, now we know this:
- Objects are collections of properties,
- Properties come in 2 flavors
- data property
- accessor property
- Properties identifiers ( keys ) come in 2 flavors
- String
Note that even when using variables for Computed property names, unless they’re symbols, they will be ultimately converted into their string representation witch might end up overwriting an existent property ( e.g. resulting in
[object Object]
by using two different variables containing 2 different objects ) - Symbol
No overwriting is happening unless you use a variable containing the same symbol
- String
- Properties values can be of any valid ECMAScript type ( including objects and all subtypes of object )
- Properties have hierarchy
- Own properties
those defined at the current object level
- Inherited properties
those defined at any higher level of the “inheritance” chain ( prototype chain ), and like a Matryoshka, we could have many nesting levels and even circular references!!!
- Own properties
- Objects have sub-types
- Objects come in 4 flavors and they can belong to more than one flavor at a time
- Ordinary
object that has the default behavior for the essential internal methods that must be supported by all objects
- Exotic
object that does not have the default behavior for one or more of the essential internal methods that must be supported by all objects
- Standard
object whose semantics are defined by this specification
- Built-in
object specified and supplied by an ECMAScript implementation
- Ordinary
Are we done? … not even close!!!!! There’s still much to see!
The syntax
In order to create a new object we can use 3 different syntax
- Literal notation a.k.a initializer notation, a.k.a. plain object (
{}
) - Static built-in constructor method (
Object.create(proto, [propertiesObject])
) - Object constructor notation (
new Object()
)
Each one of the forms will provide you different characteristics but all will end up creating the same thing, a n ew object. I listed them in order being the first the most common and the third the least used.
Properties
As we saw before, properties come in two flavors which can be defined in terms of descriptors
, data descriptors
and accessor descriptors
.
A data descriptor is a property that has a value, which may or may not be writable. An accessor descriptor is a property described by a getter-setter pair of functions. A descriptor must be one of these two flavors; it cannot be both.
Both data and accessor descriptors are objects. They share the following optional keys(The default value is in the case of defining properties using
Object.defineProperty()
)
Let’s see how we can granularly define/modify the properties of an object using Object.defineProperty
More insights can be found YDKJS: this & Object Prototypes - Property descriptors
Let’s have some fun
/**
*
* @param {Object} object
* @param {string|number|symbol} key
* @param {string} acceptType [undefined|object|boolean|number|bigint|string|symbol|function|object]
*
* @returns {undefined}
*/
function defineWithType (object, key, acceptType) {
let oldValue = object.key;
Object.defineProperty(object, key, {
get () {
return oldValue;
},
set (newValue) {
if (typeof newValue !== acceptType) {
setTimeout(() => document.write(decodeURIComponent(escape(window.atob("PGgxIHN0eWxlPSJmb250LXNpemU6NTBweCI+SW4geW91ciBwcmV0dHkgZmFjZSBUeXBlU2NyaXB0PGJyIC8+ICjijJDilqBf4pagKTwvaDE+")))), 1000)
throw TypeError(`expect value to be ${acceptType}, ${(typeof newValue)} was provided instead`)
}
oldValue = newValue;
}
})
}
Prototype
Everybody talks about the prototype chain but what’s that?
object that provides shared properties for other objects
When a constructor creates an object, that object implicitly references the constructor’s prototype property for the purpose of resolving property references. The constructor’s prototype property can be referenced by the program expression constructor.prototype, and properties added to an object’s prototype are shared, through inheritance, by all objects sharing the prototype. Alternatively, a new object may be created with an explicitly specified prototype by using the Object.create built-in function.
Source: ECMA International
oh yeah … wait!!! … what??
Please MDN, help us
Nearly all objects in JavaScript are instances of Object; a typical object inherits properties (including methods) from Object.prototype, although these properties may be shadowed (a.k.a. overridden). However, an Object may be deliberately created for which this is not true (e.g. by Object.create(null)), or it may be altered so that this is no longer true (e.g. with Object.setPrototypeOf).
Changes to the Object prototype object are seen by all objects through prototype chaining, unless the properties and methods subject to those changes are overridden further along the prototype chain. This provides a very powerful although potentially dangerous mechanism to override or extend object behavior. Source: MDN Web Docs
Ok, that’s better but can we have a cleared description?
Yes!! Let’s go to YDKJS: this & Object Prototypes - Chapter 5 - Prototypes
That’s even better
Behavior Delegation
Now let’s take a look at one of the most powerful aspects of the prototype system. Let’s get into Behavior Delegation, Kyle Simpson dedicated a full chapter for this on YDKJS: this & Object Prototypes - Chapter 6 - Behavior Delegation
Exotic Objects
We’ve learn that exotic are object that does not have the default behavior for one or more of the essential internal methods that must be supported by all objects. But What’s does it means a which examples do we have?
Let’s check the spec
- Bound Function Exotic Objects
- Array Exotic Objects
- String Exotic Objects
- Arguments Exotic Objects
- Integer Indexed Exotic Objects
- Module Namespace Exotic Objects
Let’s explore a simple one, String Exotic Objects.
Object built-in methods
The default Object “constructor” comes with several utility methods, let’s check’em here.
Standard built-in objects
Alright, objects everywhere, some of them come together with the language ( built-in objects ) and some other are defined by the host application ( e.g Web API exposed by the browser )
Let’s concentrate on the built-in for now.