Mastering Hard Parts of JavaScript: Prototype & Class II
Tags: javascript, codenewbie, prototype, oop
Published: 2020-08-20 21:03:34

---
series: Mastering Hard Parts of JavaScript
description: An introduction to JavaScript Prototype & Class using 15 exercises
---
## Using Object.create()

### Exercise 2

> Inside personStore object, create a property greet where the value is a function that logs "hello".

```js
const personStore = {
  // add code here
};

personStore.greet(); // -> Logs 'hello'
```

### Solution 2

```js
const personStore = {
  greet: function someName() {
    console.log("hello");
  },
};
```

Here greet is just a property on an object. It's value happens to be a function (that does something). We can access that property like accessing any other property of an object, using the dot notation `personStore.greet`. And since it's a function, we can run the function by adding the parentheses so `personStore.greet()` works and runs the function that we have defined.

It turns out the name of this function `someName` is not important since it's assigned to the property `greet` and so `someName` is never directly used. So we can turn it into an anonymous function:

```js
const personStore = {
  greet: function () {
    console.log("hello");
  },
};
```

And later on in ES6, they introduced a new more concise syntax, which allows us to write this example as:

```js
const personStore = {
  greet() {
    console.log("hello");
  },
};
```

All three solution shown here do exactly the same thing.

### Exercise 3

> Create a function personFromPersonStore that takes as input a name and an age. When called, the function will create person objects using the Object.create method on the personStore object.

```js
function personFromPersonStore(name, age) {
  // add code here
}

const sandra = personFromPersonStore("Sandra", 26);
console.log(sandra.name);
// -> Logs 'Sandra'
console.log(sandra.age);
//-> Logs 26
sandra.greet();
//-> Logs 'hello'
```

### Solution 3

```js
function personFromPersonStore(name, age) {
  const person = Object.create(personStore);
  person.name = name;
  person.age = age;
  return person;
}
```

The important thing to note here is that Object.create(), irrespective of the argument passed to it, always returns an empty object. So initially, `person` is an empty object with no properties.

Every object has a hidden [[prototype]] property (bad name!). Simply put, when you call a property on an object, the JS engine first checks to see if the object has that property. If it can't find such a property, it will look at its [[prototype]] property to see which Object it descends from. `const person = Object.create(personStore)` tells the JS engine to create a new empty object and return it and call it `person`, but if we call a property of `person` and `person` doesn't have that property, look up to `personStore` and see if it has that property.

So when `sandra.name` is called, the JS engine looks at the `sandra` object to see if it has a `name` property. It does, so its value is returned. Next when `sandra.age` is called, the JS engine looks at the `sandra` object to see if it has an `age` property. It does, so its value is returned. Next, `sandra.greet()` is called. The JS engine looks at the object `sandra` to see if it has a `greet` property. It does not. Instead of failing, the JS engine then looks at `sandra`'s hidden [[prototype]] property which points to personStore, so it then goes to see if `personStore` has a `greet` property. It does, so that function is invoked.

This basically achieves what class-based object oriented languages call inheritance, without using any classes. This is a gross simplification of JavaScript's prototypal architecture, for more information I've found [this chapter](https://javascript.info/prototype-inheritance) of the [javascript.info](https://javascript.info) very helpful.

### Exercise 4

> Without editing the code you've already written, add an introduce method to the personStore object that logs "Hi, my name is [name]".

```js
// add code here
sandra.introduce();
// -> Logs 'Hi, my name is Sandra'
```

### Solution 4

```js
personStore.introduce = function () {
  console.log(`Hi, my name is ${this.name}`);
};
```

We can add methods to our personStore at a later time. And the dynamic nature of JS means that even though sandra was created before we created this method, it still gets to use it. Here we also see our first introduction to the `this` keyword. In this context, `this` is always the value of the object to the left of the the function being invoked. So here when `introduce()` is called, the object to its left is `sandra`, so `this` gets assigned to `sandra`. Now when in the body of the method `this.name` is equal to `sandra.name` so its value is returned.

Do not use arrow functions when declaring methods on an object. In arrow functions `this` is lexically scoped, so in this example, if we had declared

```js
personStore.introduce = () => {
  console.log(`Hi, my name is ${this.name}`);
};
```

Here `this` would not refer to `sandra`. It would look to see if the function introduce has a `this`, it doesn't. So it would look at its outside scope to see if it has a `this`. Here the outside scope is the global memory or `window`, which does have an object called `this` so it checks to see if that object has a `name` property. It doesn't, so `undefined` is returned.

Understanding the prototype chain, what JavaScript's lexical scoping means, and how arrow functions change the behaviour of `this` are perhaps the most challenging parts of JavaScript for someone new to the language. It requires practice, but it _will_ make sense in the end.