7 days of WordPress plugins, themes & templates - for free!* Unlimited asset downloads! Start 7-Day Free Trial

Next lesson playing in 5 seconds

  • Overview
  • Transcript

3.2 The JavaScript Data Type

JavaScript's rich type-system allows us to define our own data types. You'll learn how to write a constructor function and how to use the prototype object in this lesson.

3.2 The JavaScript Data Type

In the previous session, we created objects with a factory function. We saw how easy it was to create a factory function and how easy it was to create objects from a factory function, but we also saw some of the issues with this approach. First of all we are creating objects that are simply just and if we ever need to try to determine what type of object that we have, we have to inspect the properties on that object itself. And that is essentially duck typing. If something looks like a duck, if it waddles like a duck, and if it quacks like a duck, it's a duck. In our case, if it has a firstName property A Last Name property and a great method then. For all intents and purposes it is a person object. The second issue was performance related. Any time that we create an object and define a method on that object we are creating a function object. For that and for every object that we create we are creating a new function object for the methods on that object. That increases the amount of CPU time that it takes to create those objects. And it also increases the amount of memory that we use. So the solution to those two problems are to just define a data type. That is something that JavaScript allows us to do. And that's really what JavaScript was designed for. Now we are still going to have a function that creates our objects. But instead of actually creating the object inside of the function it's going to be JavaScript itself that will create the object because we are going to use our function in conjunction with the new keyword. So just like we would create a new object by newing up the object constructor, we are instead going to create our object by calling it new person. So the first thing we need to do is change the name of this function. And constructor functions typically begin with an uppercase character and they are named to be the type of object that we want them to be. So we want to create person objects, so therefore this constructor function Is going to begin with an uppercase P. Now inside of a constructor function, you define the properties and methods that you want. So instead of creating a person object and then returning it, instead we are going to use the this keyword. Followed by a dot, and then the property that we want to create. So the this keyword refers to this object. So whenever we call the person constructor with the new keyword, it's going to create a new object and the this keyword is going to refer to this new object that we are creating. So we will say this.firstName equals firstName. And then we will do the same thing for last name. And then we can define our method. Now we're going to do so first inside of the constructor. But that isn't going to solve our particular problem. Because for every object that we are going to create, we are still creating a new function object for each of these greet properties. However, we will come back and we will fix this, so that that particular problem is no longer an issue. Now notice however, that we are losing the semi-privacy that we had before. The first name and the last name properties are now public. Because we've used this.firstName = firstName. And we did the same thing for the last name. So whenever we create a person objects we can come in here we can say obj.firstName = and then we can set it to whatever value that we wanted to. Now we could get around that by defining the properties inside of the constructor with object.defineProperties or defineProperty. I'm just going to use defineProperty. We would use the this object as the arguments. And then the name of the property that we want to define. And then the property descriptor. So here we could say that we have a getter. That is a function that returns first name, and this would work. However, also keep in mind that we are talking about a fish. See, we are eventually going to get away from defining our greets method inside of the constructor, so why would we want to create another function to return the first name, because it is another function. Every time we create a person object, the first name property would be defined and the getter function would be created. Now with ECMA Script six we can actually do a better job of privacy because we can use symbols which will be something that we will look at. But for now let's work on getting this greet method out of the constructor. And we can do that by defining it on the Prototype. Now every function has a property called Prototype. But it is only useful on constructor functions. So the createPerson function that we had before has a prototype property. But it wasn't useful because it wasn't a constructor function. Now you can think of the prototype exactly what it says it is the prototype of an object. And the wonderful thing about the prototype is that whatever you define on the prototype is shared between all instances of that constructor function. And that means it is ideal for method definitions because we can define a method on the prototype and only do so once. This means that this function object is only created when JavaScript executes this code and assigns it to person.prototype.greet. So let's get rid of this dot inside of the constructor. We also need to come in here and instead of using firstName. Because that is now undefined, we don't have firstName defined anywhere. We need to say this.firstName. And now whenever we create our Person objects, we need to change this to new person. And then new person, and now these two objects share the same greets method. So down here, obj.greet = obj2.greet because they are the same method. So the prototype is very useful but it can be a little confusing, because what we actually have is a great property that is not actually on the person objects that we create. So for example, whenever we create a person impasse Jondo to the constructor, it's going to call the person constructor function. It's going to start building that for us and it's going to take the first name. And a sign that to the first one property that we have created on the object itself. We call these properties own properties, meaning that the object owns this property or it has its own firstName property. So this firstName property is actually on the object that we have created. The same thing is true for the lastName property. However, the greet property is not because that is defined on person.prototype. So this is what happens if we say obj.firstName, JavaScript is going to look at the object itself for the firstName property. It's going to find it so it's going to return the value. However, if we tried to call the greet method, it's going to go through the same process. JavaScript is going to look on the object itself for the greet method. But it's not going to find it there because we did not define greet on the object itself. It is on the prototype. So instead JavaScript is not going to find greet on the object. So it is going to start to looking at person.prototype. It goes to whatever constructor function created that object and looks at its prototype for whatever property that we are trying to access. In our case, we have greet defined on the prototype. So it's going to find greet and execute it. Now I have to say that whenever you access something on the prototype. It does take just a little bit longer to find it because Javascript has to go through this process of finding an identifier, it's called Identifier Look Up. It starts on the object, and then it goes to the prototype. And if there are other prototypes in the chain, it starts searching each individual prototype. So the longer your prototype chain, the longer it takes to access the property that you want to access. And we will talk about that later on in this course. But for right now, just know that for a single prototype, it is Is almost as fast as accessing an own property. So don't worry about performance with a prototype because it is very very fast. Well, for the final part of this lesson let's incorporate some privacy here. And in order to do that we are going to use the new symbol that comes with ES6 or ES2015 however you want to refer to it. And what we are going to do is wrap everything inside of and immediately invoked function. Or at least as far as this person data type is concerned. Now this entire thing is referred to as the data type. So it's not just the constructor function, but it's also the things that are on the prototype. And the reason why we are wrapping this inside of an immediate leave vote function is because we are going to use symbols for the first name and last name property names. And if we hide them inside of a closure Well, then, they are protected. So this function is actually going to return Person. And then we will say, var person is equal to that. So instead of here, we are going to create the two symbols. We'll call one firstName, and let's do firstNameSymbol. And we will just call Symbol(). Now, Symbol() is a primitive data type. It is not an object, and in fact there is not a wrapper object for symbol. The only way you can create a wrapper object is to wrap just a normal object around it. So you'd just call symbol without the new keyword, and then you have a new symbol. And every time you call symbol, you're going to get a unique symbol. So we will have firstNameSymbol, lastNameSymbol. And then inside of our constructor, we are going to use these symbols for the property names. Now this adds privacy because symbols are not enumerated. If you used a for in loop, these properties would not be there. So here, we are going to say this and then we are going to use the index syntax and then we will say first name symbol is equal to our first name value. We will do the same thing for the last name except that we will use the last name symbol. And basically, any time that we want to refer to these properties, we use this type of syntax. So here instead of this.firstName, we say this and then firstNameSymbol. Now with this approach we have lost the actual firstName and lastName properties. But we can define them on the prototype using object.defineProperty or defineProperties. I'm going to use defineProperty. Because I find that to be a little cleaner, that's just my personal opinion. Now for the object that we are going to define our properties on is going to be on the prototype. And then we will have our property name, so firstName, and then the property descriptor. We will have a getter that is going to simply return this, and then our firstName symbol. And that will give us our firstName property. And we essentially just need to copy and paste that for the lastName. And we will of course change the property to lastName. And we want to use the lastName symbol. So now we can create person objects that have first name and last name properties that are getters. We have a greet method that accesses the first name, all while protecting the information that's passed to the constructor. So nothing outside of this immediately invoked function can access these values. And unlike the "privacy" from the previous lesson, this is what I call true privacy. If you look at other languages like Java and C Sharp, the private fields in the class are accessible throughout the entire class. And that includes static methods. So, like, for example if we come down here. And if we say person.foo is equal to a function. And we are going to accept a person object. We would be able to access the private fields of this person object. Now of course we have only two private fields. But since we have this first name symbol and last name symbol we can access those pieces of information by simply using the same syntax. Except that instead of using this we use the object itself. So if we wanted to do something like this where we could rename person. We could pass in the person that we wanted to rename. And then let's just change the last name because that's more common than the first name. So we could take our person. We could say that the last name is equal to what was passed to the function, and that will change that value. And that's not functionality that we had at our disposal in the previous lesson. So in my opinion, we have walked away with a better way of creating those person objects. Of course it has required a lot more code and that's one of the reasons why factory functions became so popular. Even without all of this privacy stuff, we still had to have to have our constructor, where we said this.firstName = firstName, this.lastName = lastName, and then define the greet method on the prototype. Well, people got tired of doing that. Well there was that and then there was a notable person that said that we shouldn't use the new keyword, because if you forget to use new in conjunction with a constructor function, well then this refers to the window object so we are, essentially adding new things to the window object and the rest of our code breaks. But I think that that's rather silly. Yes, you can do that. And some of the time you will do that. I know I certainly have, but it's a common mistake. We make mistakes, we fix them and we move on. And I will get off my soapbox. Well in the next lesson and we are going to look at a better way of creating data types in JavaScript, Scripts 6 or 2015 introduces the idea of a class to JavaScript, but really it's just syntactic sugar for what we have just looked at.

Back to the top