Javascript problems with bind - javascript

I'm new to JS, and have a problem figuring out why bind makes me problems:
Here is a snippet:
var MyTest = function() {
registerChannel(address, null, this._messageHandler.bind(this));
};
MyTest.prototype._messageHandler = function(msg) {
this._logToConsole(msg);
};
MyTest.prototype._close = function() {
(function(inst) {
asyncSend(address, data,
function(err, reply) {
unregisterChannel(address, inst._messageHandler.bind(inst));
}
);
})(this);
}
The registerChannel method stores the callback function I passed and the unregisterChannel tries to verify whether this callback exists and fails because 'bind' copies the method and doesn't pass them as reference (to the best of my knowledge).
The result is that unregisterChannel fails because it can't find the function I passed.
How can I solve this? apply\call ? not familiar with those...
Thank you and I hope my question did sense...

Each time you call .bind you get a new reference, so the "register" and "unregister" phases won't match because you're not passing exactly the same reference.
Therefore each object will need to retain its own copy of its bound handler, e.g.:
var MyTest = function() {
this._handler = this._messageHandler.bind(this);
registerChannel(address, null, this._handler);
};
and you can then pass this._handler to your unregisterChannel call.
An alternative would be to not put _messageHandler on the prototype at all, and create a new instance within the constructor:
var MyTest = function() {
this._messageHandler = (function(msg) {
...
}).bind(this);
registerChannel(address, null, this._messageHandler);
};

Related

How to escape "this" problem in a javascript class?

