Javascript inheritance with eventListener - javascript

I have a class designMain which extends design.
I have found that if I set an eventListener in design, when the event is fired, all references to this will only refer to the non extended design object. Is there a way to overcome this shortcoming of my extended class?
UPDATE - I've included an ES6 version which works as expected and shows what I am trying to accomplish - namely that this with extended classes will always refer to the extended class even with code (e.g. addEventListener) within the base class. In this case this.name should always be from the extended class.
Here is a jsFiddle
HTML
<div id="test">
Design (click will fail)
</div>
<div id="test2">
DesignMain (click will succeed)
</div>
javascript
design = (function() {
function design() {
this.name = "design";
var _this = this;
var e = document.getElementById("test");
e.addEventListener("click", function() {
_this.callHello();
});
}
design.prototype.callHello = function() {
// I expect that this.name will be "designMain"
console.log(this.name)
// will fail if called from the design class eventListener
this.hello();
}
return design;
})();
designMain = (function() {
function designMain() {
this.name = "designMain";
this.init();
}
designMain.prototype.init = function() {
this.extend();
var _this = this;
var e = document.getElementById("test2");
e.addEventListener("click", function() {
_this.callHello();
});
}
designMain.prototype.extend = function() {
var old = new design();
// save reference to original methods
this._designMain = Object.create(old);
for (var name in this._designMain) {
if (!this[name]) {
this[name] = this._designMain[name]
}
}
}
designMain.prototype.hello = function() {
alert("Hello " + this.name);
}
return designMain;
})();
var t = new designMain();
Using ES6 - it works as expected (see fiddle)
class design {
constructor() {
this.name = "design";
var _this = this;
var e = document.getElementById("test");
e.addEventListener("click", function() {
_this.callHello();
});
};
callHello() {
// should be "designMain"
console.log(this.name)
this.hello();
}
get name() {
return this._name;
};
set name(name) {
this._name = name;
};
}
class designMain extends design {
constructor() {
super();
this.name = "designMain";
var _this = this;
var e = document.getElementById("test2");
e.addEventListener("click", function() {
_this.callHello();
});
};
hello() {
alert("Hello " + this.name);
}
}
t = new designMain();

