JavaScript inheritance not working as I expected - javascript

What are the details why pe.getName() is working and pe.getSalary() is not?
var Employee = function(name) {
this.name = name;
}
Employee.prototype.getName = function() {
return this.name;
}
var PermanentEmployee = function(annualSalary) {
this.annualSalary = annualSalary;
}
PermanentEmployee.prototype.getSalary = function() {
return this.annualSalary;
}
var employee = new Employee("Mark");
PermanentEmployee.prototype = employee;
var pe = new PermanentEmployee(5000);
document.write(pe.getName());
document.write(pe.getSalary());

By doing this later in your code
PermanentEmployee.prototype = employee;
you might override this
PermanentEmployee.prototype.getSalary = function()
Try this instead:
function Employee(name) {
this.name = name;
}
Employee.prototype.getName = function() {
return this.name;
}
function PermanentEmployee(name, annualSalary) {
Employee.call(this, name); // calling parent's "constructor"
this.annualSalary = annualSalary;
}
PermanentEmployee.prototype = Object.create(Employee.prototype); // setting "inheritance"
PermanentEmployee.prototype.getSalary = function() {
return this.annualSalary;
}
var pe = new PermanentEmployee("Mark", 5000);
console.log(pe.getName());
console.log(pe.getSalary());

This is actually the expected behavior. If you want to understand why, here we go...
Consider the following schema, available in the excellent book Exploring ES6 by Dr. Axel Rauschmayer.
Before to go on, here is an important rule: in JavaScript, when you create an instance of a "class", this instance will reference the prototype property of its constructor through its dunder proto (__proto__ or [[Prototype]]).
Top left
In your example, Employee and PermanentEmployee are constructor functions. Like any function in JavaScript, they are instances of Function and they use Function.prototype.
var Employee = function(name) {
this.name = name;
}
var PermanentEmployee = function(annualSalary) {
this.annualSalary = annualSalary;
}
console.log(typeof Object.getPrototypeOf(Employee)); // ES5
console.log(typeof PermanentEmployee.__proto__); // ES6
console.log(Object.getPrototypeOf(Employee).constructor); // ES5
console.log(PermanentEmployee.__proto__.constructor); // ES6
Top right
When you write Employee.prototype.getName or PermanentEmployee.prototype.getSalary, you are actually adding new properties to the prototype property of Employee and PermanentEmployee. But Employee.prototype and PermanentEmployee.prototype are instances of Object and use Object.prototype.
var Employee = function(name) {
this.name = name;
}
Employee.prototype.getName = function() {
return this.name;
}
var PermanentEmployee = function(annualSalary) {
this.annualSalary = annualSalary;
}
PermanentEmployee.prototype.getSalary = function() {
return this.annualSalary;
}
console.log(typeof Employee.prototype);
console.log(typeof PermanentEmployee.prototype);
console.log(Employee.prototype);
console.log(PermanentEmployee.prototype);
Since PermanentEmployee.prototype is a simple object, this is not reasonable to do so: PermanentEmployee.prototype = employee.
When you do that, you override the prototype. To make a basic comparison, look at this example with an object literal:
var obj = {
foo: 'Foo'
};
obj.bar = 'Bar';
console.log(obj);
obj = 'Baz';
console.log(obj);
obj = {
foo: 'Foo',
bar: 'Bar',
baz: 'Baz'
};
console.log(obj);
You should keep this in mind when you play with prototypes...
Bottom right
In JavaScript, there are several strategies to create a "pure" object (functions and arrays are objects too...). You can:
Use an object literal: {}
Use a constructor: new Object()
Use Object.create()
In your example, you are using constructors. Therefore, your instances will use the prototype properties of your constructors, which themselves use the prototype property of Object. This is how the prototype chain works!
var Employee = function(name) {
this.name = name;
}
Employee.prototype.getName = function() {
return this.name;
}
var PermanentEmployee = function(annualSalary) {
this.annualSalary = annualSalary;
}
PermanentEmployee.prototype.getSalary = function() {
return this.annualSalary;
}
var employee = new Employee("Mark");
console.log(Object.getPrototypeOf(employee)); // ES5
console.log(employee.__proto__.constructor); // ES6
console.log(Object.getPrototypeOf(Object.getPrototypeOf(employee))); // ES5
console.log(employee.__proto__.__proto__.constructor); // ES6
But of course, if you have completely overriden your prototype somewhere, you break the prototype chain and it will not work as expected...