get_O3(e)
{
e.preventDefault();
let station = document.getElementById(e.target.id);
let lon = station.getAttribute('lon');
let lat = station.getAttribute('lat');
let code_station = station.getAttribute('code');
this.get_previsions(lon, lat, "O3").bind(this).then((data) =>
{
console.log(data);
});
}
I have a "this" problem, when i call the function get_previsions i get the error :
Uncaught TypeError: this.get_previsions is not a function.
It might be because of the (e) parameter because when i do a console.log(this) it returns the target. I would like that this == my class.
Thanks for the help
At any given point you can check what the current this reference is pointing to by doing the 4 following rules:
New: Was the function called using new then the this points to the new instance.
Explicit Binding: Was the function called using Function#call, Function#apply or Function#bind
Implicit Binding: Was the function called by its owner? (i.e. owner.foo() or owner["bar"]())
Default Rule: If none of the other rules happen then this is set to the window object if the script is running in "use strict" mode otherwise undefined.
Event-listeners call a function using Explicit binding (callBack.call(target, ...)) so the this reference gets set to the target. To change the this reference you either need to wrap it and call it implicitly or use Function#bind.
Implicit call Example (+ closure):
var something = {
foo: function() {
var self = this;
addEventListener("click", function(e) {
self.bar(e);
});
},
bar: function() {
}
};
Explicit call Example (Function#bind):
var something = {
foo: function() {
addEventListener("click", this.bar.bind(this));
},
bar: function() {
}
};
I'm assuming you have a class defined similar to
class thing {
get_O3(e) { ... },
get_previsions() { ... }
}
There are a few options for you. First option, you can bind all functions to this in the constructor:
class thing {
constructor () {
this.get_03 = this.get03.bind(this);
this.get_previsions = this.get_previsions.bind(this);
}
get_O3(e) { ... },
get_previsions() { ... }
}
This can get awkward, especially if you have many functions. You can write a helper bindAll function, but a less awkward/verbose solution is to use a factory method instead, bypassing this altogether:
function makeThing {
const thing = {
get_O3(e) {
...
thing.get_previsions();
},
get_previsions() { ... }
};
return thing;
}
Eric Elliot on Medium has some good reading on the topic if you want to get more in depth.

Locate variable of my prototype

I have an object in Javascript:
function MyObject(aField) {
this.field = aField;
}
MyObject.prototype.aFunction = function() {
return this.field;
}
Then
var anInstance = new MyObject("blah");
var s = anInstance.aFunction();
this works fine, but if I pass the function to another function:
callLater(anInstance.aFunction);
I don't control callLater and it's minified, but it seems that it's calling aFunction using call() or apply(). Therefore, this points to another object and field is undefined.
What's the best practice to avoid this situation I'm facing?
That's because you lost the value of this Try this instead:
callLater(function() { anInstance.aFunction() });
Explaination, Think of it this way
function MyObject(aField) {
this.field = aField;
}
function MyObject2(aField) {
this.field = aField;
}
MyObject.prototype.aFunction = someFunct;
MyObject2.prototype.aFunction = someFunct;
Now what does someFunct belong to?
Well try doing MyObject.prototype.aFunction === MyObject2.prototype.aFunction it'll be true!
You see the problem Therefore it needs to be called from the class and not just referenced by value.

javascript is it possible to use a string to call a object function

I have a generic function which can speak to multiple other functions in appropriate objects is it possible to use a string to call the appropriate function.
var string = "save";
var generic = (new function (string) {
string."alert()";
return this;
})
var save = (new function (string) {
this.alert = (function () {
alert("your document has been saved")
return this
})
return this
})
var notSaved = (new function (string) {
this.alert = (function () {
alert("your document has not been saved")
return this
})
return this
})
I am using it for a far more complex set up but here is an example. Is this possible?
Sure you can. Try something like this:
window[string].alert();
Looking at your code it's hard to tell what you're actually trying to achieve. Nonetheless, here are a few ideas that may be relevant.
First, let's make a couple of objects:
var rabbit = {
name: 'Peter',
hop: function () {
return this.name + ' hopped!'
},
jump: function () {
return this.name + ' jumped!'
}
}
var hairy_maclary = {
name: 'Hairy Maclary',
jump: function () {
return this.name + ' jumped over the fence!'
}
}
Now, you could define a function which invokes the hop method on whichever object is passed to it:
function hop(object) {
return object.hop()
}
hop(rabbit) // 'Peter hopped!'
I'm not sure why you'd do this rather than invoking hop directly, but perhaps you want to do extra stuff before or afterwards.
If you wanted to you could create a completely generic function which would invoke a given method on a given object:
function invokeMethod(object, method) {
object[method]()
}
invokeMethod(hairy_maclary, 'jump') // 'Hairy Maclary jumped over the fence!'
This is a really strange thing to want to do, though. Perhaps you could provide more of an idea of what you're actually trying to do, since your example code is rather odd.
You can enclose your functions within some object so you can access by passing name of the property using some variable (in this case named string), eg. like that:
var string = 'notSaved';
var funcs = {};
funcs.save = new function(){
this.alert = function(){
alert('called save.alert()');
};
return this;
};
funcs.notSaved = new function(){
this.alert = function(){
alert('called notSaved.alert()');
};
return this;
};
funcs[string].alert();
See working example on jsfiddle.
If your variables are global (they should not), they are also automatically enclosed within window object, so you can call them also like that: window[string].alert(). This will not work for non-global functions (in this case my solution seems to be the only one not using eval()).
eval("alert('test');");
You can call functions with eval. Even you can declare functions.
eval("function test(){ alert("test");}");
test();

Returning the method of a function

In the code below, I've got two objects declared, with one object inheriting the properties and functions of another.
I want to use the super variable to call the methods of the object I inherited from. When I trace out itemEditor, I can see the function and it's methods correctly. When I try to access the method of itemEditor, it returns undefined.
What am I doing wrong? Is there a better way to do this?
var myObject = {
itemEditor : function (vars) {
this.editItem = function () {
alert("Editing Item");
}
},
recurringItemEditor : function (vars) {
myObject .itemEditor.apply(this, [vars]);
this.prototype = myObject.itemEditor.prototype;
var super = myObject.itemEditor
this.editItem = function () {
console.log("fn.recurringItemEditor.editItem");
console.log(super);
console.log(super.editItem);
super.editItem.call(this);
}
}
Your code seems a little confused. On the one hand myObject.itemEditor is a constructor and therefore a function (myObject.itemEditor.apply(this, [vars])), and on the other you treat it like an object with a prototype (this.prototype = myObject.itemEditor.prototype;).
That's not even considering that super is a reserved keyword.
Your example may be simplifying something you are trying to do, but I don't see why you don't just use the usual prototype inheritance. That way you can still have a method in your local instance and call the prototype one within it if you want e.g.
recurringItemEditor : function (vars) {
this.prototype = new myObject.itemEditor(vars);
this.editItem = function () {
console.log("fn.recurringItemEditor.editItem");
console.log(this.prototype);
console.log(this.prototype.editItem);
this.prototype.editItem.call(this);
}
}
I used your advice and it works well now. In regards to treating it like a function and an object, myObject .itemEditor.apply(this, [vars]); was still required in order for the object to inherit the properties of itemEditor. I should have made that clear in the original code. If there's a better way to do this, let me know.
var myObject = {
itemEditor : function (vars) {
var myVars = vars + "foo";
this.editItem = function () {
alert(myVars);
}
},
recurringItemEditor : function (vars) {
myObject .itemEditor.apply(this, [vars]);
this.prototype = new myObject.itemEditor(vars);
this.editItem = function () {
console.log("fn.recurringItemEditor.editItem");
console.log(this.prototype);
console.log(this.prototype.editItem);
this.prototype.editItem.call(this);
}
}
}

Assigning scope amongst jQuery.getJSON and a JS.Class

I'm trying to assign some JSON data to a property of a JS.Class instance.
var MyClass = new JS.Class({
initialize: function(uuid) {
this.uuid = uuid;
},
write: function() {
$.getJSON(url+"?callback=?", {}, function(data) {
Assign(data);
});
function Assign(data) { this.content = data; };
}
});
var m = new MyClass("uuid_goes_here");
m.write();
The JSON is received asynchronously, which is why there's a function call within the $.getJSON callback.
The problem I have now is that the this.content within the Assign function is not within the scope of the instance method named write. So whereas this.uuid returns correctly, this.content remains undefined (as you would expect).
Any ideas on how to correct this? I've tried using a global variable as a workaround but the async call doesn't allow for that (plus it's a crappy solution).
Some points to note, in case they matter: I have to use JSONP, so the "?callback=?" has to stay, and I'd like to keep it async.
I would usually go for either czarchaic's version, or replace Accept with a bound method from the object. What you have to bear in mind is that calling Accept() like that (as a function call rather than a method call) will bind this to the global object, i.e. window. I'd try this:
var MyClass = new JS.Class({
initialize: function(uuid) {
this.uuid = uuid;
},
write: function() {
$.getJSON(url+"?callback=?", {}, this.method('setContent'));
},
setContent: function(data) {
this.content = data;
}
});
See http://jsclass.jcoglan.com/binding.html for more info.
You should cache the current instance in the write method and update it after ajax.
write: function() {
var self=this;
$.getJSON(url+"?callback=?", {}, function(data) {
self.data=data;
});
}

Categories

Resources