How come this and _this reference different instances of an object? - javascript

I have a class that can be simplified like this:
Captcha = function(el) {
this.el = $(el);
_this = this;
captchas.push(this);
};
Captcha.prototype.render = function(grecaptcha){
console.log(this.el.dom[0]);
console.log(_this.el.dom[0])
};
The class is intantiated twice with two different DOM-elements passed in as el.
Render is run when a global callbackfunction is run.
captchas = [];
//We need this for captchas.
window.CaptchaCallback = function(){
app.captchas.forEach(function(capt){
capt.grecaptcha = grecaptcha;
capt.render();
});
};
For some reason, this.el.dom[0] references the two different elements, but _this.el.dom[0] always references the last instance of the class, why?

You left off var when you initialized _this:
var Captcha = function(el) {
this.el = $(el);
var _this = this; // don't forget var!
captchas.push(this);
};
Your code was therefore creating a global variable, not a local one.
Of course, it's local to that constructor function, so it won't be visible outside anyway. You could make _this a property of the constructed object:
this._this = this;
but that doesn't make a lot of sense, since you'd need this to find _this anyway.

Because you didn't declare _this with the var keyword, a global variable is implicitly declared. Your constructor code then equivalent to:
var _this;
Captcha = function(el) {
this.el = $(el);
_this = this;
captchas.push(this);
};
Because it's global, _this always holds the value of the last instance created.

Related

Why does the variable I attach `this` to in the constructor lose its binding?

