Using Object.create()
Exercise 2
Inside personStore object, create a property greet where the value is a function that logs "hello".
const personStore = {
// add code here
};
personStore.greet(); // -> Logs 'hello'
Solution 2
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:
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:
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.
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
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 of the 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]".
// add code here
sandra.introduce();
// -> Logs 'Hi, my name is Sandra'
Solution 4
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
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.
Top comments (0)