0

I'm having trouble understanding the way constructors are used to create new objects. Here's some example code:

function Backpack(type) {
  this.type = type;
}

Backpack.log = function() {
  console.log('This is a ' + this.type + ' backpack.');
}

var smallBackpack = new Backpack('small');
console.log(smallBackpack.type);
//-> small

smallBackpack.log();
// TypeError: smallBackpack.log is not a function

What I understand is happening (and please correct me if I'm wrong) is that Backpack is being used as a constructor and returning a new object. In this instance Backpack is the prototype of smallBackpack, and contains all of the properties that will be looked up if they're not set to smallBackpack. Why is it then, that smallBackpack.log isn't present.

function Backpack(type) {
  this.type = type;
}

Backpack.prototype.log = function() {
  console.log('This is a ' + this.type + ' backpack.');
}

var smallBackpack = new Backpack('small');
console.log(smallBackpack.type);
//-> small

smallBackpack.log();
//-> This is a small backpack.

In this example, I found that if I set log to Backpack.prototype, then smallBackpack gets the function. I don't understand why this works and not the first example.

Dan
  • 119
  • 7
  • 2
    When you set a property on the prototype all instance get that method: that's just how JS works. In the first example you set a property on the `Backpack` function itself, e.g., `Backpack.log()` works but is useless. – Dave Newton Apr 19 '17 at 15:29
  • What's confusing to me is that *this.type* is available to all instances. How is that different from how I set *Backpack.log*? Why is *this.type* part of the prototype and not *log*? – Dan Apr 19 '17 at 15:32
  • Because you used "this". But it's only available to "all instances" in the sense that each instance has a "type" property--each instance has its own "type" unlike "log" where the same function is used across all instances. Might be worth looking at how the JS object system works via a tutorial whatever. It's not the same as non-prototype OOPLs. – Dave Newton Apr 19 '17 at 15:34
  • I think it's important to note that javascript objects do not **actually** inherit anything. They simply have a pointer to a prototype object, whose properties/methods/prototype they can access. It's called prototype chaining. [This is a fairly decent article explaning it](https://community.risingstack.com/javascript-prototype-chain-inheritance/) – mhodges Apr 19 '17 at 16:13
  • @mhodges I misspoke when I said *inherit*. This makes sense because if it wasn't a pointer, then objects created as an instance of the constructor wouldn't be able to get new properties as they were added. – Dan Apr 19 '17 at 17:57
  • @DaveNewton I'm guessing that I don't understand what *this* is in this situation. I thought *this* inside the function is pointing at the function in the same way that *Backpack* is. – Dan Apr 19 '17 at 17:57
  • @Dan Asking what `this` is, opens a whole can of worms, but to be brief, it depends on calling context. Since you called `.log()` on an owning object (in this case `smallBackpack`), `this` inside the log function refers to `smallBackpack` for that particular instance of the function call. If you were to create a smallBackpack2, and call the log function on that, it would refer to smallBackpack2 – mhodges Apr 19 '17 at 18:02
  • @Dan [Kyle Simpson's "You Don't Know JS: this & Object Prototypes"](https://github.com/getify/You-Dont-Know-JS/tree/master/this%20%26%20object%20prototypes) book is a phenomenal resource to have that explains this context as well as prototypal inheritance/chaining. – mhodges Apr 19 '17 at 18:05
  • @Dan I also have an answer here that sums up the 4 basic rules to determining the `this` context in any situation [here](http://stackoverflow.com/a/40556348/4987197) – mhodges Apr 19 '17 at 18:10
  • @mhodges I think I have a good grasp on how 'this' works. I think a way to rephrase my question is: Why does setting properties to the constructor but from the outside (like 'Backpack.log = function...') not work for instances of the constructor? I can still access that property ('Backpack.log()'), but it doesn't translate its instances at all. Also, thanks for the book suggestion. I'm currently going through Eloquent Javascript v2, but I may pick up YDKJS as a supplement. – Dan Apr 19 '17 at 19:18
  • @Dan Well, I don't exactly understand what you're asking, but in using words like `instance` and `constructor`, it tells me that you are trying to think about javascript objects in the classical OO sense, which you simply cannot. There is no such thing as an instance of a constructor in JS. You have a function that, when called with the `new` keyword, returns an object, and the `this` context inside a function called with the `new` keyword is automatically bound to the object returned by that function. – mhodges Apr 19 '17 at 19:27
  • @Dan The object returned is not an instance of the constructor in the classical OO sense. It is simply a new object that has the same properties that has a live link to the same prototype chain. If you update something in the object, it will not carry over across other objects (obviously), but if you add something on the prototype, it will update all objects that use that prototype - both for new objects as well as objects that already exist. – mhodges Apr 19 '17 at 19:32
  • @Dan The reason `Backpack.log = function...` does not work as you'd expect is because you are actually adding the property onto the Backpack function object itself - not onto the object returned by the function. So when you say `smallBackpack.log()`, log not a property on the object returned by `new Backpack()`, hence the error – mhodges Apr 19 '17 at 19:35
  • @mhodges I think this is starting to make sense to me. What is `this.log` doing differently? Is it not adding the property onto the Backpack function object as well? My assumption was that they're doing the same thing. – Dan Apr 19 '17 at 19:45
  • Well, maybe this goes back to understanding the primitive types, then. Primitives in JavaScript can be treated as objects. You can add/remove properties to individual instances, and you can also add/remove properties to their prototype. When you say `Backpack.log = function...` you are essentially adding it to that instance of the primitive function type's object. Place a `console.log(Backpack)` right before, and right after the `Backpack.log = ` statement and notice the difference. Before, it is just a function - after, you'll see there is an object wrapper around it with a `log` property now – mhodges Apr 19 '17 at 19:57
  • @Dan I think you still don't fully understand how the `this` context gets bound. `this` does not get scoped to the function itself (unless you explicitly do it with call/apply/bind). In other words, it's not lexical - it's contextual. Which is why it is called the this **context**. It has to do with **how** the function is called. When you use the `new` keyword, it binds the this context inside the function you are calling to a **new** object that is implicitly returned by the function. This object is **not** the same object as the function itself. – mhodges Apr 19 '17 at 20:05
  • `Backpack.log.call(smallBackpack)` may be a good example for you to play with. https://repl.it/HQiR/1 – mhodges Apr 19 '17 at 20:11
  • @mhodges I just came to that conclusion a few minutes ago. I was assuming `this` was referring to the function and not the object that it's creating with the new keyword. Thanks for your help and the confirmation. – Dan Apr 19 '17 at 20:12
  • @Dan You're welcome =) Now go read [YDKJS](https://github.com/getify/You-Dont-Know-JS/tree/master/this%20%26%20object%20prototypes) and become a master! Haha – mhodges Apr 19 '17 at 20:18

1 Answers1

0

please understand working of new keyword. it first creates empty object and then wires up "this" to that empty object hence calling this.type in your case is property of created instance of object. calling Backpack.log() doesn't create any method on instance objects.