2

Let's say I have an Employee class:

function Employee(name, age, salary) {
  this.name = name;
  this.age = age;
  this.salary = salary;
}

function Manager(name, age, salary, management_school_name) {
  ...
}

Manager.prototype = new Employee();
Manager.prototype.constructor = Manager;

In the above code, I want to make use of the fact that Employee encapsulates name, age and salary.

So how should I go about dealing with repeated parameters?

Khanh TO
  • 48,509
  • 13
  • 99
  • 115
batman
  • 5,022
  • 11
  • 52
  • 82

5 Answers5

4
function Employee(name, age, salary) {
  this.name = name;
  this.age = age;
  this.salary = salary;
}

function Manager(name, age, salary, management_school_name) {
  Employee.call(this,name,age,salary); //Call base constructor function
...
}
Manager.prototype = new Employee(); //Create prototype chain
Manager.prototype.constructor = Manager;

Another way to create prototype chain is using Object.create.

Manager.prototype = Object.create(Employee.prototype); //Create prototype chain

This is how Object.create is implemented internally:

function create(proto) {
  function F() {};      
  F.prototype = proto ;
  return new F();      
}

So when should we use Object.create and new Employee() to create prototype chain?

Object.create does not have any construction logic to create an object while we could have construction logic inside Employee, like this.default = "default". In this case, using new Employee() is not much different from Object.create. But if we need to avoid construction logic completely, we could use Object.create

Khanh TO
  • 48,509
  • 13
  • 99
  • 115
  • Is this how it is done generally? Or is my basic design itself bad? – batman Dec 22 '13 at 07:26
  • +1 But, what if I want data in the prototype as well? – thefourtheye Dec 22 '13 at 07:27
  • @learner: this is to reuse your logic to initialize `name`, `age`, `salary` – Khanh TO Dec 22 '13 at 07:27
  • @thefourtheye: the data should not be in the prototype as they will be shared. It should be per instance – Khanh TO Dec 22 '13 at 07:27
  • @KhanhTO Sorry, what I really meant was data and functions. – thefourtheye Dec 22 '13 at 07:28
  • @thefourtheye, can you please explain what you meant? – batman Dec 22 '13 at 07:29
  • @KhanhTO, yes, but is this how it is done in the industry? Is this a good design on the whole? – batman Dec 22 '13 at 07:30
  • @thefourtheye: i mean we should only put methods in the prototype to be shared. Properties should be per instance – Khanh TO Dec 22 '13 at 07:30
  • @learner Lets say I have an Object `A` with a function constructor, which has `method1` in its prototype. Now, I want to create Object `B` with some other function constructor and I want to inherit `method1` from the prototype. – thefourtheye Dec 22 '13 at 07:31
  • @KhanhTO Thats fine, but how do I inherit the prototype methods, in this case? – thefourtheye Dec 22 '13 at 07:32
  • 1
    @thefourtheye: in the question, this line does the job of creating prototype chain: `Manager.prototype = new Employee();` – Khanh TO Dec 22 '13 at 07:36
  • @learner: It's hard to say that this is done in the industry. Everybody has different opinions of which is the best approach. – Khanh TO Dec 22 '13 at 07:38
  • @thefourtheye: You have to modify the prototype to add methods to it, if not, how to add methods? We just need to ensure that adding methods to a subclass's prototype has no impact on the super's class prototype. In this case `new Employee();` does the job. It would be wrong if we do it like `Manager.prototype = Employee.prototype;` – Khanh TO Dec 22 '13 at 07:41
  • @KhanhTO So, I believe this is the only way to do prototypal inheritance?!? – thefourtheye Dec 22 '13 at 07:43
  • @thefourtheye: there is another way (using object.create) like: `Manager.prototype = Object.create(Employee.prototype)`;Actually, `Object.create` returns a new object with the prototype set to the passed in parameter. In this case, this approach is similar to `new Employee();` – Khanh TO Dec 22 '13 at 07:46
  • @KhanhTO, is there a reason to prefer `Object.create` over `new Super_Class()` generally? – batman Dec 22 '13 at 07:50
  • AFAIK `new` runs the constructor, `Object.create` does not. Also you can do `Employer.apply(this, arguments)` – elclanrs Dec 22 '13 at 07:52
  • @learner: `Object.create` does not have any construction logic to create an object. In your case, your Employee class also does not. If we have a line inside your Employee class like `this.abc="default"`; and want to avoid this, you could use `Object.create` – Khanh TO Dec 22 '13 at 07:53
  • @KhanhTO Looks like its little complicated, I ll add a separate question. – thefourtheye Dec 22 '13 at 08:03
  • 1
    @thefourtheye You use `Child.prototype=Object.create(Parent.prototype);` When your Parent has instance variables like `this.someObject=[]` All Child instances will have someObject on the prototype but if I create ben and then jerry as Child and do `ben.someObject.push("value from ben");` jerry.someObject will have the value ["value from ben"] because Child objects share the `someObject` member. An explanation about instance specific members and shared prototype members can be found in the introduction here: http://stackoverflow.com/a/16063711/1641941 – HMR Dec 22 '13 at 08:18
  • 1
    @learner You should not set Child.prototype to an instance of Parent because Parent instance specific members are set on Child.prototype and all Child instances will share members that are meant to be instance specific, see comment to thefourtheye. At best you'll immediately shadow these instance specific members but you're wasting memory defining members on Child.prototype that are immediately shadowed. – HMR Dec 22 '13 at 08:29
