How to call an instantiated class' function in javascript? - javascript

To tell the true, i can call the function but just in hard-coded way. Instead of hard-coding the submit binding my getData function, i'd like to call the function by arguments. Please help me, how to do this.
Thanks.
formhandler = new xForm.Main(); // new formhandler
formhandler.setForm(this.formid); // fn.setForm(string) // configure the container, which has the form elements, or the form itself
modal = new xModal.Main(); // new modal handler
modal.setModal(this.modalid); // set modal element
modal.showModal(); // show
modal.bindClose(".cancel"); // bind closing classes
modal.bindSubmit(".submit", formhandler, "getData"); // bind submit to call formhandler.getData()
in the xModal.js
var xModal = xModal || {};
xModal.Main = function()
{
var self = this;
...
this.bindSubmit = function(classname, a, b)
{
this.closeclass = classname;
$("#"+this.target).find(classname).click(function(){
a.call(b); // edited after the original post, i forgot to replace the call's argument to b in the question excuse me for the inaccuracy
});
}
this function should call the getData in the xForm (here is the snippet from xForm)
var xForm = xForm || {};
xForm.Main = function()
{
var self = this;
this.target = "";
this.data = {};
...
this.getData = function()
{
getSingleElements();
getMultiElements();
return returnData();
}
Update:
I think i just found a method to do this, but please tell me if i made something uncorrectly, or you have a better solution for this problem (i'm pretty sure someone has)
I think, i have the correct method.
in the xForm i made a fn, which calls functions by parameters contains in the self (which is equals to this, actually)
var xForm = xForm || {};
xForm.Main = function()
{
var self = this;
this.callFn = function(func)
{
return self[func].call(this);
}
...
then i call the fn from the another class (xModal)
var xModal = xModal || {};
xModal.Main = function()
{
var self = this;
this.bindSubmit = function(classname, a, b)
{
this.closeclass = classname;
$("#"+this.target).find(classname).click(function(){
a.callFn(b);
});
}
then i just have to tell the xModal this:
modal.bindSubmit(".submit", formhandler, "getData"); // bind submit to call formhandler.getData()
so now the modal class will call the args[1]'s args[2] function. also able to give more parameters to the call fn by apply method.
works fine at me, but i don't know, maybe you can help me in make this better.

You bind a method name of a certain object to the submit event:
modal.bindSubmit(".submit", formhandler, "getData");
But you want to pass arguments to the method as well. This is not the Javascript way of doing it. Instead, just bind an anonymous function to the event, and call the method however you like from within this anonymous function:
modal.bindSubmit(".submit", function(){
formhandler.getData("My arguments");
});
What you see in my example is an anonymous function passed as an argument. In Javascript, there is no distinction between a value like a string or an integer, and a function. A function can be assigned to a variable, and passed as an argument.
To make it more clear, you can also write it like this:
var eventHandler = function(){
formhandler.getData("My arguments");
};
modal.bindSubmit(".submit", eventHandler);
This is called "first class functions", and are part of the "functional programming" paradigm.
Inside the event handler function, you still have access to the variables in the scooe it was created in, like the formhandler object. This is called a "closure".
Read up on this. It will boggle your mind at first, but it is really worth your time, as it will open your eyes to much simpler solutions.
From your example, I am not sure what the object modal is. If it is a jQuery element, my example should work right away, othewise, you would need to update your code to call the function passed in as the event handler, instead of calling a method on an object.

Related

How to assign a function with parameters without executing it in javascript?

My objective is to change the border style of button elements when they are clicked with javascript. I made a function setBorder and assigned to the onclick event on all button elements as:
function setBorder(myobj) {
myobj.style.borderStyle = "inset";
}
var menubtn = document.getElementById("menu_btn");
menubtn.onactive = setBorder(menubtn);
The problem here is that the border style is changed as soon as the page loads because when javascript is parsed the function setBorder() is executed due to the brackets (). Another alternative I thought was:
function setBorder() {
this.style.borderStyle = "inset";
}
var menubtn = document.getElementById("menu_btn");
menubtn.onactive = setBorder;
I thought this would take the object menubtn -- but this didn't happen. Why didn't this take the object menubtn?
I think there could be a way in which setBorder could be linked as the prototype object of all button elements. The prototype object would have a function func_SetBorder as it's property. Then we could call the func_setBorder as:
menubtn.onclick = menubtn.func_setborder;
This solution achieves what you're looking for via the use of a function closure.
var buttons = [ ... ]; // An array containing the buttons
for (let b of buttons) {
b.onactive = onActive(b);
}
function onActive(button) {
return function () {
button.style.borderStyle = 'inset';
}
}
Well, in JavaScript, when you use this keyword inside function in this it will points to an object on which you call your function. So, you have your function like this
function myFunc() {
this.do_smth;
}
and then you call it: my_obj.myFunc(), then, inside your myFunc thiswill points to my_obj.
Assume that you want to call your function with another object:
obj_foo.myFunc()
In this case this inside your function will points to obj_foo
If you want to call your function with different objects (but you must be sure that your objects have yhe same properties) its better to use call/apply or bind.
bind will say to your function "this is the scope which you should work with". But you should always bind your function to different objects in case of using this. More pretty and safely way is to use call/apply. You also should call your function with call/apply each time like with bind, but it looks more better.
So, your code should be like this: setBorder.call(menubtn)
What you want is binding or binding arguments. Javascript provides a native way to bind a function. If you want to bind arguments only, you can use this method as quoted from here:
Function.prototype.arg = function() {
if (typeof this !== "function")
throw new TypeError("Function.prototype.arg needs to be called on a function");
var slice = Array.prototype.slice,
args = slice.call(arguments),
fn = this,
partial = function() {
return fn.apply(this, args.concat(slice.call(arguments)));
// ^^^^
};
partial.prototype = Object.create(this.prototype);
return partial;
};
You would use it like this:
function setBorder(myobj) {
myobj.style.borderStyle = "inset";
}
var menubtn = document.getElementById("menu_btn");
menubtn.onactive = setBorder.arg(menubtn);
Or:
function setBorder() {
this.style.borderStyle = "inset";
}
var menubtn = document.getElementById("menu_btn");
menubtn.onactive = setBorder.bind(menubtn);
By CSS
#menuBtn:active{
border-style:inset;
}
By Javascript
You should try the eventlistener property as
menubtn.addEventListener("mousedown", setBorder);
menubtn.addEventListener("mouseup", removeBorder);
and inside the setBorder and removeBorder function you can use this
Assign it to a variable.
var functionName = function () {
//things
}
If you want this to be menubtn in the setBorder function, you need to bind it :
menubtn.onclick = setBorder.bind(menubtn)

JavaScript event methods inherited from prototype objects

I feel like what I have is correct code, but obviously I am missing something here.
What I am trying to do is create an event method in the prototype object of my constructor. Here is what I have so far:
function Controls(but) {
this.but = document.getElementById(but);
this.but.onclick = function() {
displayMessageTwo();
}
}
Controls.prototype.displayMessageTwo = function() {
alert("HELLO");
}
var Main = new Controls('testingTwo');
My logic here is that I am creating a constructor from which to build controls for something (let's say a slideshow).. this.but equals the html element of a link called whatever is passed as an argument to the constructor.
In my prototype object, I define my method and then create my object. However, this is not working as I had expected.
What am I doing wrong here?
I suspect that when the event handler fires, the context of the invocation is not the instance on which you registered the callback.
Try something like the following
function Controls(but) {
var that = this;
this.but = document.getElementById(but);
this.but.onclick = function() {
that.displayMessageTwo(); // that is closed-in
}
}
What am I doing wrong here?
You are calling displayMessageTwo(); as if it was a global function. It is not, it is a inherited property on your instance. Usually you would refer to the instance with the this keyword, but inside the event handler you can't. Create a variable referencing the object, and call that one's method like so:
function Controls(but) {
this.but = document.getElementById(but);
var that = this;
this.but.onclick = function() {
that.displayMessageTwo();
}
}
As your displayMessageTwo method does not care about its context (does not reference other properties via this), you even might assign it directly:
this.but.onclick = this.displayMessageTwo;
But I'd recommend to avoid that, methods should always be executed with correct thisValue. You also might use bind:
this.but.onclick = this.displayMessageTwo.bind(this);
but it needs additional code for older, non-supporting browsers.
I would think:
function Controls(but) {
this.but = document.getElementById(but);
this.but.onclick = this.displayMessageTwo;
}
Controls.prototype.displayMessageTwo = function() {
alert("HELLO");
}
var Main = new Controls('testingTwo');
is clearer (assigning the function assigned to prototype.displayMessageTwo). If this doesn't work it may be because
this.but.onclick = this.displayMessageTwo;
is evaluated before:
Controls.prototype.displayMessageTwo = function() {
making it null...

Binding "this" when passing an object's member function

I had a "class" defined and was making only one instance of it. The instance possessed a member function that would end up being passed around (it's a mouse handler, but that's not important). Since I would only ever make one instance of my "class", I decided to rewrite it as a singleton by using an object literal.
So I have
var mySingleton = {
theObjects : [];
}
mySingleton.mouseHandler = (function() {
var that = this;
return function (e) {
for (var indx = 0; indx < that.theObjects.length; indx++) {
// do something to that.theObjects[indx];
}
}
}());
mySingleton.addObject = function(newObj) {
this.theObjects.push(newObj);
}
However, when I try to use this code (after adding a few objects), I keep getting an that.theObjects is undefined error. It's referring to the line in the for loop.
Update for 2015 – Use Function.bind() to specify the value of this within the function. Then, instead of using that, you can use this.
mySingleton.mouseHandler = function (e) {
for (var indx = 0; indx < this.theObjects.length; indx++) {
// do something to this.theObjects[indx];
}
}.bind(mySingleton);
This doesn't work if you want mouseHandler to have the context of the 'moused' element. For that, use my original answer below.
If you need to support IE8 or (heaven forbid) earlier, you'll need to use a polyfill.
Since you are calling the function that creates mouseHandler immediately, it is run in the context of window, not mySingleton. So that refers to window. Instead of calling it immediately, just change it to a method so that it runs in the context of mySingleton:
mySingleton.getMouseHandler = function() {
var that = this;
return function() { ... };
};
myElement.onclick = mySingleton.getMouseHandler();
Of course, since you are already using a singleton, you can just reference it directly. In your click handler, instead of checking that.theObjects, check mySingleton.theObjects. Or, in mouseHandler change var that = this to var that = mySingleton.
Edit: Or, pass the context to your anonymous function when you call it:
mySingleton.mouseHandler = (function() {
var that = this;
return function (e) {
for (var indx = 0; indx < that.theObjects.length; indx++) {
// do something to that.theObjects[indx];
}
}
}).call(mySingleton);
There are a few popular ways to do this. First, super-simple solution is just reference mySingleton directly and bypass the confusion associated with this. Instead of that.theObjects just do mySingleton.theObjects and move on with your life and things will work fine.
However, there is a common pattern to do this binding. Here's how underscore.js does it
Check out the annoted source to underscore, where you will find this
_.bind = function(func, obj) {
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
var args = slice.call(arguments, 2);
return function() {
return func.apply(obj, args.concat(slice.call(arguments)));
};
};
The other answers here so far are also correct. Providing my viewpoint here in case it helps.
The key to understanding why the code doesn't behave as you expect requires understanding how this works in JavaScript. The problem is that this depends on how the function is called.
First, if you call the function in the method style, this is what you'd expect:
mySingleton.mouseHandler(); // this === mySingleton
If you attach the function to something esle, that works too.
var anotherSingleton = {};
anotherSingleton.foo = mySingleton.mouseHandler;
anotherSingleton.foo(); // this === anotherSingleton
If you detach the function, this becomes the global scope object (window)
var foo = mySingleton.mouseHandler;
foo(); // this === window
And finally, you can force this to be something else using call or apply:
var randomThingy = {};
mySingleton.mouseHandler.call(randomThingy); // this === randomThingy
The takeaway is that this is determined at runtime based on the context of how the function was called. Often, frameworks that allow you to make "classes" abstract these details from you by implicitly applying the bind pattern on your behalf. This is why it used to work, and no longer does.
As others have mentioned, you can change your handler to reference the variable by its scoped name (mySingleton) or otherwise bind it as discussed.
Here's an article I wrote on the subject a few years ago that goes into more detail: http://trephine.org/t/index.php?title=Understanding_JavaScript%27s_this_keyword
Hope this helps!

JS: Scope Inside Constructor Function

I am new to OOP and I am trying to rewrite a simple JS function as an object literal and then as a constructor function. I succeeded in writing the object literal version, but I clearly have a scope problem inside the anon function which handles the onclick event (inside my constructor function). Please let me know how to make the onclick event work.
Object Literal Version Which WORKS:
var submit = {
form_id : "",
submit_button_id : "",
submit_form: function(){
var button = document.getElementById(submit.submit_button_id);
var form = document.getElementById(submit.form_id);
button.onclick = function(){
form.submit();
return false;
}
}
}
addLoadEvent(function(){
submit.form_id = "form_vars";
submit.submit_button_id = "but_submit";
submit.submit_form();
});
Constructor Function Version Which DOESN'T WORK:
function SubmitForm(button_id, form_id){
this.submit_button = document.getElementById(button_id);
this.form = document.getElementById(form_id);
this.submit_form = function(){
// problem function below
this.submit_button.onclick = function(){
this.form.submit();
}
}
}
addLoadEvent(function(){
var form_to_submit = new SubmitForm("but_submit", "form_vars");
form_to_submit.submit_form();
});
P.S. I am aware that I should be using DOM API event handlers instead of HTML-DOM ones. I am just tackling one thing at a time.
this inside your function will not necessarily be the same as this in the constructor, it is decided by how you call that function. For instance, if you call a function f by doing f(), then this === window, if it is a method on an object x.f() then this === x. Also see Function:call and Function:apply.
Simplest way to solve this is to have a local variable in the constructor (like var me = this;) and then use me instead of this in the inner function, since that will not be overridden.
Read up on lexical scoping and closures if you want to learn more.

How to get reference to this, when the this is override by calling the method.call(otherObject)?

var A = function(x){
var that = this;
this.a = x;
}
A.prototype = {
init: function(){
alert(this.a); // as that is not found :s
}
};
var a = new A(1);
var b = new A(2)
a.init.call(b);
// I want to alert number 1, how to do it?
I need this because I use jQuery events.
my sick solution... not so good
I got my question answered but this has some problems, I have to define a local that var and create a closure for every event... sick!
var that = this;
this.view.skinAppliedSignal.add(function(){
that.onSkinApplied();
});
// then in the onSkinApplied the this is correct. any way not so hacky to get this done?
You cannot do that, in general. The only this that exists when a function runs is the this established by the invocation.
You can "trap" things in a closure when you establish event handlers:
function something() {
$('.whatever').each(function() {
var whatever = $(this);
whatever.find('button').click(function() {
whatever.hide();
});
});
}
In that example, "whatever" is used to save the element from the "each" loop so that the event handlers hooked up to button elements can get access to it.
edit — Based on a comment and the update to the question, it's clear that perhaps what's desired is something like the ".bind()" function that's part of ES5 (and which is supplied by some libraries, like Prototype, Functional, and jQuery). By using "bind", you basically wrap any function of your choice up in another function, such that your function will always be called with this set to some specific object.
Can't be done with a prototype. You could do it this way, however:
var A = function(x){
var that = this;
this.a = x;
this.init = function(){
alert(that.a);
}
}
var a = new A(1);
var b = new A(2)
a.init.call(b);​

Categories

Resources