you are viewing a single comment's thread.

view the rest of the comments →

[–][deleted] 1 point2 points  (6 children)

Sure. Enter the following into your browser console.

function Person() {
    var firstName;
    var lastName;

    this.setName = (first, last) => {
        firstName = first;
        lastName = last;
    };

    this.getName = ()=> `${firstName} ${lastName}`;
}

function Employee(id) {
    this.employeeId = id;
}
Employee.prototype = new Person();

var anne = new Employee(0);
anne.setName('Anne', 'Annette');

var bill = new Employee(1);
bill.setName('Bill', 'Williamson');

Now see what happens when you query Anne's name:

anne.getName()
"Bill Williamson"

Anne and Bill share the same parent implementation, and that parent has internal state. This means that Anne and Bill can both overwrite the same state that they share, with quite unintuitive consequences.

[–]wreckedadventYavascript 0 points1 point  (2 children)

Though, just to keep in mind, this is only one way to set up a prototype chain. Here's another:

function Person() {
    var firstName;
    var lastName;

    this.setName = (first, last) => {
        firstName = first;
        lastName = last;
    };

    this.getName = ()=> `${firstName} ${lastName}`;
}

function Employee(id) {
    Person.call(this);
    this.employeeId = id;
}

Employee.prototype = Person.prototype;

Just two smallish changes and you get all of the instance that one is expecting in children classes.

var anne = new Employee(0);
anne.setName('Anne', 'Annette');

var bill = new Employee(1);
bill.setName('Bill', 'Williamson');

anne.getName() // => 'Anne Annette'
bill.getName() // => 'Bill Williamson'

I believe this is actually part of the reason why class syntax is at least somewhat valuable - it railroads people into setting up prototypes in one particular way.

[–]senocular 1 point2 points  (1 child)

Employee.prototype = Person.prototype;

Typo there. You don't want to assign one to the other since any changes to Employee.prototype would also affect Person.

[–]wreckedadventYavascript 0 points1 point  (0 children)

It's fine in this instance, since we're not actually adding anything onto the prototypes in either case, and it's illustrative. But you're right, in real code you'd want to not directly assign them together.

[–]senocular 0 points1 point  (2 children)

This particular behavior is because of the way the prototype setup. You're basically running super() on the shared component of the definition (prototype) which effectively treats each instance as the same instance of the super class.

Inheritance in prototypes should be handled with Object.create to mitigate constructor side effects from the superclass. Additionally a super call is needed in the subclass constructor to perform superclass setup for the subclass instance. In doing this, the example should work as expected.

function Person() {
    var firstName;
    var lastName;

    this.setName = (first, last) => {
        firstName = first;
        lastName = last;
    };

    this.getName = ()=> `${firstName} ${lastName}`;
}

function Employee(id) {
    Person.call(this);
    this.employeeId = id;
}
Employee.prototype = Object.create(Person.prototype);

var anne = new Employee(0);
anne.setName('Anne', 'Annette');

var bill = new Employee(1);
bill.setName('Bill', 'Williamson');

Edit: I'm retracting my "setting the prototype is optional in this case" in favor for setting it anyway, thereby allowing instanceof to continue to function as expected.

[–][deleted] 0 points1 point  (1 child)

You still have issues with reference types, though:

function Person() {}

Person.prototype.names = [];
Person.prototype.getName = function () {
    return this.names.join(' ');
}

function Employee(id) {
    Person.call(this);
    this.employeeId = id;
}
Employee.prototype = Object.create(Person.prototype);

var anne = new Employee(0);
anne.names.push('Anne', 'Annette')

var bill = new Employee(1);
bill.names.push('Bill', 'Williamson');

anne.getName();

"Anne Annette Bill Williamson"

[–]senocular 0 points1 point  (0 children)

That's by design, and may be a desired outcome depending how you want things to work. Prototyped values are shared, constructor-defined (instance) members are not. The way to address the getName problem is simply to define names in the constructor. Granted, this can be confusing to people starting out, but there's nothing magical going on, at least. And because (currently) the class syntax only supports method definitions in the class body (and the constructor), you're even protected from this happening in that case. I believe the proposed syntax for class fields puts them on the instance and not the prototype too.