I'm really beating my head against this one. I cannot figure out how javascript's binding works. In the constructor of my class, I set var self = this;, which I thought would forever give me an absolute reference to my instance. However, that doesn't seem to be the case at all.
Below is the primary structure of my class.
var MyClass = (function() {
function MyClass(data) {
var self = this;
this._element = $('.someSelector');
}
MyClass.prototype.styleIt = function() {
self._element.css('display', 'block');
}
return MyClass;
})();
In another class, which holds instances of MyClass, I call the styleIt function as follows:
Class2:
...
$('#id').click(function(evt) {
evt.preventDefault();
for (int i = 0; i < self._objects.length; i++) {
[i].styleIt();
}
}
However, confusingly (to me) the self inside of styleIt is bound to window! What is happening here?
self is defined inside of the inner MyClass function and thus is only visible within that scope. Your styleIt method is not defined within that scope and thus cannot see the self variable.
If you want your methods to be self-referential (e.g. not rely on this), then you have to define the methods within the constructor (not on the prototype) so they can see the self variable.
Here's one way to do that:
var MyClass = (function() {
function MyClass(data) {
var self = this;
this._element = $('.someSelector');
this.styleIt = function() {
self._element.css('display', 'block');
return self;
}
}
})();
In your code that calls styleIt() like this:
$('#id').click(function(evt) {
evt.preventDefault();
for (int i = 0; i < self._objects.length; i++) {
[i].styleIt();
}
}
this code is simply not correct (two coding errors) and should not need the self referential feature above. This code can be done like this:
$('#id').click(function(evt) {
evt.preventDefault();
for (var i = 0; i < self._objects.length; i++) {
self._objects[i].styleIt();
}
}
When calling a method using obj.method() this will be set to obj inside of the method so you wouldn't need the self feature in the styleIt() method. You could just rely on the value of this.

confusion, this-pointer and events in javascript

I have a javascript object which I would like to be able to handle some interactive features. It's a bit tricky to describe the scenario in a simple way so hopefully it'll not get all out of hand here.
my object looks something like
myobject = function() {
this.initialize = function() {
// HERE this = the myobject instance
var test = document.createElement('div');
test.onmousedown = this.mousedown;
}
this.mousedown = function(e) {
// HERE this = the calling div-element
}
}
So my problem is basically that this will not be an myobject instance when this.mousedown(e) is called, it will rather be the caller (if the terminology is correct?) in this case it is the div I created and put in a variable called test above.
I would like to have access to the instance on which the method is being run (as I believe that to be the mousedown method I created).
This far I have had some ideas which I have tried out:
create a data- attribute on the div containing the this object and operate on that.
sending the this pointer as an argument along with e to this.mousedown(e)
It's all I can think of now hope it makes sence.
You could create a copy when you first instantiate the object:
var myobject = function() {
var self = this;
this.initialize() {
// HERE this = the myobject instance
var test = document.createElement('div');
test.onmousedown = this.mousedown;
}
this.mousedown(e) {
// HERE this = the calling div-element
// use self instead of this
}
}
The simplest solution is to make a 'self' var that you refer to in the callback:
myobject = funciton() {
var self = this;
this.initialize() {
//use self to refer to myobject
self.mousedown(e);
}
this.mousedown(e) {
}
}

ko.computed on an ko.observableArray

I'm trying to use a computed to calculate the total of some product.
function productViewModel(){
self = this;
function productModel(data)
{
var self=this;
self.id=ko.observable(data.id);
self.codigo=ko.observable(data.codigo);
self.recurso=ko.observable(data.recurso);
self.unidad=ko.observable(data.unidad);
self.precio_unitario=ko.observable(0);
self.cantidad=ko.observable(0);
self.total=ko.computed(function()
{
return self.precio_unitario()*self.cantidad();
},productModel);
}
self.products = ko.observableArray([]);
self.addProduct = function(product)
{
self.products.push(new productModel(product));
};
self.removeProduct = function()
{
self.products.remove(this);
};
}
orden = new productViewModel()
ko.applyBindings(orden);
But when precio_unitario and cantidad are changed. total doesn't update.
function productModel(data)
{
var self=this;
...
self.total=ko.computed(function()
{
return self.precio_unitario()*self.cantidad();
},this);
}
You should be binding the ko.computed to this not to the function. You want it to be bound to the object thats created, not to the constructor, which won't have those properties on it. Since you're using self, this will actually be taken care of by default, and if you like you can omit the second argument entirely.
Within the constructor function, this or self will refer to the object that is created when you use the new operator. So all the properties will be created on that object.
self = this; should be var self = this;; otherwise you're overwriting the global self. Also take out ,productModel on the computed; it's not necessary.
Important parts:
function productViewModel() {
var self = this;
function productModel(data) {
var self = this;
...
self.total = ko.computed(function() {
return self.precio_unitario()*self.cantidad();
});
}
...
}
Also it's important make sure you're always using the correct format for writing to observables. It should be self.catidad(newValue); and not self.catidad = newValue;

"this" in JavaScript. reference to an object inside a factory

I wrote some classes in javascript and i wrote a few FunctionFactories for them. But I think that i have done some things wrong.
I renamed some things of my code, that you can understand it better.
So the first class is the "root"-class. this class has children, which i add later.
function templateRoot(){
this.id = "root";
this.parent = null;
this.children = [];
this.editable = true; // bla
this.render = function(){
$.each(this.children,function(i,obj){
this.children[i].render();
var baseButtons = this.getBaseButtons();
$('#'+this.id).append(baseButtons);
});
};
this.addBase = addBaseFactory(this);
};
The attribute "addBase" gets a function which is delivered by addBaseFactory...
function addBaseFactory(that){
return function(){
var newBase = new base(that.children.length, that.id);
that.children.push(newBase);
};
}
...and the base class which is used to generate a object in "addBase" looks like this:
function base(count, parent){
this.id = parent+"_base"+count;
this.parent = parent;
this.children = [];
this.remove = function (){
$('#'+this.id).remove();
};
this.render = baseRenderFactory(this);
this.addChild = addChildFactory(this);
this.getBaseButtons = function(){
var addAttributeButton = new $("<button>+ Attribute</button>").button();
var addTextButton = new $("<button>+ Text</button>").button();
return [addAttributeButton, addTextButton];
};
}
The problem now is. When i debug the code and set a breakpoint within the "render" function of the root-object. Then i can see, that "this" is not the root but the "base" object. And i cannot figure out why it is like that because the "root" object is the owner of this function, and my base has an own render function which is not called directly there.
So even the "this" in the line
$.each(this.children,function(i,obj){
Refers to the "base" object. But the "this" is inside the "root" object...
Hope you can help me :-)
EDIT:
The code to let it run:
var test = new templateRoot();
test.addBase();
test.render();
EDIT 2:
"that" in "addBaseFactory" refers to the correct "base" object.
I found your explanation pretty confusing, so I may have misinterpreted what you're trying to do, but I think you expect this within your nested functions to the same object as the this in the outer templateRoot() function. That's not how this works in JavaScript. Nested functions don't inherit the same this as the containing function - each function has its own this object that is set depending on how the function is called.
Here's one possible solution, which uses the fact that nested functions can see variables from their containing function(s):
function templateRoot(){
var self = this; // save a reference to this for use in nested functions
this.id = "root";
this.parent = null;
this.children = [];
this.editable = true; // bla
this.render = function(){
$.each(self.children,function(i,obj){
self.children[i].render();
var baseButtons = this.getBaseButtons();
$('#'+self.id).append(baseButtons);
});
};
this.addBase = addBaseFactory(this);
};
A detailed explanation about how this works in JS can be found at MDN.
Wouldn't this render its childerens children, since jquery would send each child as this?
this.render = function(){
$.each(this.children,function(i,obj){
this.children[i].render();
var baseButtons = this.getBaseButtons();
$('#'+this.id).append(baseButtons);
});
};
Btw in what scope is addBaseFactory called? Because I think the "this" in the base, will refer to that scope.

Carrying scope of "this" into inner functions

How can I extend the scope of this inward? I thought reassigning a higher scoped variable would do the trick... what am I doing wrong? http://jsfiddle.net/8bqXM/
function Player(configs) {
this.opts = {
playerID: "cSurf"
};
configs = $.extend(this.opts, configs);
var the = this;
this.init = function(){
var $player = $("#" + the.configs.playerID);
alert($player.attr("id"));
}
}
var cSurf = new Player();
$(document).ready(function(){
cSurf.init();
});
In your code, configs is not a public member of the Player object being created. You declared it without var so it's a global variable.
configs = $.extend(this.opts, configs);
should be:
this.configs = $.extend(this.opts, configs);
Then in your init() you can do the.configs.playerID.
Example: http://jsfiddle.net/rCuXa/
(Your jsFiddle also had MooTools loaded instead of jQuery. Check the menu on the left.)
EDIT:
By the way, since you're calling init() from the context of the new Player object, you really don't need to use the as the reference to this.
Inside init method, this refers to the Player that was created when you call it that way.
var $player = $("#" + this.configs.playerID);
Example: http://jsfiddle.net/rCuXa/1/
EDIT2: Additionally, if init() is always going to be called from the context of the Player instance you create, you may want to consider placing it on the prototype object of Player.
That way it will be shared among all instances instead of being recreated for each one:
Player.prototype.init = function(){
// ---------v----------"this" is the instance of Player
var $player = $("#" + this.configs.playerID);
alert($player.attr("id"));
}
Example: http://jsfiddle.net/rCuXa/2/
You just have a minor bug here. You're assigning the extend to configs, which is not on this, but then you reference it later via 'the' as if it is. This works:
function Player(configs) {
this.opts = {
playerID: "cSurf"
};
this.configs = $.extend(this.opts, configs);
var the = this;
this.init = function(){
var $player = $("#" + the.configs.playerID);
alert($player.attr("id"));
}
}
var cSurf = new Player();
cSurf.init();

Categories

Resources