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);
Related
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();
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.
I have a base object ProfileDialog which I am extending with Object.assign().
var ProfileDialog = function (containerObj) {
this.init = function () {
this.container = containerObj;
};
this.render = function () {
let content = document.createElement('div');
content.innerText = 'Dialog here';
this.container.appendChild(content);
};
this.init();
this.render();
};
Mixin:
var DialogMixin = function () {
return {
open: function () {
this.container.style.display = 'block';
},
close: function () {
this.container.style.display = 'none';
}
}
};
Now I do the assignment:
Object.assign(ProfileDialog.prototype, DialogMixin());
It works just fine, this context resolves fine in open and close methods.
But, when I put the mixin in a deeper structure, putting it inside actions property:
var DialogMixin = function () {
return {
actions: {
open: function () {
this.container.style.display = 'block';
},
close: function () {
this.container.style.display = 'none';
}
}
}
};
The context becomes actions object so the code breaks.
How do I properly extend the object with new methods when they are put in a deep structure?
The only thing i can think of is using bind to bind this.
So something like
var ProfileDialog = function (containerObj) {
this.containerObj = containerObj;
};
var DialogMixin = function (newThis) {
var obj = {
actions: {
open: function () {
console.log('open', this, this.containerObj.style);
}
}
}
obj.actions.open = obj.actions.open.bind(newThis);
return obj;
};
var container = {
style : 'some style'
};
var dialog = new ProfileDialog(container);
var mixinDialog = Object.assign(dialog, DialogMixin(dialog));
mixinDialog.actions.open();
See https://jsfiddle.net/zqt1me9d/4/
In the following function, my objects inside floatShareBar function is undefined. Do I have to init or define a var before the functions? it throws me js error : .float - function undefined.
(function($) {
.
.
.
$("body").on("ab.snap", function(event) {
if (event.snapPoint >= 768) {
floatShareBar.float()
} else {
floatShareBar.unfloat();
}
});
var floatShareBar = function() {
var fShareBar = $('#article-share');
this.float = function() {
console.log(
};
this.unfloat = function() {
console.log("unfloat");
};
};
.
.
.
})(jQuery);
You need to get an instance of that function with a self instantiating call:
var floatShareBar = (function() {
var fShareBar = $('#article-share');
this.float = function() {
console.log('float');
};
this.unfloat = function() {
console.log("unfloat");
};
return this;
})();
UPDATE 1: I modified it to create an object within the function to attach those functions to, since in the previous example this refers to the window object
var floatShareBar = (function() {
var fShareBar = $('#article-share');
var instance = {};
instance.float = function() {
console.log('float');
};
instance.unfloat = function() {
console.log("unfloat");
};
return instance;
})();
UPDATE 2: You can actually just use the new keyword as well, look here for more info
var floatShareBar = new (function() {
var fShareBar = $('#article-share');
this.float = function() {
console.log('float');
};
this.unfloat = function() {
console.log("unfloat");
};
})();
Change you function to this:
$("body").on("ab.snap", function(event) {
if (event.snapPoint >= 768) {
(new floatShareBar()).float()
} else {
(new floatShareBar()).unfloat();
}
});
function floatShareBar () {
var fShareBar = $('#article-share');
this.float = function() {
console.log(
};
this.unfloat = function() {
console.log("unfloat");
};
};
you should declare functions when using var before you call them.
How can I extend prototype A with prototype B, so whenever I call prototype A, both will be executed?
var Helper = function() {
}
Helper.prototype.resizer = function() {
$('body').append(' Window resized ');
}
var Something = function() {
// Extend Helper.resizer with Something.anything
// extend(Helper.resizer, this.anything);
}
Something.prototype.anything = function() {
$('body').append(' Run this on resize to ');
}
var help = new Helper();
var some = new Something();
$(window).on("resize", function(){
help.resizer();
});
Made an example at codepen:
http://codepen.io/robbue/pen/892c8f61e1b5a970d6f694a59db401a6
jQuery allowed, or just vanilla.
I don't really understand your question because prototypes are not executed, but I think you want something like this:
var Helper = function() {}
Helper.prototype.resizer = function() {
$('body').append(' Window resized ');
}
var Something = function(h) {
var oldresizer = h.resizer,
that = this;
h.resizer = function() {
var res = oldresizer.apply(this, arguments);
that.anything();
return res;
};
}
Something.prototype.anything = function() {
$('body').append(' Run this on resize to ');
}
var help = new Helper();
new Something(help);
$(window).on("resize", function(){
help.resizer();
});
or that:
function Helper() {}
Helper.prototype.resizer = function() {
$('body').append(' Window resized ');
}
function Something() { // inherits Helper
Helper.apply(this, arguments);
}
Something.prototype = Object.create(Helper.prototype);
Something.prototype.anything = function() {
$('body').append(' Run this on resize to ');
};
Something.prototype.resizer = function() {
Helper.prototype.resizer.call(this);
this.anything();
};
var help = new Something(help);
$(window).on("resize", function(){
help.resizer();
});