Object-oriented JavaScript¶
Prototype-oriented JavaScript¶
Recall JavaScript's Types¶
ECMAScript defines 7 primitive (Immutable) types for values:
ECMAScript defines a special mutable type called object for collections of properties (objects and array).
In a dynamic language you don't specify the type when you declare a variable and the type of a variable can change.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Data_types
Objects¶
An object is a mutable unordered collection of properties. A property is a tuple of a key and a value. A property key is either a string or a symbol. A property value can be any ECMAScript language value.
You can access the properties of an object using the dot notation (property names: "^[a-z]+(_[a-z]+)+$"
):
Properties can also be accessed or set using the bracket notation:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects
Methods¶
When a function is stored as a property of an object, we call it a method. When a method is invoked, the value of this
inside the method is the object the method is called on.
When a function which is not the property of an object is invoked, this
is bound to the global object. This is an error in the design of the language as it prevent the definition of helper funtions.
This issue can be addressed with: - The apply(this, args)
, call(this, arg, ...)
or bind(this)
methods of a Function
object that redefine this
. - The arrow function expression that do not define its own this
and takes the one present in its scope.
Notes:
- The
apply(this, args)
function is a method ofFunction
instances, which allows to override thethis
object with one provided as argument. For example, given agetName
method on aPerson
object, callinggetName.apply(animal, args)
will execute thatgetName
function as if it had been called likeanimal.getName(args)
. - The
call(this, ...)
function is identical, except that the arguments are given directly, instead of in the form of an array. - The
bind(this)
method ofFunction
returns a new function whosethis
object is overridden with the provided one.
The prototype property¶
Every object automatically has a prototype property, intended to describe what that object is.
When an object's prototype property is the prototype of another object, we say that the former inherits from the second. Here, obj
inherits from Object
.
Notes:
All objects have a property holding a prototype object. That property is often named __proto__
, but no standard enforces this. Note that this prototype is an object, so it also has a prototype property. This creates the prototype chain.
When an object is created, its prototype property is automatically set to Object.prototype
, the prototype of the Object
type. Note that, Object.prototype
being an object, it also has a prototype property itself, but it is null
since it is the end of the prototype chain.
Whenever a property or method is requested on an object, it is first searched for in that object, and if not found, it is searched on its prototype, and so on until it is found or the entire prototype chain has been traversed.
Prototypes on functions¶
Every function also has a prototype property. If a function is used as a constructor, this prototype is used as the newly created object's prototype. This prototype automatically contains a constructor
property, pointing back to the function.
The Object.assign
function extends the prototype given as first argument with the properties of the prototype given as second argument.
Notes:
Every created function has a prototype property, similarly to objects. However, this time it is named prototype
. The value of that property is what will be used to populate the prototype property of any object created using that function as a constructor.
To use a function as a constructor, it must be called with the new
keyword, in which case, the this
keyword in that function will refer to the newly created object. Otherwise, it points to the global object.
In this example, while we could do Person.prototype = personPrototype
, it is good practice to use the Object.assign(dst, src)
function: instead of simply replacing the existing prototype with the provided one, it extends the existing one with all properties of the provided prototype.
See https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes
Prototype Inheritance building blocks¶
Object.create(proto)
: Returns new empty object with prototype property equal to proto
.
Object.assign(dst, src)
: Extends dst
object with all enumerable, own properties of src
. (Those are essentially the user-defined properties that are not inherited. Hence constructor
and __prop__
are *not copied by Object.assign
.)*
If we want that A inherits B, then we have to:
Object.create
a new prototype object forA
whose prototype property isB
's prototype.Object.assign
all enumerable own properties ofA
's old prototype to the new one.- Since
Object.assign
does not copyconstructor
to the new prototype, do so manually. - Replace
A
's prototype with the new one.
1 |
|
Prototype Inheritance¶
Example
When a lookup fails on the apple object, it now falls back on the Fruit prototype
.
Consider the Array object¶
The Array
object is a global object that is used in the construction of arrays; which are high-level, list-like objects.
Here, the []
notation is a shorthand for the Array
constructor.
Array
is a function and the new
operator changes its behavior: - It creates a new object that inherits from the Array.prototype
object. - It binds the newly created object to the this
keyword. - It returns the newly created object unless the function returns another object.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
Overriding the prototype¶
Changes to an object's prototype are seen by all instances of that object. That's why it's called a prototype.
The properties and methods of prototypes can be overridden along the chain.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype
Putting it all together¶
As objects inherit properties from their prototype, we can say that JavaScript is a prototype-based language and not a class-based one.
While JavaScript does offer a class syntax, it is only syntactic sugar for the prototype-based inheritance system.
Object-oriented JavaScript¶
The Object-oriented Syntax¶
Introduced in ECMAScript 2015, classes are syntactic sugar over JavaScript's prototype-based inheritance. It is not a new object-oriented inheritance model.
- The
extends
keyword is used in class declarations or class expressions to create a class as the child of another class. - The
constructor
method is a special method for creating and initializing an object described by a class. - The
super
keyword is used to access and call functions on an object's parent.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
Static properties and methods¶
The class syntax enables the use of static
methods and properties, which are shared across all instances of the class.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
Private properties and methods¶
The class syntax enables the use of private
properties and methods, which are not accessible outside of the class.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
Getters and setters¶
The class syntax enables the use of getters
and setters
to access and modify properties.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
Species¶
Sometimes, you want to create a new object of the same type as an existing object. For example, when you want to create a new array from an existing array, you want to create a new array of the same type.
The Symbol.species
property allows you to do that by specifying the constructor function that is used to create derived objects.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
Notes:
A lot of things are going on in this example.
The [...]()
syntax
When defining an object's property, JavaScript allows us to provide an expression to be evaluated to that property's name, instead of providing an explicit name. That expression must then be surrounded by square brackets, which should remind you of how one can access an object's property through the same notation instead of the dot notation. This feature is called Computed Property Names. For example, it can be used as follows
Symbol.species
Recall that Symbol
represents a unique identifier. The Symbol
class offers static properties that contain symbols created for specific intents. This is the case of Symbol.species
: it is a symbol intended to be used as a property name of all objects, and that identifies a static getter returning the function to be used to instantiate that object. Writing static get [Symbol.species]() { //...
thus means declaring a static getter whose identifier is the symbol stored as a static property of the Symbol
class, Symbol.species
, and whose value corresponds to the function that instantiates that object.
Mix-ins¶
Multiple inheritance is not supported in JavaScript, but it is possible to simulate it with mix-ins.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
Modules¶
ECMAScript 6 introduced modules, a reusable piece of code that can be exported from one program and imported for use in another program.
Exporting¶
Any value can be exported (object, number, string, etc).
An export can be made default by following export
with default
.
Modules¶
ECMAScript 6 introduced modules, a reusable piece of code that can be exported from one program and imported for use in another program.
Importing¶
The imported script must be loaded as a module.
The import
statement must always be at the top of the file, before any other code.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules
Notes:
Example