0

I am a little confused with the Javascript prototyping mechanics. I have the following code :

function Person () {
  this.name = "no name";
  this.setName = function (n) {
    this.name = n;
  }
}

function Student () {
  this.id = "123";
}
Student.prototype = new Person();

s = new Student();
s.setName("Klaus");

After the execution of the code the object s has two names. The name "Klaus" in the object itself and the name "no name" in its prototype. I know that the property is shadowed and it works fine, but this doesn't feel naturally?! Is there a better way to only use the property of the prototype?

landunder
  • 372
  • 1
  • 5
  • 17
  • 2
    Why do you have the `this.setName = function (n) { this.name = n; }` construct at all? Why don't you write `Person.prototype.setName = function (n) { this.name = n; }` outside of your `Person()` function? – t.niese Jan 26 '16 at 16:49
  • @Teemu as far as I understand it, `.setName` is called in the context of `s` though, which is the student. Thus the property would be on `s` as well (not in the prototype). Initially, when assigning `new Person` to the prototype property of `Student`, the property is created in its prototype as well. – nils Jan 26 '16 at 16:57
  • Yes, but it seems OP understands this, they say: "I know that the [prototype] property is shadowed". The actual question is unclear though. – Teemu Jan 26 '16 at 17:01
  • @Teemu oh yes you are right. I should stop writing answers for today. – t.niese Jan 26 '16 at 17:01
  • What do you actually want to achieve? Access `name` in the prototype and in the own property of `s` in context of `s`? – Teemu Jan 26 '16 at 17:07
  • The property `name = "no name"` stays in the prototype and I can still retrieve it. Even if I'm doing it this way: `Person.prototype.setName = function (n) { this.name = n; }`. – landunder Jan 26 '16 at 17:13
  • Yes, but what is your question? What do you want instead own property shadowing the prototype property? The prototype is always shadowed if you set an own property using the same name. You've to refer directly to the prototype to get the original `name`, i.e. "no name". – Teemu Jan 26 '16 at 17:16
  • You shouldn't change shadowed properties in the prototype in the first place, unless you have a really good reason to do so (it's not impossible, just a bit of work, since this is not the way JavaScript was designed to work). Just use instance properties, and different names if you need to keep an old value. – nils Jan 26 '16 at 17:20
  • @nils Why not? Prototype properties are handy default values, excluding methods ofcourse. – Teemu Jan 26 '16 at 17:23
  • Yes, you shouldn't try to modify them though (which sounds like what he is trying to do). If you want to use them as default values, you can of course. But then you probably want to define them as properties on the `.prototype` property instead? Or do I have things backwards? – nils Jan 26 '16 at 17:25

1 Answers1

0

You can work on the prototype property directly, but it would take a disproportionate amount of effort and is probably not considered best practice. Instead, you could call the Person constructor in the right this context.

First of all, it is highly recommended to use Object.create instead of the new Operator, when assigning to the prototype property of a function. With the new Operator, the Person constructor is called, but in the wrong this context. To prevent that, you can link them like this:

Student.prototype = Object.create(Person.prototype);

Instead, if you want to call the constructor of the prototypal Link (Person) inside Student, you could call it in the constructor with the correct this context:

function Student () {
  Person.call(this);
  this.id = "123";
}

Also, unless you want to create a single function for each instance, I would move the setName function to the [[Prototype]] of Person:

function Person () {
  this.name = "no name";
}

Person.prototype.setName = function (n) {
  this.name = n;
}

function Student () {
  Person.call(this); // Call the Person constructor
  this.id = "123";
}
Student.prototype = Object.create(Person.prototype);

s = new Student();
// s.name is 'no name'
s.setName("Klaus");
// s.name is 'Klaus'

Alternatively, as @Teemu mentioned, you could also place the name property on the Person.prototype to use it as a default value:

function Person () {
}

Person.prototype.setName = function (n) {
  this.name = n;
}

Person.prototype.name = "no name"; // Define the name directly

function Student () {
  Person.call(this); // you only need this, if there are other things happening in the Person constructor that you need as well
  this.id = "123";
}
Student.prototype = Object.create(Person.prototype);

s = new Student();
// s.name is 'no name'
s.setName("Klaus");
// s.name is 'Klaus'
nils
  • 25,734
  • 5
  • 70
  • 79
  • If I do it this way the `name` is not longer in the prototype, but a property of the created object! That is what I wanted. It was just the `Object.create()` instead of `new`. – landunder Jan 26 '16 at 17:32