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
Related
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...
How, if possible, do I create a function constructor function?..a constructor that also has prototype methods.
I know how to create an object constructor function i.e.
Function Thing(val) { this.prop1 = val }
Thing.prototype.action = function()...
But to make create a new function with prototype prop and methods, the best I can come up with is, for example:
Function Func(val) {
var func = function(x) {return val*x};
func.val = val;
func.__proto__ = proto;
return func;
}
const proto = {
product: function(x) { return this.val*x },
};
For the sake of code hygiene, is there a more elegant solution?
try...
function Person(name) {
this.name = name;
}
// create a new instance
var person = new Person('codechimp');
console.log('person.constructor is ' + person.constructor);
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...
I have the inheritance chain Vehicle -> Motorized -> Car implemented:
function Vehicle()
{
var m_name;
this.setName = function(pName) {
m_name = pName;
};
this.getName = function() {
return m_name;
};
}
function Motorized()
{
var m_started = false;
this.start = function() {
m_started = true;
console.log(getName() + " started");
};
}
function Car()
{ }
//set up the inheritance chain
Motorized.prototype = new Vehicle();
Car.prototype = new Motorized();
// use
var lCar = new Car;
lCar.setName("Focus");
console.log(lCar.getName()); // Focus
lCar.start(); // ReferenceError: getName is not defined
When I invoke lCar.start() (defined in function Motorized), I get an ReferenceError: getName is not defined. How can I use the inherted method getName() in my subclass Motorized?
Because Javascript doesn't know where to look for your getName() method. You can clarify the syntax declaring a self variable that always points to the right object, like this:
function Vehicle()
{
var self = this; // Vehicle
var m_name;
this.setName = function(pName) {
self.m_name = pName;
};
this.getName = function() {
return self.m_name;
};
}
function Motorized()
{
var self = this; // Motorized
var m_started = false;
this.start = function() {
/*
`self` is Motorized, with proto Vehicle, so
has a getName() method.
`this` instead is the object where you call
start() from, i.e. Car, in the example down below.
*/
self.m_started = true;
console.log(self.getName() + " started");
};
}
function Car()
{ }
//set up the inheritance chain
Motorized.prototype = new Vehicle();
Car.prototype = new Motorized();
// use
var lCar = new Car;
lCar.setName("Focus");
console.log(lCar.getName()); // Focus
lCar.start(); // Focus started
Note that in this case, using the keyword this instead of self throughout the code would have worked as well, but you definitely cannot omit it before getName(). Also, if you are planning to add more code later on, such as event handlers in jQuery, having a clear reference to the class you're coding in can be useful, as this can become easily ambiguous, at least from the human point of view.
Anyway, whether using self is a bad coding pattern or not is the topic of this question; the point in your example is that you need to call self.getName() or this.getName().
if (typeof Object.create !== 'function') {
Object.create = function (o) {
function F() {
}
F.prototype = o;
var f = new F();
if(f.init){
f.init();
};
return f;
};
}
var inherit = function(P, C) {
var i;
for(i in P) {
// if is the current parent
if(P.hasOwnProperty(i) === false) {
continue;
};
// define the uper property
C.uper = {};
// methods
if(typeof P[i] === 'function') {
// set as super
C.uper[i] = P[i];
// if child already defined, skip
if(typeof C[i] === 'function') {
continue;
};
C[i] = P[i];
}
// properties
else {
// if child already defined a property, skip
if(!(typeof C[i] === 'undefined')) {
continue;
};
C[i] = P[i];
}
}
return C;
}
var Parent1 = (function(){
var that = {};
// private var
var _name = 'Parent1';
// public var
that.lastName = 'LastName';
// public method
that.getName = function(){
// if this.uper.getName.call(this)
return _name + this.lastName;
// else
// return _name + that.lastName;
}
// return the literal object
return that;
}());
var Parent2 = {
// fake private var
_name: 'Parent2',
// public method
getName: function(){
// as we call this method with the call method
// we can use this
return this._name;
}
}
var Child1 = inherit(Parent1, (function(){
var that = {};
// overriden public method
that.getName = function(){
// how to call the this.uper.getName() like this?
return 'Child 1\'s name: ' + this.uper.getName.call(this);
}
that.init = function(){
console.log('init');
}
// return the literal object
return that;
}()));
var Child2 = inherit(Parent2, {
getName: function(){
// how to call the this.uper.getName() like this?
return 'Child 2\'s name: ' + this.uper.getName.call(this);
}
});
var child1 = Object.create(Child1);
// output: Child 1's name: Parent1LastName
console.log(child1.getName());
var child2 = Object.create(Child2);
// output: Child 2's name: Parent2
console.log(child2.getName());
// how to call the this.uper.getName() like this?
how to call the this.uper.getName() like this?
Yes
Javascript uses Prototypal inheritance. So essentially objects inherit Objects (and everything is an Object)
Here are a couple links that should help get the point across.
Javascript Module Pattern
Module Pattern In-depth
Here's the basic module pattern:
var MODULE = (function (my) {
my.anotherMethod = function () {
// added method...
};
return my;
}(MODULE));
Then you can do something like this to mimic inheritance:
var MODULE_TWO = (function (old) {
var my = {},
key;
for (key in old) {
if (old.hasOwnProperty(key)) {
my[key] = old[key];
}
}
var super_moduleMethod = old.moduleMethod;
my.moduleMethod = function () {
// override method on the clone, access to super through super_moduleMethod
};
return my;
}(MODULE));
This style of coding takes a bit of getting used to, but I definitely prefer it to classical inheritance at this point. If this code isn't making sense, check out the Douglas Crockford lectures and it should clarify most of it.
addressing the edit:
You can create different instantiations of these objects by using the new operator.
OR
I'd recommend using this little method which extends the Object Prototype (again if this doesn't make sense see the Douglas Crockford video). I forget the exact reasons why this is so heavily recommended by him, but at the very least it eliminates some confusion in that the new operator is a bit different than in classical languages. Needless to say using only using the new operator is insufficient.
What this function does is extends the Object prototype with a method create. It then...
Defines function F in a contained namespace.
Assigns the function F's prototype to the object that is passed
returns the newly constructed Object.
(outlined better by douglas crockford himself in the prototypal inheritance link)
if (typeof Object.create !== 'function') {
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
}
newObject = Object.create(oldObject);
So using your code...
var a = Object.create(MODULE_TWO),
var b = Object.create(MODULE_TWO);
Answering based on your last edit, you could use something like this:
function Class(ctor, parent) {
var c = Function.prototype.call;
function clas() {
// expose the parent as super
this.super = parent;
ctor.apply(this, arguments);
}
// save the constructor
clas.constructor = ctor;
// provide a static constructor
clas.init = function() {
c.apply(parent.constructor, arguments);
};
// Setup the prototype
clas.prototype = parent ? parent.prototype : {};
// provide an extend method
clas.extend = function(methods) {
for(var i in methods) {
if (methods.hasOwnProperty(i)) {
clas.prototype[i] = methods[i];
}
}
return clas;
};
return clas;
}
Examples:
var Animal = Class(function(name) {
this.name = name;
});
var Cat = Class(function(name) {
this.super(name);
}, Animal).extend({
meow: function() {
console.log('Meow! My name is ' + this.name + '.');
}
});
new Cat('Neko').meow();
There are at least a trillion different ways to implement "Classes" in JavaScript, the more you want to hide the internals the more "magical" the code becomes, the above is very simple though.
You can (and probably need) customize this to fit your needs. But always keep in mind that there might be situations where a full blown Class emulation approach might not be the best one.
I already posted it as a comment, but in case you want to have everything hidden away for you, I've written a pretty feature rich, but still fast, Class library my own:
https://github.com/BonsaiDen/neko.js