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!");
}
};
Related
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();
Hey guys am a bit confused in running a function from the other. What I have tried is:
var a = (function() {
var babe = function() {
console.log('yay');
};
})();
When I run the code like a.babe() it gives me error like TypeError: Cannot read property 'babe' of undefined. Why is it happening like this?
I want to call the function babe from a ... How can i do it?
I hope you guys can help me ...
Thanks in advance.
You are creating a function called babe and it stays only within the scope of the immediately invoked function expression (IIFE) surrounding it. And since you are not returning anything to the caller, by default, the function call will be evaluated to undefined and that will be stored in a. That is why you are getting the error,
TypeError: Cannot read property 'babe' of undefined
It means that, you are trying to access a property called babe on an undefined value. In this case, a is undefined.
Instead of all this, you can simply return the babe function from the IIFE, wrapped in an object, like this
var a = (function() {
return {
babe: function() {
console.log('yay');
}
};
})()
Now, that the returned object is assigned to a, you can invoke babe function like this
a.babe();
babe is a local variable to the IIFE in that code. It isn't returned. It isn't a property of anything. It isn't accessible.
You have to expose it publicly if you want it to call it from outside that function.
var a = (function() {
var babe = function() {
console.log('yay');
}
return babe;
})();
a();
var a = (function() {
var babe = function() {
console.log('yay');
}
return {
"babe": babe
};
})();
a.babe();
Why have you made a a self executing function? What you want to do is to make a function babe part of an Object a for that, simply do this:
var a = {
babe: function() {document.write('yay');}
}
a.babe();
I have made an object a and added a function babe inside it.. then used the dot notation to access the function inside object a
To use your code you must do
var a = (function() {
var babe = function() { console.log('yay'); }
return babe
})();
a();
To make it a.babe() look at #thefourtheye or #Quentin.
The dot syntax is used to access a "property" of an object. So, in a simple example, you could use something like:
var a = {
babe: function(){ console.log('yay') }
};
a.babe(); // yay
In the case of your example, if you want to create a closure to hide other variables and functions from the global scope, you have to return an object that will then get assigned to a:
var a = (function() {
return {
babe: function(){ console.log('yay') }
}
})();
a.babe(); // yay
Just remember, anytime you use a dot, the variable in front HAS to be an object.
I am trying to create method inside a function. I can make this such a way:
function sample() {};
sample.show = function() { alert() };
And I'll see the alert calling sample.show();. But for the reason of code beautifying I want to move all method declarations inside functions. I tried that:
function sample() {
sample.show = function() { alert() };
}
But I get: TypeError: Object function sample() has no method 'show'
Another ways I tried:
function sample() {
this.show = function() { alert() };
}
function sample() {
sample.prototype.show = function() { alert() };
}
function sample() {
this.prototype.method = function show () { alert() };
}
But result was the same. I can't even find any information about creating methods inside functions. Can you point me a right way?
UPD: I want to have ability to call sample() function which also does some stuff. So there are no solution in answers yet.
function sample() {
this.show = function() { alert() };
console.log('you called "sample()"');
}
sample(); // ==> you called "sample()"
First attempt:
function sample() {
sample.show = function() { alert() };
}
This would only create a "static" method on the sample function and only after executing it
console.log(sample.show);
//would show undefined on the console
sample();
console.log(sample.show);
//would then show the method definition as it has now been
//defined as a property of "sample"
Second Attempt:
function sample() {
this.show = function() { alert() };
}
This will only work if you create a instance of sample
console.log(sample.show);
//will print undefined as there is no property "show" on sample
sample();
console.log(window.show);
//will print the function, but since sample was executed without a
//context, show is created as a property of the global object
//in this case "window"
var d = new sample();
console.log(d.show);
//will print the function as we now have a instance of sample which
//show was made a property of
console.log(sample.prototype.show);
//will show undefined as you are not actually creating the "show" method
//on the constructor sample's prototype
Now with the prototype version:
function sample() {
sample.prototype.show = function() { alert() };
}
With this after you will be able to access the "show" method either through the instance or from the prototype chain, but for the prototype chain call to work you have to at least make one instance before hand
console.log(sample.prototype.show);
//will print undefined as "show" has not been defined on the prototype yet
var d = new sample();
console.log(d.show);
console.log(sample.prototype.show);
//Both will now print the function because the method has been defined
However the last one:
function sample() {
this.prototype.method = function show () { alert() };
}
Will not work at all as you can not access the prototype directly from the instance you will get a undefined error once you try to create an instance.
For it to work you would have to go through the constructor chain to set the method
function sample() {
this.constructor.prototype.method = function show () { alert() };
//is the same as doing
sample.prototype.method = function show(){ alert() };
}
So overall for your "beautifying" to work you have to call sample first, either directly, for the first one, or by creating an instance for the others.
the code in the sample function will not be executed until sample is called. So you could get your example to work by doing this:
function sample() {
sample.show = function() { alert() };
}
sample()
sample.show()
You can try this:
function sample() {
this.show = function() { alert() };
}
var x = new sample();
x.show();
you need to make sample an object
var sample = {
this.show = function() { alert() };
}
I have two simple links that when clicked should wait one second and then add a class that changes the color of the text. The working version uses $.proxy and the non-working version I'm trying to use native JavaScript to change the meaning of this. Why is btnWaitNoProxy this still referring to the global object?
fiddle
code:
var obj = {
wait: function () {
setTimeout(function () {
console.log('inside the setTimeout');
$(this).addClass('lesson');
//refers to global object in the console
}, 1000);
}
};
$('#btnProxy').on('click', function () {
console.log('preparing to add class...');
setTimeout($.proxy(function () {
$(this).addClass('lesson')
console.log(this);
}, this), 1000);
});
$('#btnWaitNoProxy').on('click', function () {
console.log(this);
//call still refers to the global object
obj.wait.call(this);
});
Because you are using setTimeout in wait, so that callback method passed to setTimeout will be executed in the global context by default.
One possible solution is to use $.proxy() as you have done again with the setTimeout handler
var obj = {
wait: function () {
setTimeout($.proxy(function () {
console.log('inside the setTimeout');
$(this).addClass('lesson');
//refers to global object in the console
}, this), 1000);
}
};
Another is to use a closure variable like
var obj = {
wait: function () {
var self = this;
setTimeout(function () {
console.log('inside the setTimeout');
$(self).addClass('lesson');
//refers to global object in the console
}, 1000);
}
};
When you pass a method to setTimeout() (or any other function, for that matter), it will be invoked with a wrong this value.
Code executed by setTimeout() is run in a separate execution context to the function from which it was called. As a consequence, the this keyword for the called function will be set to the window (or global) object; it will not be the same as the this value for the function that called setTimeout.
Source: MDN — window.setTimeout
There are plenty solutions:
Using $.proxy
Using Function.prototype.bind
Aliasing this
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();
}
);
}