JS: Scope Inside Constructor Function - javascript

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.

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)

The meaning of `this` in JS template method pattern

Why does the marked line fail to find protectedACMember?
var Module = (function (ns) {
function AbstractClass() {
this.protectedACMember = "abstract";
this.abstractPublicACMethod = function (input) {
this.methodToImplement();
}
}
ConcreteClass.prototype = new AbstractClass();
function ConcreteClass(){
var privateCCMember = "private CC";
var privateCCMethod = function(){
alert(this.protectedACMember); // cant find protectedACMember
}
this.methodToImplement = function(){
privateCCMethod();
console.log('Implemented method ');
}
}
ns.ConcreteClass = ConcreteClass;
return ns;
})(Module || {});
//somewhere later
var cc = new Module.ConcreteClass();
cc.abstractPublicACMethod();
are there any good patterns for simulating private, protected and public members? Static/non-static as well?
You should change that part of code like this:
var self = this;
var privateCCMethod = function(){
alert(self.protectedACMember); // this -> self
}
This way you get the reference in the closure.
The reason is, that "this" is a reserved word, and its value is set by the interpreter. Your privateCCMethod is an anonymous function, not the object property, so if you call it simply by privateCCMethod() syntax, this will be null.
If you'd like "this" to be bound to something specific you can always use .call syntax, like this:
privateCCMethod.call(this)
Another way to ensure that this means what you want is to use bind. Bind allows you to ensure a function is called with a specific value of this.
Most newer browsers support it (even IE9!) and there's a workaround for those that don't.
Bind - MDN Documentation
It fails to find protectedACMember because what the this keyword means changes when you enter the function privateCCMethod. A common practice is to store the outer this for use inside the functions:
function ConcreteClass(){
var privateCCMember = "private CC";
// store the outer this
var that = this;
var privateCCMethod = function(){
alert(that.protectedACMember);
}
...
The rest of your questions are fairly loaded and should probably be posted as a separate question.

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...

How to call an instantiated class' function in 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.

Accessing this from within an object's inline function

I'm having difficulty referencing "this" from within a javascript inline function, within an object method.
var testObject = {
oThis : this,
testVariable : "somestring",
init : function(){
console.log(this.testVariable); // outputs testVariable as expected
this.testObject.submit(function(){
var anotherThis = this;
console.log(this.testVariable) // undefined
console.log(oThis.testVariable) // undefined
console.log(testObject.testVariable) // outputs testVariable
console.log(anotherThis.testVariable) // undefined
}
}
How do I access this.testVariable from within the submit function?
I'm also using jQuery as well, if that makes a difference.
I wonder if this is the best approach - and maybe I should have submit as a separate function, and then reference that inline, like:
init : function(){
this.testObject.submit = this.submitForm;
},
submitForm : function(){
// do validation here
console.log(this.testVariable) // outputs testvariable
.
.
.
return valid;
}
But this didn't seem to work either - and I think I'd just like to keep the submit function inline within my init method for now.
A common way is to assign the this you want to a local variable.
init: function() {
var _this = this;
this.testObject.submit(function() {
console.log(_this.testVariable); // outputs testVariable
});
}
You could also do this using ES6 arrow functions:
init: function(){
this.testObject.submit( () => {
console.log(this.testVariable);
}
}
Arrow functions capture the this value of the enclosing context, avoiding the need to assign this to a new variable, or to use bound functions.
The "this" variable is bound dynamically when a function — any function, regardless of where it was defined — is called.
Without seeing what that "submit" function is supposed to do, or where it's supposed to be used, it's hard to say how to change it. One thing you could do is to define "submit" in your "init" function:
init: function() {
// whatever
var instance = this;
instance.submitForm = function() {
console.log(instance.testVariable);
// ...
};
}
As long as "init" is called initially with "this" set to an instance of one of your objects, you should be good.
You can only access the oThis variable from the context of the object, which is lost because you are inside of another function. or through instantiating a new object. like this
var testInstance = new testObject();
Then you could access oThis by using:
testInstance.oThis;
but that would be redundant
I would try something like this Matt:
init: function(){
var self = this; // this allows you to access the parent object from different contexts
this.testObject.submit(function(){
console.log(self.testVariable);
}
A possible answer is to use arrow functions and pass in the object that "this" should refer to...
function create() {
var thing = { name: "thingy" };
thing.doStuff = function() {
alert(this.name);
}
thing.doStuff(thing);
}
The reason this works is arrow functions automatically have a final thisArg optional parameter which is bound to this.

Categories

Resources