# Classes and Instances

## Defining a Class

The Blueprint object model is based on principles from object-oriented programming. This means that we use classes to define all abstract data types. All classes in Blueprint extend the `BaseObject` (or `BO` for short) class. Below, we define a `Person` class.

{% code title="person.js" %}

```javascript
const {BO} = require ('@onehilltech/blueprint');

const Person = BO.extend ({
  firstName: 'Barack',     // default first name for all persons
  lastName: 'Obama',       // default last name for all persons
  
  fullName () { 
    return `${this.firstName} ${this.lastName}`;
  },
  
  greet () {
   console.log ('Hello, World!');
  }
});

module.exports = Person;
```

{% endcode %}

As shown above, you define a class by calling the  `BO.extend` static method. The extend method takes the definition of the class as its main parameter. The class definition is just a plain JavaScript object (also known as a hash) consisting of data properties (*e.g.*, `firstName` and `lastName`) and methods (*i.e.*, `fullName` and `greet`).&#x20;

{% hint style="info" %}
The *execution context* of a method is the current object.
{% endhint %}

{% hint style="info" %}
Unlike ES6 class definitions, you are not required to defined data properties in the constructor
{% endhint %}

The return value from the `create()` method is a JavaScript class object.

## Creating an Instance

The easiest way to create an instance of a class (*i.e.*, instantiate a class) is to use the `new` operator.

```javascript
let person = new Person ();
```

Above we created a new instance of the `Person` class. Once you create an instance of the class, you can use it like an object from any object-oriented programming language.

```javascript
console.log (person.fullName ());    // "Barack Obama"

person.firstName = 'George';
person.lastName = 'Bush';
console.log (person.fullName ());    // "George Bush"
```

### Using the Create Method

The other approach for creating an instance is to use the static `create()` method on the class.

```javascript
let person = Person.create();
```

This method for creating an instance is most useful when you want to apply a [mixin](/developer-guide/the-object-model/mixins.md) to the created instance.

### Initializing the Instance

Data properties in the class definition can have no value (*i.e.*, `null`), a default value, or be `undefined` (*i.e.*, not appear in the definition). This, however, does not mean you cannot initialize data properties when you create the object. Similar to the hash provided to the `extend()` method when defining the class, you can pass a hash to the object being created.

```javascript
let p1 = new Person ({firstName: 'George', lastName: 'Bush'});
let p2 = Person.create ({firstName: 'Bill', lastName: 'Clinton'});
```

You can even initialize the instance with data properties and methods that are not defined on the class.&#x20;

```javascript
let person = new Person ({firstName: 'George', middleInitial: 'W', lastName: 'Bush'});
```

{% hint style="info" %}
The data property does not have to exist on the corresponding class when passing the hash to the created object. This means the data property will be unknown to the corresponding class, but known to the client that created the instance.
{% endhint %}

{% hint style="info" %}
The initialization hash can also contain methods.
{% endhint %}

#### Using the init method

There are situations where defining a data property in the definition hash is not acceptable because all instances of the class will use the same variable. This is the case with object-like types in JavaScript, *e.g.*, objects and arrays. For example, let's assume the `Person` class from above has a data property named `friends`, which is an array of names.

{% code title="person.js" %}

```javascript
const Person = BO.extend ({
  // ...
  friends: [],
});
```

{% endcode %}

When we create instances of `Person`, all instances of `Person` will share the same `friends` array. For example, `p1` and `p2` from the example above would share the same `friends` array. If this is not the intended behavior you want, then you need to initialize the `friends` data property in the `init()` method.

{% code title="student.js" %}

```javascript
const Student = BO.extend ({
  // ...
  
  friends: null,
  
  init () {
    this._super.call (this, ...arguments);
    this.friends = [];
  }
});
```

{% endcode %}

{% hint style="info" %}
Use the `init()` method to initialize object-like data properties if you do not want all instances to share the same data property instance.
{% endhint %}

{% hint style="info" %}
The `init()` method *must always* call `this._super.call (this, ...arguments)`. Otherwise, the object model will not initialize the instance properly.
{% endhint %}

Now,  each instance of the `Person` class will have its own `friends` array.&#x20;

## Extending a Class

You've had a preview of extending a class when you created the `Person`. When you create a class, it will have a static `extend` method. You use this method to extend the class—creating a new class definition. For example, we can create a `Student` class from the `Person` class.

{% code title="student.js" %}

```javascript
const Student = Person.extend ({
  classification: null,
});
```

{% endcode %}

{% hint style="info" %}
Extending a class is also called *subclassing* in object-oriented programming.
{% endhint %}

The `Student` class will inherit the property and methods of the `Person` class. Similarly, we can create a `Undergraduate` class by extending the `Student` class.

{% code title="undergraduate.js" %}

```javascript
const Undergraduate = Student.extend ({
  /// ...
});
```

{% endcode %}

### Overriding a Base Class Method

When you extend a class, you have the option of overriding the methods in the base class. This means you are redefining its behavior to the new one that you provide. For example, let's assume we want the `Student` class to override the `greet()` method in the `Person` class.

{% code title="student.js" %}

```javascript
const Student = Person.extend ({
  greet () {
    console.log ('YOLO!');
  }   
});
```

{% endcode %}

Now, when we invoke the `greet()` method from an instance of a `Student`, we will get a different console message.

```javascript
let person = new Person ();
person.greet ();     // "Hello, World!"

let student = new Student ();
student.greet ();    // "YOLO!"
```

{% hint style="info" %}
If you do not override a base class method, the extended class will use (or inherit) the behavior of the base class method.
{% endhint %}

### Calling the Base Class Method

Just because you override a base class method in the extended class does not mean you do not need the behavior of the base class method. Sometimes, you many need to base class method's behavior in addition to the behavior you can provide in the extended class. For example, what if we want to print  the `Person` greeting in addition to the greeting from the `Student` class. We can do this by calling the base class method.

{% code title="student.js" %}

```javascript
const Student = Person.extend ({
  greet () {
    this._super.call (this, ...arguments);
    console.log (' YOLO!');
  }  
});
```

{% endcode %}

{% hint style="info" %}
Use `this._super.call (this, ...arguments)` or `this._super.apply (this, arguments)` to call the base class method. The former is the preferred approach over the latter approach for [performance reasons](https://jsperf.com/call-apply-segu).
{% endhint %}

Now, the `greet()` method from the `Student` class will print the greeting from the `Person` class in addition to its greeting.

```javascript
let student = new Student ();
student.greet ();    // "Hello, World! YOLO!"
```

##


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://blueprint.onehilltech.com/developer-guide/the-object-model/classes-and-instances.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