Thanks to Bergi and the link he posted - I have a solution. Extending / inheriting was way simpler using Object.create and I only needed to add design.call(this) to run the constructor of the base class.
designMain = (function() {
designMain.prototype = Object.create(design.prototype);
function designMain() {
design.call(this)
Working solution:
design = (function() {
function design() {
this.name = "design";
var _this = this;
var e = document.getElementById("test");
e.addEventListener("click", function() {
_this.callHello();
});
}
design.prototype.callHello = function() {
// will fail if called from the design class eventListener
console.log(this.name)
this.hello();
}
return design;
})();
designMain = (function() {
designMain.prototype = Object.create(design.prototype);
function designMain() {
design.call(this)
this.name = "designMain";
this.init();
}
designMain.prototype.init = function() {
var _this = this;
var e = document.getElementById("test2");
e.addEventListener("click", function() {
_this.callHello();
});
}
designMain.prototype.hello = function() {
alert("Hello " + this.name);
}
return designMain;
})();
var t = new designMain();

Related

How to access object properties from prototype in javascript? [duplicate]

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 6 years ago.
I have class below when I call printData I get this.collection is undefined.
How do I access this.collection from the prototype inside printData()? Or do i need to change the class structure. Actually the object returns function which intern returns object in hierarchy.
Thanks in advance!
Sample Class:
var DbProvider = (function () {
function DbProvider(db) {
var that = this; // create a reference to "this" object
that.collection = db;
}
DbProvider.prototype.create = function () {
return {
action: function () {
var y = {
printData: function () {
alert('Hello ' + this.collection.Name);
}
};
return y;
}
};
};
return DbProvider;
})();
Usage:
var a = new DbProvider({ "Name": "John" });
a.create().action().printData();
You could save the this reference and bind it to the printData function
var DbProvider = (function () {
function DbProvider(db) {
var that = this; // create a reference to "this" object
that.collection = db;
}
DbProvider.prototype.create = function () {
var self = this;
return {
action: function () {
var y = {
printData: function () {
alert('Hello ' + this.collection.Name);
}.bind(self)
};
return y;
}
};
};
return DbProvider;
})();
var a = new DbProvider({ "Name": "John" });
a.create().action().printData();
Or you could refactor a bit and move that to the outer scope of DbProvider and use that in printData
var DbProvider = (function () {
var that;
function DbProvider(db) {
that = this; // create a reference to "this" object
that.collection = db;
}
DbProvider.prototype.create = function () {
return {
action: function () {
var y = {
printData: function () {
alert('Hello ' + that.collection.Name);
}
};
return y;
}
};
};
return DbProvider;
})();
var a = new DbProvider({ "Name": "John" });
a.create().action().printData();
just need to keep track of the this pointer correctly, like this
var DbProvider = (function() {
function DbProvider(db) {
this.collection = db;
}
DbProvider.prototype.create = function() {
var self = this;
return {
action: function() {
var y = {
printData: function() {
alert('Hello ' + self.collection.Name);
}
};
return y;
}
};
};
return DbProvider;
})();
let dbProvider = new DbProvider({
Name: "test"
});
dbProvider.create().action().printData();
Keeping ES5 syntax and the call structure a solution would be:
var DbProvider = (function () {
function DbProvider(db) {
var that = this; // create a reference to "this" object
that.collection = db;
}
DbProvider.prototype.create = function () {
var that = this;
return {
action: function() {
var y = {
printData: function () {
console.log('Hello ' + that.collection.Name);
}
};
return y;
}
};
};
return DbProvider;
})();
Definitely not elegant but it works :)
If you do not want to change your structure, you can achieve this behavior if you change you functions to arrow functions.
var DbProvider = (function () {
function DbProvider(db) {
var that = this; // create a reference to "this" object
that.collection = db;
}
DbProvider.prototype.create = function() {
return {
action: () => {
var y = {
printData: () => {
alert('Hello ' + this.collection.Name);
}
};
return y;
}
};
};
return DbProvider;
})();
The way you are creating this "class" is definitely non standard. Let me know if you want an example of how to better structure it.

Javascript, Calling functions from another onclick in object constructor

I need help calling the this.load function from inside the thediv.onclick. I stripped out most of the code so its really basic, but I really can't find a way to do it. Here is what I currently have:
function CreatePizza(Name, Toppings) {
this.n = Name;
this.t = Toppings;
this.load = function loadpizza() {
//function that i want to be called
}
this.create = function button() {
var thediv = document.createElement("div");
thediv.onclick = function() {
// Call this.load function here
}
}
}
The problem is that inside the onclick handler, this will refer to the <div>, not the other this which you refer to repeatedly.
Two possible solutions:
Save a reference to your desired this:
that = this;
thediv.onclick = function () {
that.load()
};
Bind this to your function:
thediv.onclick = function () {
this.load();
}.bind(this);
Or, if that's the only thing you're doing in that function anyway:
thediv.onclick = this.load.bind(this);
Because of closures, you can simply assign this to a variable and call it from that!
function CreatePizza(Name, Toppings) {
var self = this;
this.n = Name;
this.t = Toppings;
this.load = function loadpizza() {
//function that i want to be called
}
this.create = function button() {
var thediv = document.createElement("div");
thediv.onclick = function() {
self.load();
}
}
}
I would like to mention that a nicer - and not necessarily better, before anyone starts a flamewar - way to attach events to your div (and more elegant in my opinion) is to use thediv.addEventListener('click', self.load, false). Just a side-note, though.
Backup the this object before binding the event.
this.create = function button() {
var that = this,
thediv = document.createElement("div");
thediv.onclick = function() {
// Call this.load function here
that.load();
}
}
function CreatePizza(Name, Toppings) {
this.n = Name;
this.t = Toppings;
var foo = function loadpizza() {
//function that i want to be called
};
this.load = foo;
this.create = function button() {
var thediv = document.createElement("div");
thediv.onclick = function() {
foo();
}
}
}
function CreatePizza(Name, Toppings) {
this.n = Name;
this.t = Toppings;
this.load = function loadpizza() {
//function that i want to be called
}
var self = this;
this.create = function button() {
var thediv = document.createElement("div");
thediv.onclick = function() {
self.load()
}
}
}

How can I add OOP to these JS functions?

I know this may seem like a repeated question, but I am currently stuck as to the best way to approach this, limited mostly by my lack of knowledge. Thus I am here to learn.
I am trying to do some simple OOP with JavaScript but coming from C# I am having a few issues with how to best solve this problem. Below I have four "Classes"; DisplayEngine, DisplayElement, Box, and Grid.
I would like Box and Grid to inherit DisplayElement, and be able to call the base functions in each of their respective functions. Almost like super.call() or something.
How would you best approach this?
var DisplayEngine = function() {
this.elements = [];
this.add = function(element) {
this.elements.push(element);
};
this.update = function() {
this.elements.forEach(function(element) {
element.update();
})
};
this.draw = function() {
this.elements.forEach(function(element) {
element.draw();
})
};
};
var DisplayElement = function() {
this.update = function() {
console.log('DisplayElement update');
};
this.draw = function() {
console.log('DisplayElement draw');
};
};
var Box = function() {
this.update = function() {
console.log('Box update');
// call DisplayElement.update()
};
this.draw = function() {
console.log('Box draw');
// call DisplayElement.draw()
};
};
var Grid = function() {
this.update = function() {
console.log('Grid update');
// call DisplayElement.update()
};
this.draw = function() {
console.log('Grid draw');
// call DisplayElement.draw()
};
};
$(function() {
var displayEngine = new DisplayEngine();
var box = new Box();
var grid = new Grid();
displayEngine.add(box);
displayEngine.add(grid);
displayEngine.update();
displayEngine.draw();
});
Here is a way to do it with prototype, each "class" need to be in his own file, the important part is Grid.prototype = new DisplayElement(); This allow you to call function from the DisplayElement in Grid:
DisplayEngine.js
function DisplayEngine() {
this.elements = [];
}
DisplayEngine.prototype.add = function(element) {
this.elements.push(element);
}
DisplayEngine.prototype.update = function() {
this.elements.forEach(function(element) {
element.update();
})
}
DisplayEngine.prototype.draw = function() {
this.elements.forEach(function(element) {
element.draw();
})
}
DisplayElement.js
function DisplayElement() {
}
DisplayElement.prototype.updateElement = function() {
console.log('DisplayElement update');
}
DisplayElement.prototype.drawElement = function() {
console.log('DisplayElement draw');
}
Box.js
function Box() {
}
Box.prototype = new DisplayElement();
Box.prototype.update = function() {
console.log('Box update');
this.updateElement();
}
Box.prototype.draw = function() {
console.log('Box draw');
this.drawElement();
}
Grid.js
function Grid() {
}
Grid.prototype = new DisplayElement();
Box.prototype.update = function() {
console.log('Grid update');
this.updateElement();
}
Box.prototype.draw = function() {
console.log('Grid draw');
this.drawElement();
}
Main.js
$(function() {
var displayEngine = new DisplayEngine();
var box = new Box();
var grid = new Grid();
displayEngine.add(box);
displayEngine.add(grid);
displayEngine.update();
displayEngine.draw();
});
To just answer to your question, declare your objects such as :
function DisplayElement() {};
DisplayElement.prototype.update = function() {
console.log('DisplayElement update');
};
DisplayElement.prototype.draw = function() {
console.log('DisplayElement draw');
};
// ...
// Now, instanciation :
var myElement = new DisplayElement();
Then, for inheritance :
function Box() {
DisplayEngine.call(this, arguments); // Call the super constructor
}
Box.prototype = Object.create(DisplayEngine.prototype); // "Apply" the inheritance
Box.prototype.constructor = Box; // Redefine the constructor to Box because it was overriden by the previous line
I disagree about those saying that you doesn't need "classes" in Javascript. In implementations such as Node.js which will handle datas must have, in my opinion, classes. It's easier (always in my opinion) to read, maintain, and use.
You can use the prototypal style like what Shryme explained, or you can use a library that mimcs the classical oop style in javascript, check Classing{js} : http://www.classingjs.co.nf/

Javascript extend class with props?

I have class defined as:
function MyClass() {
}
MyClass.prototype = {
init: function() {
alert('My parent class!');
},
method1: function() {},
method2: function() {}
};
and properties object:
{
init: function() {
MySubClass.superclass.init.apply(this, arguments);
alert('test!');
},
test: function() {
alert();
}
}
and i need function which will be extends base class (MyClass) with props (object) and return NEW extended subclass (to MySubClass):
MySubclass = extend(MyClass, {
init: function() {
MySubClass.superclass.init.apply(this, arguments);
alert('test!');
},
test: function() {
alert();
}
});
Constructor must be replaced by new constructor (from init).
I need a correct way.
Why it's not working good?
extend: function(bc, o) {
// result class (apply only first constructor)
var cls = function() {};
cls.prototype = bc.prototype;
for (var k in o)
cls.prototype[k] = o[k];
cls.superclass = bc.prototype;
return cls;
}
Your extend function would have to look something like this - now this is much simpler than how you should really implement it but it should work:
var extend = function(Super, props) {
var sinstance = new Super()
var sclass = props.constructor || sinstance.constructor;
sclass.prototype.super = sinstance;
sclass.prototype.constructor = Super;
//use underscore extend prototypes cause im too lazy to type it out
_.extend(sclass.prototype, sinstance, props);
return sclass;
}
Calling subclass.super.call(this, props...) allows you access overridden super methods.
Just tested and this works if underscore js is on the page:
function MyClass() {
}
MyClass.prototype = {
init: function() {
alert('My parent class!');
},
method1: function() {},
method2: function() {}
};
MySubclass = extend(MyClass, {
init: function() {
this.super.init.apply(this, arguments);
alert('test!');
},
test: function() {
alert();
}
});
var test = new MySubclass();
test.init("yo"); //alerts My parent class
For your update you can also do this:
MySubclass2 = extend(MyClass, {
constructor: function() {
this.init();
},
init: function() {
this.super.init.apply(this, arguments);
alert('test!');
},
test: function() {
alert();
}
});
var test2 = new MySubclass2();//alerts My parent class
If you are open to using a library, use _.extend from underscore.js. Otherwise, add this code to your codebase:
extend = function(obj) {
each(slice.call(arguments, 1), function(source) {
if (source) {
for (var prop in source) {
obj[prop] = source[prop];
}
}
});
return obj;
};
I see you've tagged with jQuery - if you are indeed using jQuery, you may be interested in using $.extend.
Take a look at this: https://github.com/haroldiedema/joii
var BaseClass = function()
{
this.some_var = "foobar";
/**
* #return string
*/
this.someMethod = function() {
return this.some_var;
}
};
var MyClass = new Class({ extends: BaseClass }, function()
{
/**
* #param string value
*/
this.__construct = function(value)
{
this.some_var = value;
}
})
Usage:
var obj = new MyClass("Hello World!");
console.log( obj.someMethod() ); // Hello World!

"This" not refering to current object

I am kind of new to OOP in JS. I would like to know why when creating sub-objects, this stops referring to the main object AFTER the second level of subobjects.
function Clase()
{
this.__construct = function()
{
this.paginator();
alert('__construct finished');
};
this.paginator = function()
{
this.paginator.title = function()
{
this.paginator.title.set_offsets = function()
{
alert('paginator.title.set_offsets executed!');
};
};
this.paginator.title(); //instantiating
alert('subobject paginator created');
};
this.__construct();
}
var instancia = new Clase();
instancia.paginator.title.set_offsets();
http://jsfiddle.net/WYWwE/
The error is: this.paginator is undefined.
And now, if I use closures, it works perfectly:
function Clase()
{
self = this;
this.__construct = function()
{
this.paginator();
alert('__construct finished');
};
this.paginator = function()
{
self.paginator.title = function()
{
self.paginator.title.set_offsets = function()
{
alert('instancia.paginator.title.set_offsets() executed');
};
};
self.paginator.title();
alert('this.paginator created');
};
this.__construct();
}
var instancia = new Clase();
instancia.paginator.title.set_offsets();
http://jsfiddle.net/esjHu/
So, AFAIK after some point, "this" stops refering to the class "Clase" and refers to something else. If so, is it a good practice to use closures this way?
Is it also correct to start the class with self = this; and from then on use ONLY "self"? for instance: http://jsfiddle.net/byGRX/
You lose the reference to the "original" this when you nest functions. To remedy do the following:
function Clase() {
var that = this;
this.paginator = {
title: {
set_offsets: function() {
alert('paginator.title.set_offsets executed!');
}
}
};
};
var foo = new Clase();
foo.paginator.title.set_offsets();​
http://jsfiddle.net/vd5YK/
You don't lose reference to the this object, here's what happens:
For example:
function Class() {
this.func1 = function () {
this.func1.func2 = function () {
alert('Works!');
};
};
this.func1.func2();
}
x = new Class();
Now, the reason you get an error saying that func2 doesn't exist is because the function object for func2 isn't constructed until you call func1:
function Class() {
this.func1 = function () {
this.func1.func2 = function () {
alert('Works!');
};
};
this.func1();
this.func1.func2();
}
x = new Class();
And now it works.
EDIT:
So, why doesn't this work:
function Class() {
this.func1 = function() {
this.func1.func2 = function() {
this.func1.func2.func3 = function() {
alert('works!');
};
this.func1.func2.property = 5;
};
};
this.func1();
this.func1.func2();
}
x = new Class();
x.func1.func2.func3();
Basically, what your trying to do is add a property named property and a method named func3 to the function object of func2, but the problem is that func2 isn't constructed before calling func1. It's the same as doing:
function Class() {
this.func1 = function() {
this.func1.func2 = function() {};
};
this.func1.func2.func3 = function() {
alert('works!');
};
this.func1.func2.property = 5;
this.func1();
this.func1.func2();
}
x = new Class();
x.func1.func2.func3();
If you want it to work you need first construct the function object for func2 by calling func1:
function Class() {
this.func1 = function() {
this.func1.func2 = function() {};
};
this.func1();
this.func1.func2.func3 = function() {
alert('works!');
};
this.func1.func2.property = 5;
// this.func1.func2();
}
x = new Class();
x.func1.func2.func3();
alert(x.func1.func2.property);

Categories

Resources