Related

Inheritance in Javascript function error

I apply inheritance in JavaScript in following way:
var employee = function(name) {
this.name = name;
}
employee.prototype.getName = function() {
return this.name;
}
var pEmployee = function(salary) {
this.salary = salary;
}
pEmployee.prototype.getSalary = function() {
return this.salary;
}
var employee = new employee("mark");
pEmployee.prototype = employee;
var pe = new pEmployee(5000);
console.log(pe.getName());
console.log(pe.getSalary());
but it showing following error in console:
Uncaught TypeError: pe.getSalary is not a function
Could any one tell me what's the reason behind this error?
It's because you've added getSalary to the object pEmployee.prototype refers to, but then completely replaced pEmployee.prototype with a new object. So naturally the new object doesn't have getSalary.
What you've shown isn't the correct way to set up inheritance in ES5 and earlier. Instead, see inline comments:
var Employee = function(name) {
this.name = name;
};
Employee.prototype.getName = function() {
return this.name;
};
var PEmployee = function(name, salary) {
// Note call to superclass
Employee.call(this, name);
// Now this level's initialization
this.salary = salary;
};
// This sets up inheritance between PEmployee.prototype and
// Employee prototype (then fixes up the
// constructor property)
PEmployee.prototype = Object.create(Employee.prototype);
PEmployee.prototype.constructor = PEmployee;
// NOW you add the method
PEmployee.prototype.getSalary = function() {
return this.salary;
};
// Usage
var employee = new Employee();
var pe = new PEmployee("Mark", 5000);
console.log(pe.getName());
console.log(pe.getSalary());
See my answer here for a more thorough example, and also what it would look like if you used ES2015's class syntax instead.

Interesting JavaScript inheritance pattern

I have recently watched a video where Douglas Crockford was explaining inheritance patterns of Javascript. The video itself is pretty old - it was filmed 6 years ago - but still useful. In that video he showed one inheritance pattern he kinda invented (although I am not sure who the author is). This is the code using his approach:
// imitation of new operator
function objectConstructor(obj, initializer, methods) {
// create prototype
var func, prototype = Object.create(obj && obj.prototype);
// add methods to the prototype
if(methods) Object.keys(methods).forEach(function(key) {
prototype[key] = methods[key];
});
// function that will create objects with prototype defined above
func = function() {
var that = Object.create(prototype);
if(typeof initializer === 'function') initializer.apply(that, arguments);
return that;
}
func.prototype = prototype;
prototype.constructor = func;
return func;
}
var person = objectConstructor(Object, function(name) {
this.name = name;
}, {
showName: function() {
console.log(this.name);
}
});
var employee = objectConstructor(person, function(name, profession) {
this.name = name;
this.profession = profession;
}, {
showProfession: function() {
console.log(this.profession);
}
});
var employeeInfo = employee('Mike', 'Driver');
employeeInfo.showName(); // Mike
employeeInfo.showProfession(); // Driver
Unfortanately, he didn't show the invocation. So, this part
var employeeInfo = employee('Mike', 'Driver');
employeeInfo.showName();
employeeInfo.showProfession();
is mine. It generally works, but it turns out that I repeat this.name = name; for both "classes" - person and employee. I played around but I didn't manage to make it work properly without that repetition. Seems I cannot get name because such a property isn't contained in the prototypal chain for employee. I didn't succeed either in mixing in stuff like person.call(this, arguments). So, apart from whether it is cool/nice/smart/sensible etc. or not in 2017, how could I remove this.name = name; from employee and get the same result? Or everything is ok and this approach doesn't suppose it?
Here is your snippet with 2 small modifications so that you can do a super(name) type of call.
I've placed comments were I've made the modifications.. with prefix keith:
// imitation of new operator
function objectConstructor(obj, initializer, methods) {
// create prototype
var func, prototype = Object.create(obj && obj.prototype);
// add methods to the prototype
if(methods) Object.keys(methods).forEach(function(key) {
prototype[key] = methods[key];
});
// function that will create objects with prototype defined above
func = function() {
var that = Object.create(prototype);
if(typeof initializer === 'function') initializer.apply(that, arguments);
return that;
}
func.prototype = prototype;
//keith: store the initialization in constructor,
//keith: as func is already creating the object..
prototype.constructor = initializer;
return func;
}
var person = objectConstructor(Object, function(name) {
this.name = name;
}, {
showName: function() {
console.log(this.name);
}
});
var employee = objectConstructor(person, function(name, profession) {
//keith: call our super person(name)
person.prototype.constructor.call(this, name);
this.profession = profession;
}, {
showProfession: function() {
console.log(this.profession);
}
});
var employeeInfo = employee('Mike', 'Driver');
employeeInfo.showName(); // Mike
employeeInfo.showProfession(); // Driver
Since the func constructor completely disregards this, passing any context to it via call or apply will not work. Creating a way to copy over the super class' properties after creating an object is one of the ways you could accomplish your task.
// imitation of new operator
function objectConstructor(obj, initializer, methods) {
// create prototype
var func, prototype = Object.create(obj && obj.prototype);
// add methods to the prototype
if(methods) Object.keys(methods).forEach(function(key) {
prototype[key] = methods[key];
});
// function that will create objects with prototype defined above
func = function() {
var that = Object.create(prototype);
if(typeof initializer === 'function') initializer.apply(that, arguments);
return that;
}
func.prototype = prototype;
prototype.constructor = func;
return func;
}
function copyProperties(source, target) {
for (var prop in source) {
if (source.hasOwnProperty(prop)) {
target[prop] = source[prop];
}
}
}
var person = objectConstructor(Object, function(name) {
this.name = name;
}, {
showName: function() {
console.log(this.name);
}
});
var employee = objectConstructor(person, function(name, profession) {
copyProperties(person.apply(null, arguments), this);
this.profession = profession;
}, {
showProfession: function() {
console.log(this.profession);
}
});
var employeeInfo = employee('Mike', 'Driver');
employeeInfo.showName(); // Mike
employeeInfo.showProfession(); // Driver