1

You can try this:

Function.prototype.inherit = function(Parent) {
    this.prototype = Object.create(Parent.prototype)
    this.prototype.constructor = this
}

Source (Bulgarian Lang)

You can see also this demo

Ifch0o1
  • 900
  • 1
  • 14
  • 32
1

As stated before; it's best not to create an instance of Parent to set at the prototype of Child. To re use Parent constructor you can do Parent.call(this,args); in Child.

I like to use an arguments object for better readability and when a bunch of functions are called in a chain the functions can take out and mutate the part that concerns them. For example

function Employee(args) {
  //name is mandatory
  if(typeof args.name === "undefined")
    throw new Error("Cannot create an Employee instance without passing a name.");
  this.name = args.name;
  //age is optional and default is 25
  this.age = (typeof args.age === "undefined")? 25:args,age;
  //optional, defaults to 2500
  this.salary = (typeof args.salary === "undefined")?2500:args.salary;
}

function Manager(args) {
  //re use Employee constructor
  Employee.call(this,args);
  //set Employee specific values same as Employee
}
Manager.prototype = Object.create(Employee.prototype);
Manager.prototype.constructor = Manager;

var m = new Manager({name:"Harry",age:33,salary:5000});

More info about constructor functions and prototype here.

Community
  • 1
  • 1
HMR
  • 37,593
  • 24
  • 91
  • 160
  • Thanks. Is this an example of prototypal or pseudo-classical inheritance? – batman Dec 22 '13 at 08:42
  • @learner I guess it is. This was the only way to do it for a long time. Recently there is another way to do it with Object.create but you'll have add an init or constructor function to initialise instance specific members so it basically comes down to the same thing. – HMR Dec 22 '13 at 08:57
  • prototypal and pseudo-classical are quite different actually. – batman Dec 22 '13 at 09:08
  • @learner Interesting, after I answer you you answered your own question. Could you show me the differences? In my understanding and the patterns I've seen used is that the Child's prototype is set to a shallow copy of the Parent and either constructor or init function is used when creating instances to set instance specific members. The code looks different as you can do it in hundreds of different ways but the outcome is basically the same. I could be wrong and would be happy if you could show the the major differences. – HMR Dec 22 '13 at 09:25
0

Manager is an staff too, so why don't you do this:

function Staff(name, age, salary) {
  this.name = name;
  this.age = age;
  this.salary = salary;
}

function Position(positionName, management_school_name) {
   this.positionName = positionName;
   this.managent_school_name = management_shool_name;
}

Staff.prototype.position = new Position();
Ringo
  • 3,795
  • 3
  • 22
  • 37
  • can you please explain? I don't really follow. – batman Dec 22 '13 at 07:41
  • In my example I merge your Manager and Employee class into one Staff class. Now Manager and Employee are Staff too and so they use the same infos: name, age, salary. If you want to make a Staff as Manager, just set it's position prototype. Sorry for my English! – Ringo Dec 22 '13 at 07:47
0

It is properly explained in above posts WHAT you wanted to achieve. But AFAIK below lines will explaing WHY it is so.

There are two ways you can add public properties and methods to your class (rather function class) :

Method 1 of adding public property, added to each instance:

function MyClass(){
      this.publicProperty = 'public property'; 
}

Method 2 of adding public property, added to prototype, common for all instances:

MyClass.prototype.publicMethod = function(){
      console.log('public method');
}

When you want to inherit from a Class you need to inherit all the public properties and methods.

Inheriting properties and methods added using method 1:

function MyDerivedClass(){
      MyClass.apply(this);
}

Inheriting properties and methods added using method 2:

MyDerivedClass.prototype = Object.create(MyClass.prototype);

Hope this helps.

Niranjan Borawake
  • 1,628
  • 13
  • 20