Javascript Object Function Scoping - javascript

Let's say I have a class:
var asdf = new Class({
myFunction: function () {
//some stuff here
},
anotherFunction: function() {
globalObject.dosomethingandusecallback(
function() { // this is the callback
//how do I call myFunction() here? I can't seem to get it to work?
}
);
}
});
I seem to have some scoping problems in trying to call myFunction within the definition of my callback function. What am I missing here? I thought it should have access to myFunction in this context?
Thanks!

Copy the this keyword into a variable outside of the callback function, and use that variable inside the callback:
anotherFunction: function() {
var self = this;
globalObject.dosomethingandusecallback(
function() { // this is the callback
self.myFunction();
}
);
}

Related

Calling a JavaScript function from a nested objects function fails

On the simple example below and on JSFiddle here - https://jsfiddle.net/jasondavis/dnLzytju/ you can see the issue I have.
I can see why it could happen but I am not sure how to fix it while keeping the same JS structure.
The issue is when I define a JavaScript objects prototype functions and I have a 2nd level nested object which has a function and in that function I call a function on the parent/root level it fails.
This function from the code below this.nestedObject.nested_object_function() tries to call the function this.normal_function() however it fails and says:
Uncaught TypeError: this.normal_function is not a function
at Object.nested_object_function (VM2493:79)
I assume the reason is that this is referencing this.nestedObject instead of the parent object.
If that is the case, then how can I call that function like I am trying to do from the nested object function and call a parent function?
I have also tried calling JsLibTest.normal_function() as a test from the this.nestedObject.nested_object_function() function but I get the same error.
var JsLibTest = (function (document) {
"use strict";
var JsLibTest = function (){
// run init() function on initiation of a new JsLibTest object
this.init();
};
/**
* JsLibTest prototype functions
*/
JsLibTest.prototype = {
init: function() {
// as expected this function runs fine
this.normal_function();
// nested level objects functions run fune from parent level object function
this.nestedObject.nested_object_function();
},
normal_function: function() {
console.log('this.normal_function() ran');
},
nestedObject: {
// calling a function on the parent object fails here when called from this nested object function
nested_object_function: function() {
this.normal_function();
console.log('this.nestedObject.nested_object_function() ran');
},
}
};
return JsLibTest;
})(document);
// run it
$(document).ready(function(){
var Sidebar2 = new JsLibTest();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Your assessment is correct. this will be set to the nested object instead of the parent object and that's why it says the function is undefined.
What you need is a way of referencing the parent. Objects don't normally carry any information needed to reference an object which references them. This makes sense when you consider the fact that many objects can reference the same object internally.
You can either store a reference to the parent object and reference that in the nested function:
var nested = {
g() {
this.parent.f();
}
};
var parent = {
f() {
console.log('called');
}
};
nested.parent = parent;
nested.g();
or you can use Function.prototype.call (or something similar) to set the correct context.
var obj = {
f() {
console.log('called');
},
g() {
this.nested.nested_f.call(this);
},
nested: {
nested_f() {
this.f();
}
}
};
obj.g();
Putting the last solution in to the context of your problem:
var JsLibTest = (function(document) {
"use strict";
var JsLibTest = function() {
this.init();
};
JsLibTest.prototype = {
init: function() {
this.normal_function();
// NOTICE: Using .call here to set the context
this.nestedObject.nested_object_function.call(this);
},
normal_function: function() {
console.log('this.normal_function() ran');
},
nestedObject: {
nested_object_function: function() {
this.normal_function();
console.log('this.nestedObject.nested_object_function() ran');
}
}
};
return JsLibTest;
})(document);
// run it
$(document).ready(function() {
var Sidebar2 = new JsLibTest();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
You are correct that scope doesn't have access to the parent. Easy solution would be that you pass parent to the nested object like:
this.nestedObject.nested_object_function(this);
then in your nested function call parent as:
nested_object_function: function(self) {
self.normal_function();
alert('this.nestedObject.nested_object_function() ran');
}
since you pass this (parent) as self you can then call it from nested one.
At first, the Object must be unique for Each, having a prototype:
this.nestedObject=Object.create(this.nestedObject);
var JsLibTest = function (){
// run init() function on initiation of a new JsLibTest object
this.init();
//bind to childs:
this.nestedObject.parent=this;
};
Now you can use this.parent inside of your inner function...
this.parent.normal_function();
If you want this to be the parent, bind:
var JsLibTest = function (){
// run init() function on initiation of a new JsLibTest object
this.init();
//bind to childs:
for(i in this.nestedObject){
var el=this.nestedObject[i];
if(typeof el==="function"){
this.nestedObject[i]=el.bind(this);
}
}
};
To make it easier, may use sth like that ( a helper function):
getfunc:function(...a){
a.reduce((obj,key)=>obj[key],this).bind(this);
}
Use like this:
JsLibTestInstance("nestedObject","nestedobject_function")();
Yea, you are right that the this value in your JSLibTest.prototype.nestedObject function is pointing to nestedObject and not JSLibTest.
If you want to maintain the same call signature, you can declare nestedObject as an IIFE:
nestedObject: (function() {
var that = this;
return {
nested_object_function: function() {
console.log(that);
// this.normal_function();
alert('this.nestedObject.nested_object_function() ran');
}
}
}())
https://jsfiddle.net/dnLzytju/1/
Note: You probably do not want to declare your prototype that way is it effectively deletes all the native prototype methods of the object.
To author your code in a similar way, consider using Object.assign to help you out.
var foo = Object.assign({}, Function.prototype, {
bar() {
console.log("Hello!")
}
});
foo.bar();

This context inside class

Consider following class and calling foo method: (jsFiddle: http://jsfiddle.net/dpvbd9LL/2/)
function MyClass() {
this.Prop = "Hello";
}
MyClass.prototype.foo = function () {
var self = this;
$("<p>" + self.Prop + "</p>").appendTo("#result");
}
function sampleMethod(callback) {
// do magic stuff
callback();
}
function executeMethod() {
var myClass = new MyClass();
sampleMethod(function() {
myClass.foo();
});
sampleMethod(myClass.foo);
}
(function() {
executeMethod();
})();
The output:
Hello
undefined
As you can see, there is a big difference between calls:
sampleMethod(function() {
myClass.foo();
});
sampleMethod(myClass.foo);
Why that happens? How to write a class to prevent such a situation?
This is caused by the fact that sampleMethod(myClass.foo); pass function of myClass but not in class context, if you test the current object this, you will see that is window, the global one.
If you declare sampleMethod in the object context, you could do something like that.
You can solve this by passing the parent scope to the anonymous function executioner, like this:
function sampleMethod(callback, scope) {
if(scope) scope[callback]();
else callback();
}
function executeMethod() {
var myClass = new MyClass();
sampleMethod(function() {
myClass.foo();
});
sampleMethod("foo", myClass);
}
The problem is that you are trying to pass a function of a -for the sampleMethod- unexisting scope. So if you pass both you can call them as you need to.

JavaScript, 'this' reference, when calling object function from anchor tag

Here is a simplified version of my JS:
var myObject = function() {
return {
functionOne: function() {
//some other logic here
},
functionTwo: function() {
var self = this;
//some logic here
//then call functionOne
self.functionOne();
}
};
}
Then I have this in the body of my html:
click me
Why do I get the error Uncaught TypeError: Object [some url] has no method 'functionOne', when I click the link?
The error that you're seeing doesn't reflect the example code you've shown.
That said, in the way that you're using the code, you should be able to reduce it to simply:
var myObject = {
functionOne: function() {
},
functionTwo: function() {
this.functionOne();
}
}
Your myObject is a function that needs to be called in order to get that object
click me
Why not just define myObject as an object:
var myObject = {
functionOne: function() {
//some other logic here
},
functionTwo: function() {
var self = this;
//some logic here
//then call functionOne
self.functionOne();
}
};
What you need is for your function to be executed immediately. Further to that your declarion of the self variable leads me to think that you are trying to create a closure so that you can access functionOne from functionTwo. If that is the case then I think the following is what you were after:
var myObject = (function() {
function func1( ) {
}
function func2( ) {
func1();
}
return {
functionOne: func1,
functionTwo: func2
};
}());

Call functions from function inside an object (object literal)

I'm learning to use object literals in JS, and I'm trying to get a function inside an object to run by calling it through another function in the same object. Why isn't the function "run" running when calling it from the function "init"?
var RunApp = {
init: function(){
this.run()
},
run: function() {
alert("It's running!");
}
};
That code is only a declaration. You need to actually call the function:
RunApp.init();
Demo: http://jsfiddle.net/mattball/s6MJ5/
There is nothing magical about the init property of an object, which you happen to have assigned a function to. So if you don't call it, then it won't run. No functions are ever executed for you when constructing an object literal like this.
As such, your code becomes this:
var RunApp = {
init: function(){
this.run()
},
run: function() {
alert("It's running!");
}
};
// Now we call init
RunApp.init();
You can try the following code. It should work:
var RunApp = {
init: function(){
RunApp.run()
},
run: function() {
alert("It's running!");
}
};

Define an anonymous function as part of an object definition?

Suppose I have:
var myfunc = function() {
// do stuff
}
myfunc.foo = function() {
//do other stuff
};
Now myfunc has a property foo that is a function, great. Is there a way to create myfunc from the get-go in this state? That is, I want foo to be defined when myfunc is created. The syntax, I would imagine, is something like:
var myfunc = {
:function() {
// do stuff
},
foo: function() {
// do other stuff
}
}
Except that's wrong.
You can place an anonymous function inside an object, however the only plausible way of doing this is to call the anonymous function when the object is initialised, otherwise the function will never be able to be called - it's anonymous!
Here's a JSFiddle: http://jsfiddle.net/g105b/99K5F/
var myfunc = function() {
this.foo = function() {
console.log("myfunc.foo called!");
};
(function() {
console.log("Anonymous function called.");
})();
};
// Initialising "myfunc" will call the anonymous function.
var instance = new myfunc();
// Now the foo method can be called.
instance.foo();
A little confused as to what functionality you are looking to gain here...
If you want some code to execute when the myfunc is defined, you could use the module pattern:
var myfunc = (function() {
var self = {};
// any initialization code can go here
alert("myfunc init code");
self.somePublicMethod = function () {
}
return self;
}());
This can also be called an immediate function, a function that is defined and executed at the same time.
From within the closure, and code that is not defined as part of another function will be executed when the object is defined, so when you do something like this:
myfunc.somePublicMethod()
the alert would have already been fired.
(This answer written before the first half of the question was significantly revised)
Now myfunc has a property foo that is a function
No, it doesn't.
You called it with myfunc() so this is a reference to the global window object, thus you are creating window.foo.
Possibly what you are looking for is:
function myfunc () {
// do stuff when myfunc is called
}
myfunc.foo = function () {
// do stuff when myfunc.foo is called
};
or perhaps:
function myfunc () {
// do stuff when myfunc is instantiated
this.foo = function () {
// Add a foo property to this when myfunc is instantiated
// ... which is only worth while if you are doing pretty
// ... odd stuff with the variables that are passed in
}
}
var instance = new myfunc();
or maybe:
function myfunc () {
// do stuff when myfunc is instantiated
}
myfunc.prototype.foo = function () {
// Have a foo function on every instance of myfunc
}
var instance = new myfunc();
… but you've abstracted the problem you are trying to solve away in your question, so it is hard to tell what you are actually trying to achieve.
You can use jQuery:
var myFunc = jQuery.extend(
function() { ... },
{
bar: "wioll haven be",
foo: function() { alert(myFunc.bar); }
}
);
myFunc();
myFunc.foo();
This is mostly code acrobatics, this is probably the closest you'll get:
var myfunc;
(myfunc = function(){}).foo = function(){};
There is no practical difference in declaring methods later though, since javascript is single-threaded.

Categories