Uncaught TypeError: Cannot set property 'getName' of undefined(anonymous function)

<script>
var Employee = new function(name)
{
this.name=name;
}
Employee.prototype.getName = function()
{
return this.name;
}
var PermanenetEmployee = new function(annualsalary)
{
this.annualsalary=annualsalary;
}
var employee = new Employee("rahul");
PermanenetEmployee.prototype = employee;
var pe = new PermanenetEmployee(5001);
document.write(pe.getName());
</script>
i am implementing inheritance in java script. From this code i want to print employee name like "rahul".But i am getting error in this like Uncaught TypeError: Cannot set property 'getName' of undefined(anonymous function).How to resolve this error?
Employee.prototype.getName = function()
{
return this.name;
}
This is the problem:
var Employee = new function(name)
// ------------^^^
{
this.name=name;
}
(And the same for PermanenetEmployee.)
You don't want new there. new calls the function. You want to do that later, as you have when assigning to employee.
Note that the way you're setting up inheritance between them is an anti-pattern. To make PermanenetEmployee correctly "subclass" Employee, do this:
PermanenetEmployee.prototype = Object.create(Employee.prototype);
PermanenetEmployee.prototype.constructor = PermanenetEmployee;
not
var employee = new Employee("rahul");
PermanenetEmployee.prototype = employee;
...and then have PermanenetEmployee accept name and pass it to Employee:
var PermanenetEmployee = function(name, annualsalary) {
Employee.all(this, name); // <====
// ...
};
...or better use, use ES2015 ("ES6") class (transpiling if you need to, for instance with Babel).
Here's a correct setup. I've also fixed the typo in PermanenetEmployee:
var Employee = function(name) {
this.name = name;
};
Employee.prototype.getName = function() {
return this.name;
};
var PermanentEmployee = function(name, annualSalary) {
Employee.call(this, name);
this.annualSalary = annualSalary;
};
// Set up subclass
PermanentEmployee.prototype = Object.create(Employee.prototype);
PermanentEmployee.prototype.constructor = PermanentEmployee.prototype;
PermanentEmployee.prototype.getAnnualSalary = function() {
return this.annualSalary;
};
// Using
var pe = new PermanentEmployee("Rahul", 5001);
console.log(pe.getName());
console.log(pe.getAnnualSalary());
And with ES2015:
class Employee {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
class PermanentEmployee extends Employee {
constructor(name, annualSalary) {
super(name);
this.annualSalary = annualSalary;
}
getAnnualSalary() {
return this.annualSalary;
}
}
// Using
var pe = new PermanentEmployee("Rahul", 5001);
console.log(pe.getName());
console.log(pe.getAnnualSalary());
Again, note that you need to transpile if you want to use that syntax in the wild (for now).
There are a couple ways you can get inheritance to work in JS, I am using this pattern.
First declare the base prototype:
Employee = function () {
};
Employee.prototype = {
getName: function () {}
};
And then the prototype that inherits the base:
PermanentEmployee = function () {
Employee.call(this);
};
PermanentEmployee.prototype = Object.create(Employee.prototype);
PermanentEmployee.constructor = PermanentEmployee;
PermanentEmployee.prototype.foo = function() {}

extending objects in Javascript

I'm kinda lost in getting the object extending to work. I have read dozens of sites related to this topic, but I'm still no wiser. It seems that everyone uses it's own approach to make this work, and so do I , I'm trying to find the best approach for extending/inheriting objects.
I am also aware that there are tons of frameworks/plugins out there to cover this functionality, but i'd just like to understand how it works in general. Not mentioning that most of these frameworks include lots of other stuff I may never use, hence I'm trying to make my own.
I was able to extend an object , everything seemed to be ok until I started adding methods to the target object. To understand the issue, please see the below example...
or just try this JSFiddle
The thing is, that after initializing the new instance of Rabbit object, I wasn't able to access Rabbit's method changeName.
And I don't understand why it's happening, i.e why it doesn't recognize the method.
[*] Please see my updated code below (also the JFiddle), everything now seems to be working ok.
Can anoyne please advise, if this is a good approach or what am I missing?
var Class = (function(NewClass){
if(NewClass.length != 0){
var extend = function(target, source, args) {
Object.getOwnPropertyNames(source).forEach(function(propName) {
if(propName !== "Extend")
{
Object.defineProperty(
target, propName,
Object.getOwnPropertyDescriptor(source, propName)
);
}
if (typeof source[propName] !== 'undefined'){
delete source[propName];
}
});
return target;
};
var inherit = function(source, args){
var baseClass = Object.getPrototypeOf(this);
baseClass.prototype = extend.call(this, baseClass, source, args);
};
if(NewClass.Extend){
var Class = function(){ //// New Class Constructor ////
if(typeof NewClass.Extend === 'function'){
NewClass.Extend.apply(this, arguments);
inherit.call(this, NewClass.Extend);
console.log(NewClass)
inherit.call(this, NewClass, arguments);
if(NewClass.Initialize){
NewClass.Initialize.call(this, arguments);
}
}
};
Class.prototype.constructor = Class;
return Class;
}
}
});
var Animal =(function(args){//// constructor ////
var self = this;
self.name = typeof args !== 'undefined' ? args.name : null;
self.bags = 0;
});
var Rabbit = new Class({
Extend: Animal ,
Initialize: function(){
console.log(this.name)
},
changeName: function(a){
console.log(this.name)
}
});
var LittleRabbit = new Rabbit({name: "LittleRabbit", type: "None"});
console.log(LittleRabbit instanceof Rabbit)
console.log(LittleRabbit)
LittleRabbit.changeName("alex");
your extend function work wrong, because Object.getPrototypeOf return prototype, so in more cases it object
var extend = function(source, args){
var baseClass = Object.getPrototypeOf(this);
source.apply(this, args);
//so here you just add property prototype to object, and this not same as set prototype to function.
baseClass.prototype = Object.create(source.prototype);
};
So you can fix this like in snippet below:
function Class(args) {
if (arguments.length != 0) {
var C = function() {
if (typeof args.Extend == 'function') {
args.Extend.apply(this, arguments)
}
if (args.Initialize) {
args.Initialize.call(this);
}
};
if (typeof args.Extend == 'function') {
C.prototype = Object.create(args.Extend.prototype);
}
Object.keys(args).filter(function(el) {
return ['Extend', 'Initialize'].indexOf(el) == -1
}).forEach(function(el) {
C.prototype[el] = args[el];
});
return C;
}
};
var Animal = (function(args) { //// constructor ////
var self = this;
self.name = typeof args !== 'undefined' ? args.name : null;
self.bags = 0;
});
var Rabbit = Class({
Extend: Animal,
Initialize: function() {
console.log(this.name);
},
changeName: function(a) {
this.name = a;
}
});
var LittleRabbit = new Rabbit({
name: "LittleRabbit",
type: "None"
});
console.log(LittleRabbit instanceof Rabbit);
console.log(LittleRabbit instanceof Animal);
console.log(LittleRabbit.name);
LittleRabbit.changeName('new little rabbit');
console.log(LittleRabbit.name);
I would suggest reading the MDN article detailing the JavaScript object model. It contains examples of "manually" subclassing:
function Employee() {
this.name = "";
this.dept = "general";
}
function Manager() {
Employee.call(this);
this.reports = [];
}
Manager.prototype = Object.create(Employee.prototype);
function WorkerBee() {
Employee.call(this);
this.projects = [];
}
WorkerBee.prototype = Object.create(Employee.prototype)
Translating your example to this style is simple:
function Animal(name) {
this.name = name;
this.bags = 0;
}
function Rabbit(name) {
Animal.call(this, name);
console.log(this.name);
}
Rabbit.prototype = Object.create(Animal.prototype);
Rabbit.prototype.changeName = function(name) {
this.name = name;
};
Then you can easily run your example, modified a bit:
var LittleRabbit = new Rabbit("LittleRabbit");
console.log(LittleRabbit instanceof Rabbit)
console.log(LittleRabbit)
LittleRabbit.changeName("new name");
Once you understand this, I'd recommend not building your own class creation mechanism and just use ES6 classes:
class Animal {
constructor(name) {
this.name = name;
this.bags = 0;
}
}
class Rabbit extends Animal {
constructor(name) {
super(name);
console.log(this.name);
}
changeName(name) {
this.name = name;
}
}
You can see this example in the Babel REPL. Some browsers/js runtimes natively support ES6 classes already, but you can use Babel to translate your code to ES5 for environments that don't yet.
As an aside, there is actually more that needs to be done to subclass completely correctly. A more complete example (that may not work in all environments) is this:
function Animal() {}
function Rabbit() {
Animal.call(this);
}
Rabbit.prototype = Object.create(Animal.prototype);
Rabbit.prototype.constructor = Rabbit;
Rabbit.__proto__ = Animal;
May ES6 class inheritance an option for you:
'use strict';
class Animal {
constructor( name ) {
this.name = name;
}
changeName( name ) {
this.name = name;
}
}
class Rabbit extends Animal {
constructor() {
super( 'rabbit' );
}
}
let littleRabbit = new Rabbit();
console.log( littleRabbit.name ); //log default name
littleRabbit.changeName( 'littleRabbit' ); //executing an method of animal class
console.log( littleRabbit.name ); //log changed name
You don't need the "overhead" for making OOP inheritance for old good javascript because there are "translators" out there which translate your es6 code to es5 code. For Example babel: https://babeljs.io/
I think it is worth to give it a try...

Override this.method = function() inside of a var x = function()

I have a Javascript Object:
var Dog = function() {
this.speak = function() {
return 'woof'
}
this.trick = function() {
return 'sitting'
}
}
I want to make a new object, Cat, that is based on Dog but has a different speak method:
var Cat = ???
...
this.speak = function() {
return 'meow'
}
...
So I can ultimately do this:
var tabby = new Cat();
tabby.speak() //returns 'meow'
tabby.trick() //returns 'sitting'
I have little experience with 'object-oriented Javascript' and can't seem to find an example online that reflects what I want to do, nor do I know what keywords to search for.
I thought it would be something similar to how I override functions in Backbone, but this seems different:
var Cat = Dog.extend({
//the speak function below would override the one that returns 'woof'
speak: function() {
return 'meow'
}
});
Any help is appreciated!
(Above is the simplified version of my ultimate goal - I want to override the render method inside of Rickshaw.Graph.Axis.X)
Usually in JavaScript, we'll define methods on a prototype, instead on the instance:
function Dog() {}
Dog.prototype.speak = function () {
return 'woof';
};
Dog.prototype.trick = function () {
return 'sitting';
};
The difference between defining a method on a prototype and on the instance is that prototypes allow you to share properties between all objects that have their prototype set to the same object. This object is where you define what properties are shared, and a prototype object can also have its own prototype, creating a prototype chain.
To create a simple example, let's take a look at a simple example:
var foo = { bar: 1 };
// create a new object with its prototype set to `foo`
var baz = Object.create(foo);
console.log(foo.bar); // 1
console.log(baz.bar); // 1
// prototype properties and values are shared
foo.bar = 2;
console.log(foo.bar); // 2
console.log(baz.bar); // 2
// this is an instance property, so the property is no longer shared
baz.bar = 3;
console.log(foo.bar); // 2
console.log(baz.bar); // 3
If you have a prototypal-inheritance system in place, this is what you'd do:
function Dog() {}
Dog.prototype.speak = function () {
return 'woof';
};
Dog.prototype.trick = function () {
return 'sitting';
};
function Cat() {}
Cat.prototype = Object.create(Dog.prototype);
Cat.prototype.constructor = Cat;
Cat.prototype.speak = function () {
return 'meow';
};
Remember that if you define a property on an instance, it will override the property set on the prototype, the prototype's prototype, and so on.
If you're looking for an answer to your specific situation where you have the methods defined on an instance (and it isn't a built-in constructor -- some have special behaviour), you need to do something a bit different:
function Cat() {
Dog.call(this);
this.speak = function () {
return 'meow';
};
}
What this does is it gives the Dog function the current object of the instance of Cat, on which it can set properties on. This is done by setting the value of this in Dog to the instance of Cat, just for that call using func.call(/* value of this */);.
Given your current approach, you could do this:
var Dog = function() {
this.speak = function() {
return 'woof'
}
this.trick = function() {
return 'sitting'
}
}
var Cat = function() {
Dog.call(this)
this.speak = function() {
return 'meow'
}
}
Though we're not using prototypal inheritance here. We're just assigning own properties directly to the objects. This is less efficient than using shared, inherited methods. Not to mention that the Cat creates the speak method on the Dog, then overwrites it immediately.
var tabby = new Cat();
tabby.speak() //returns 'meow'
tabby.trick() //returns 'sitting'
function Dog() {}
Dog.prototype.speak = function() {
return 'woof';
};
Dog.prototype.trick = function() {
return 'sitting';
};
function Cat(){}
Cat.prototype = Object.create(Dog.prototype);
Cat.prototype.speak = function() {
return 'meow';
};
var fluffy = new Cat();
fluffy.speak(); // 'meow'
fluffy.trick(); // 'sitting'
Basically, you want to do some Googling for "prototypal inheritance".
Same concept as the other answers but here is the javascript secret inherits function slightly simplified from google closure - https://code.google.com/p/closure-library/source/browse/closure/goog/base.js#1578
var inherits = function(child, parent) {
function tmp () {};
tmp.prototype = parent.prototype;
child.superClass_ = parent.prototype;
child.prototype = new tmp;
child.prototype.constructor = child;
};
Now you can
var Dog = function() {};
Dog.prototype.speak = function(){
return 'woof';
}
Dog.prototype.trick = function(){
return 'sitting';
}
var Cat = function(){};
inherits(Cat, Dog); //weird
Cat.prototype.speak = function(){
return "meow";
};
var HouseCat = function(){};
inherits(HouseCat, Cat);
HouseCat.prototype.speak = function(){
return "purr";
};
var dog = new Dog();
var cat = new Cat();
var houseCat = new HouseCat();
console.log(dog.speak()); //woof
console.log(cat.speak()); //meow
console.log(houseCat.speak()); //purr
console.log(houseCat.trick()); //sitting
console.log(cat instanceof Dog); //true
console.log(houseCat instanceof Cat); //true
console.log(houseCat instanceof Dog); //true
console.log(dog instanceof Cat); //false
console.log(cat instanceof HouseCat); //false

Categories

Resources