1
function Person(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
}
Person.prototype.talk = function () {
    return this.firstName + " " + this.lastName;
}
//creating a Person object for extension
var manager = new Person('jon', 'doe');
console.log(manager.talk());
//Manager prototype..but doesn't inherit Person methods 
function Manager(firstName, lastName, accessCode) {
    //shared properties
    this.firstName = firstName;
    this.lastName = lastName;

    this.accesscode = accessCode;
}
function personChecker(person) {
    var returnCode = 0;
    if (person instanceof Person) {
        returnCode = 1;
    }
    else if (person instanceof Manager) {
        returnCode = 2;
    }
    return returnCode;
}
console.log(personChecker(manager));

Is it possible to share a prototype and have a different constructor? I would like to have Manager inherit everything from Person (and then extend it) and have a function switch on the prototype and do something different depending on the argument passed to the personChecker function

Oriol
  • 274,082
  • 63
  • 437
  • 513
wootscootinboogie
  • 8,461
  • 33
  • 112
  • 197
  • Yes, it is possible, however `instanceof` might not work as expected then. What do you actually want to do? That "function switch" sounds like a bad idea. Are you sure you don't want to "subclass" Person? – Bergi Dec 22 '13 at 21:14
  • @Bergi I want some sort of functionality where I have a method that can take a manager, janitor and president object which inherit from Person. How should I differentiate between those types? Should I use something like `hasOwnProperty` instead of switching on the prototype? – wootscootinboogie Dec 22 '13 at 21:17
  • Inheriting three times from Person sounds like the right thing - but then they don't have the same prototype ("share"). Whether it's better to distinguish the instances by calling their own methods or by instanceof-checking them should be another question. – Bergi Dec 22 '13 at 21:23
  • @Bergi so let's say that the personChecker function appends the firstName and lastName properties on the page, but it does so in a different color depending on the person, manager, etc. How would that be implemented? Would I switch on to be able to tell the difference? – wootscootinboogie Dec 22 '13 at 21:28
  • @wootscootinboogie: You could always add an extra field to your objects to tell what class they belong to. So you would do `this.personType = "JANITOR"`, `this.personType = "MANAGER"`, etc. – hugomg Dec 22 '13 at 21:30
  • @wootscootinboogie: Why don't you give each person a method that lets them choose their own color? – Bergi Dec 22 '13 at 21:32
  • @Bergi because it seems to me the color information doesn't belong to the person, but the page. So I thought it might be a better idea to keep a method somewhere else. – wootscootinboogie Dec 22 '13 at 21:37
  • @missingno I have done that before when deserializing .NET interface types, and it's a quick and easy solution. – wootscootinboogie Dec 22 '13 at 21:38

3 Answers3

2

The typical way to support this inheritance:

// create a blank function to pass the prototype
var blank = function() {};

// assign the prototype to inherit to the blank constructor
blank.prototype = Person.prototype;

// pass an instance of the prototype as the Manager prototype
Manager.prototype = new blank();

var person = new Person('John', 'Doe');
var manager = new Manager('Greg', 'Smith', 'access1234');

manager.talk(); // Greg Smith
person.talk(); // John Doe

Here is a fiddle: http://jsfiddle.net/XF28r/

Note a Manager is also a Person so you need to switch the check around.

function personChecker(person) {
    var returnCode = 0;
    if (person instanceof Manager) {
        returnCode = 2;
    } else if (person instanceof Person) {
        returnCode = 1;
    }
    return returnCode;
}

Although note I would put this in a helper method:

function extend(parent, child) {

  var blank = function() {};
  blank.prototype = parent.prototype;
  child.prototype = new blank();
}

So then you can simply use it:

extend(Person, Manager);

As Bergi has mentioned in the comments, this can also be reduced down to:

function extend(parent, child) {
  child.prototype = Object.create(parent.prototype);
}

(works IE 9 upward)

Stuart Wakefield
  • 6,294
  • 24
  • 35
  • Instead of that helper function, you should just use `Object.create` or a shim of it (@DaggNabbit: it's OK here, because he uses `blank` explicitly, although some explanation would've helped) – Bergi Dec 22 '13 at 21:47
  • That's nice @Bergi, I didn't know that. IE9 upward but still. I'll add it to my answer – Stuart Wakefield Dec 22 '13 at 21:48
2

In JavaScript, inheritance is generally done like this:

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

Object.create creates a new object with the object passed as the first argument in its prototype chain.

If you need to support old browsers that don't have Object.create, you can do the "prototype dance" instead:

function F(){}
F.prototype = Person.prototype;
Manager.prototype = new F();

In any case, avoid code calling the constructor to get such an object (Manager.prototype = new Person()), this is a well-known antipattern and will break as soon as your constructor relies on any of its arguments (and it causes other, more subtle problems).

Dagg Nabbit
  • 75,346
  • 19
  • 113
  • 141
  • Interesting, I think this is what I really wanted to do. What are some of the more subtle problems this antipattern causes? – wootscootinboogie Dec 22 '13 at 21:51
  • 1
    I think the most common problem is that anything the constructor does with `this` will happen to your new prototype object, when the assumption is probably that `this` is just an instance object. – Dagg Nabbit Dec 22 '13 at 21:53
  • Thank you, This is the best explanation I've come across yet! – Sukima Jan 09 '15 at 19:02
1

Use the following:

function Person(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
}
Person.prototype.talk = function () {
    return this.firstName + " " + this.lastName;
}

function Manager(firstName, lastName, accessCode) {
    //shared properties
    this.firstName = firstName;
    this.lastName = lastName;

    this.accesscode = accessCode;
}
Manager.prototype = new Person('jon', 'doe');

function personChecker(person) {
    var returnCode = 0;
    if (person instanceof Manager) {
        returnCode = 2;
    }
    else if (person instanceof Person) {
        returnCode = 1;
    }
    return returnCode;
}

Note that I have changed the order of the conditionals, because an instance of Manager is an instance of Person too:

var per = new Person('A', 'B');
per.talk();              // 'A B'
per instanceof Person;   // true
per instanceof Manager;  // false
personChecker(per);      // 1

var man = new Manager('A', 'B');
man.talk();              // 'A B'
man instanceof Person;   // true !!!!
man instanceof Manager;  // true !!!!
personChecker(man);      // 2

If you want to do it the good way, instead of

Manager.prototype = new Person('jon', 'doe');

use

Manager.prototype = Object.create(Person.prototype, {constructor: {value: Manager}});

but it doesn't work on old browsers.

Oriol
  • 274,082
  • 63
  • 437
  • 513
  • 2
    -1 Why are all your managers called jon doe? [Don't use `new` for inheritance](http://stackoverflow.com/q/12592913/1048572)! – Bergi Dec 22 '13 at 21:30
  • @Bergi I thought that wootscootinboogie wanted "jon doe" to be a default name – Oriol Dec 22 '13 at 21:34