4

I want the prototype of these instances to be the same, but the following equality check evaluates to false.

var emp1 = new(EmployeeScope())("John");
var emp2 = new(EmployeeScope())("Amit");
var mgr1 = new(ManagerScope())("Robert", "Data");
var mgr2 = new(ManagerScope())("Alex", "Science");
emp1.getName() // John
emp2.getName() // Amit
mgr1.getDept() // Data
mgr2.getDept() // Science
mgr1.getName() // Robert
mgr2.getName() // Alex

emp1.__proto__ === emp2.__proto__ //false
mgr1.__proto__ === mgr2.__proto__ //false

function EmployeeScope() {
  var name;

  function Employee(newName) {
    name = newName;
  }
  Employee.prototype.getName = function() {
    return name
  };
  Employee.prototype.setName = function(newName) {
    name = newName
  };
  return Employee;
}

function ManagerScope() {
  var Employee = EmployeeScope();
  var dept;

  function Manager(newName, newDept) {
    new Employee(newName);
    dept = newDept;
  }
  Manager.prototype = Object.create(Employee.prototype);
  Manager.prototype.constructor = Manager;
  Manager.prototype.getDept = function() {
    return dept
  };
  Manager.prototype.setDept = function(newDept) {
    dept = newDept
  };
  return Manager;
}

2 Answers2

1

The reason the two objects have different prototypes is that the constructor functions Employee and Manager are created again at each call of the wrapper functions you put them in. So they represent different constructors when called in different calls of the wrapper functions.

A common solution to let the object methods access private members, is by defining them not on the prototype, but on the instances. That way you can define them in the constructor scope:

function Employee(newName) {
    var name = newName;
    this.getName = function() {
        return name
    };
    this.setName = function(newName) {
        name = newName
    };
}

function Manager(newName, newDept) {
    var dept = newDept;
    // Inherit from Employee
    Employee.call(this, newName);

    this.getDept = function() {
        return dept
    };
    this.setDept = function(newDept) {
        dept = newDept
    };
}

var emp1 = new Employee("John");
var emp2 = new Employee("Amit");
var mgr1 = new Manager("Robert", "Data");
var mgr2 = new Manager("Alex", "Science");

console.log(emp1.getName()) // John
console.log(emp2.getName()) // Amit
console.log(mgr1.getDept()) // Data
console.log(mgr2.getDept()) // Science
console.log(mgr1.getName()) // Robert
console.log(mgr2.getName()) // Alex

console.log(Object.getPrototypeOf(emp1) === Object.getPrototypeOf(emp2)); 
console.log(Object.getPrototypeOf(mgr1) === Object.getPrototypeOf(mgr2)); 

Note that it is advised to use Object.getPrototypeOf() instead of __proto__.

Secondly, you should declare local variables with var (or let, const), because otherwise the variable is silently declared as a global variable, and you would get the same name for every employee.

trincot
  • 317,000
  • 35
  • 244
  • 286
  • I had thought of this approach. It changes the output of getName(). All calls to getName() returns the name of the last created employee.... – Gourav Kumar Shaw Feb 14 '17 at 04:21
  • That is because `var` is missing in the declaration of the `name` in the Employee constructor: it then becomes a global variable. But with `var` it works. – trincot Feb 14 '17 at 05:51
1

Maybe it's easier to understand why they don't share the same prototype when you write the code as following:

var EmployeeA = EmployeeScope();
var EmployeeB = EmployeeScope();
EmployeeA === EmployeeB // false
EmployeeA.prototype === EmployeeB.prototype // false
var emp1 = new EmployeeA("John");
var emp2 = new EmployeeB("Amit");
Object.getPrototypeOf(emp1) === EmployeeA.prototype // true
Object.getPrototypeOf(emp2) === EmployeeB.prototype // true

Having your EmployeeScope create a new class (constructor + prototype) on every new call isn't the best idea. Also you can only use it for a single instance, given that name is statically stored:

var emp3 = new EmployeeB("Dirk");
Object.getPrototypeOf(emp2) === Object.getPrototypeOf(emp3) // true
emp2.getName() // Dirk - oops

Your Manager class suffers from the same problem of course. So drop those "scope" functions and make them standard classes:

function Employee(newName) {
  this.name = newName;
}
Employee.prototype.getName = function() {
  return this.name
};
Employee.prototype.setName = function(newName) {
  this.name = newName
};


function Manager(newName, newDept) {
  Employee.call(this, newName);
  this.dept = newDept;
}
Manager.prototype = Object.create(Employee.prototype);
Manager.prototype.constructor = Manager;
Manager.prototype.getDept = function() {
  return this.dept
};
Manager.prototype.setDept = function(newDept) {
  this.dept = newDept
};

var emp1 = new Employee("John");
var emp2 = new Employee("Amit");
var emp3 = new Employee("Dirk");
var mgr1 = new Manager("Robert", "Data");
var mgr2 = new Manager("Alex", "Science");
Object.getPrototypeOf(emp1) === Employee.prototype // true
Object.getPrototypeOf(emp2) === Employee.prototype // true
Object.getPrototypeOf(emp3) === Employee.prototype // true
Object.getPrototypeOf(mgr1) === Manager.prototype // true
Object.getPrototypeOf(mgr2) === Manager.prototype // true
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • The problem with this approach is that it makes the name and dept properties publicly accessible. Being able to keep the variables private was my main motivation behind wrapping the functions in scopes. – Gourav Kumar Shaw Feb 14 '17 at 04:39
  • In that case, [create a local variable (with `var`) in the *constructor*](http://stackoverflow.com/a/13418980/1048572), not some scope surrounding it. And define all methods that need to access it as closures inside the constructor scope. However, keeping `name` and `dept` "private" really isn't necessary given that there are publicly available setter and getter methods. Rather drop those and keep only the property. Simplicity rules :-) – Bergi Feb 14 '17 at 04:56
  • The very same approach was suggested by Trincot's answer. It works. Thanks a lot. – Gourav Kumar Shaw Feb 14 '17 at 07:03