Javascript: onclick does not call function - javascript

When I run the following code, the alert messages shows up but this.test() does not run.
function SomeFunction() {
document.onclick = function(e) {
alert("Hi");
this.test();
};
}
SomeFunction.prototype.test = function (){
...
}
However when I try this:
function SomeFunction() {
document.onclick = this.test();
}
SomeFunction.prototype.test = function (){
...
}
this.test() will run.
EDIT: I have added more details to my code.

Your issue is related to scope. Try:
document.onclick = function(e) {
alert("Hi");
test();
};
this will vary depending upon where it is called from. In your first example, you are calling it from within an anonymous function and therefore the scope of this shifts to inside that function. In the second example, the scope is a level higher and refers to your SomeFunction() function.
Since your function is nested inside another function, you may want to reassign this to a another variable that is accessible up the scope chain. E.g.
function SomeFunction() {
that = this;
document.onclick = function(e) {
alert("Hi");
that.test();
};
}

In the first example, the word "this" refers to function itself. So the function test would be outside its scope.
But on the second one you are telling the code that this.test() is the actual onclick function, that is why it's working there and not on the first one.

Related

how does this function expression work?

Function express CANNOT be used before being declared correct? But is also passed in a keypress function as well. How is this magic happening?
I am doing a code along and noticed it while I was looking over it.
var controller = (function(budgetCtrl, UICtrl){
var setUpEventListeners = function(){
//CTRLADDITEM is called below
document.querySelector(DOM.inputBtn).addEventListener('click', **ctrlAddItem**);
document.addEventListener('keypress', function(e){
if(event.keyCode === 13 || event.which === 13){
**ctrlAddItem();**
}
});
}
var **ctrlAddItem** = function(){
updateBudget();
}
};
}
})(budgetController, UIController );
This is called IIFE (Immediately Invoked Function Expression) and it's fairly common in Javascript as well as other languages like Go. Here is a simple version...
(function() {
alert('invoked immediately');
})();
This code will run immediately. Note: it is not invoked before it is defined. It is invoked by the trailing parenthesis, which come directly after it is defined. It is the exact same thing as doing this...
function doSomething() {
}
doSomething();
We have just inlined the function by wrapping it in parenthesis and then called it (), instead of calling it by name.
If you assign the result to a variable, it works just as expected, you are assigning the result of the function.
// these two are equivalent
var result = (function () {
return 5;
})();
var result = 5;
Now, the value in result will equal 5.
Why do we use them?
Most commonly, we use them to isolate the scope of your code to prevent it from polluting the global scope. For example, if this is your application code...
function MyApp() {
}
You have now polluted the global scope by creating window.MyApp. If you use a third party library that also provides a function on the global scope called MyApp, it will override yours. To prevent that, we can do...
(function(window) {
function MyApp() {}
MyApp();
})(window);
Now, MyApp is not attached to the window and we still have access to the window.
Further down the rabbit hole
You must convert your function from a declaration to an expression before you can invoke it immediately. To do this, you wrap it in parenthesis.
This does NOT work
function (){
// do something
}()
This DOES work (thanks to the parenthesis)
(function () {
})()
You can also use any unary operator instead of parenthesis. All of these work
~function () {
}()
+function () {
}()
-function () {
}()
void function () {
}()
What you have is:
// Pass ctrlAddItem
document.querySelector(DOM.inputBtn).addEventListener('click', ctrlAddItem);
// Include closure to ctrlAddItem in function expression
document.addEventListener('keypress', function(e){
if(event.keyCode === 13 || event.which === 13){
ctrlAddItem();
}
});
// later...
var ctrlAddItem = function(){ /* whatever */ }
So ctrlAddItem is declared, and therefore exists with a value of undefined before any code is executed. At the point it's used in addEventListener, its value is still undefined.
However, before the outer function completes (and before the listeners are called), ctrlAddItem is assigned a value. So by the time the listeners are called, it's a (reference to a) function.

Adding a function to global windows object

I was playing around with javascript objects to understand "this" and function context better. I stumbled across this problem. I get the error "obj2 is not defined" unless I run window.obj2() after assigning it, but I don't know why. Shouldn't it be enough to assign the function to window.obj2 without also executing it immediately afterwards? I know that you're not supposed to pollute the window object, this is just a test.
Thanks!
window.obj2 = function(){
console.log('obj2 in window.object',this);
}
window.obj2(); // problem when this line is commented out
(function () {
var parent = {
obj : function(){
//console.log(this);
obj2();
this.obj2();
window.obj2();
},
obj2 :function(){
console.log('obj2 in parent',this);
}
}
parent.obj();
}());
EXPLANATION
OP asks why does he have to execute the function after defining it in order for it to become defined later in the code... See what happens when you comment out the problem line.
Solution to the mystery:
You forgot a semicolon:
window.obj2 = function(){
console.log('obj2 in window.object',this);
}; // <--
Without it, the code will be interpreted as
// I'm naming the functions to refer to them later
window.obj2 = function a(){
...
}(function b() { ... }());
I.e. the parenthesis around b are interpreted as call operation of a (just like you did with b itself: (function b() {...}()).
The engine first executes b in order to pass the return value as argument to a, and only after that the return value is assigned to window.obj2.
So, at the moment b is called, window.obj2 does indeed not exist yet.
So, the reason why adding window.obj2() makes it work is not because you are accessing window.obj2, but because it makes the code un-ambigious. The following parenthesis cannot be interpreted as call operation anymore. You could use any statement there, e.g.
window.obj2 = function(){
console.log('obj2 in window.object',this);
}
"foo";
(function () {
obj2();
}());
If you defined function window.obj2 inside the anonymous function which does call itself then it works fine, have a look code.
<script>
//window.obj2(); // problem
(function (window) {
window.obj2 = function(){
console.log('obj2 in window.object',this);
}
var parent = {
obj : function(){
//console.log(this);
obj2();
this.obj2();
window.obj2();
},
obj2 :function(){
console.log('obj2 in parent',this);
}
}
parent.obj();
}(window));
</script>
<body>
</body>

self-defining function(also called lazy function definition) when using function declaration

In the "JavaScript Patterns" book by Stoyan Stefanov, there's a part about Self-Defining Function.
var scareMe = function(){
console.log("Boo!");
scareMe = function(){
console.log("Double Boo!");
}
}
scareMe();//==>Boo!
scareMe();//==>Double Boo!
It works as I expected. But I modify the scareMe function as following:
function scareMe(){
console.log("Boo!");
function scareMe(){
console.log("Double Boo!");
}
}
scareMe();//==>Boo!
scareMe();//==>Boo!
Problem:
what's the difference between them?
In the second case, why the output is not "Double Boo!", but "Boo!"
The first scareMe function when invoked overwrite its own behavior by creating another function scareMe inside it, which overwrites the one in the upper scope, so the definition of original scareMe changes, i have see this approach been used if you want to do a first time set up in an application and want to change its behavior all over right after setting it up.
If you had defined:
var scareMe = function(){
console.log("Boo!");
var scareMe = function(){ //define it with var
console.log("Double boo!");
}
}
scareMe();//==>Boo!
scareMe();//==>Boo! //you will see the behavior as that of the second one.
Also one practical implementation of a one time setup:
var scareMe = function(){
console.log("Boo!");
//I have done my job now. I am no longer needed.
scareMe = undefined;
}
scareMe();//==>Boo!
scareMe();//==> oops error
Second Case you are creating a new function with the name scareMe whose scope is only within the function, it doesn't overwrite itself.
Try this one for instance:
function scareMe(){
console.log("Boo!");
function scareMe(){
console.log("Double bool!");
}
scareMe(); //Now this invokes the once defined inside the scope of this function itself.
}
scareMe();//==>Boo! and Double bool!
In your first approch, scareMe is a global variable (in your context). When in the "double boo", you change the value of that global variable, so it works.
In the second approch, the inner scareMe is a local variable and it won't change value of the global one.
So it's about the variable scope.
Except for hoisting and debuggability, you can consider:
function f(/* ... */) { /* ... */ }
To be equivalent to:
var f = function(/* ... */) { /* ... */ };
If we translate your second code sample to use this second form, we get:
var scareMe = function() {
console.log("Boo!");
var scareMe = function() {
console.log("Double bool!");
};
};
Note that this is not the same as your first snippet; the inner function definition has a var on it. With the inner var, it creates a new variable called scareMe that shadows the outer one.

Naming a formerly anonymous function breaks

Why does this not work:
$(document).on('click','a',myFunction);
var myFunction = function() {
debugger;
}
When this does:
$(document).on('click','a',function() {
debugger;
}
I've started to learn more by naming all my anonymous functions and breaking them out into their own separate named functions.
You have to swap the lines:
var myFunction = function() {
debugger;
}
$(document).on('click','a', myFunction);
Otherwise, you would be assigning undefined as the event handler, as the variable myFunction doesn't have a value yet when you were passing it to .on.
Also: assigning a function to a variable like that doesn't make it named, it's still an anonymous function, just stored in a variable. A named function would be:
var myFunction = function someName() {
debugger;
}
See Named function expressions demystified for more details.
Instead of saying:
var myFunction = function() {
debugger;
}
you need to say:
function myFunction() {
debugger;
}
This will declare the function on the first pass so that it can be referenced during program execution.

accessing a hidden function with javascript

Hi I have a few custom functions wrapped in jQuery's document.ready function. Most of these functions are utilized from within that function and work, but there is a case where I would like to access a function contained within this from the global scope. How can I do this? can i do something like:
jQueryReadyScope.myFunctionName('paramaters');
Thank you very much.
Nope, but you can name the function and pass it to .ready():
var myFunctionName = function (params) {
// do things
}
// pass as callback to ready function
jQuery(document).ready(myFunctionName);
// access directly like any other function:
myFunctionName('paramaters');
That's a scope issue, and all you need to do is specify the namespace. In this case, you're talking global so we'll use window.
window.myFunction = function() { ... stuff }
To access it from the global scope it would need to be assigned to a global variable, either by declaring it outside your document ready or by assigning it as a property of window:
var yourGlobalFunction1 = function() { ... }
$(document).ready(function() {
function privateFunction() { ... }
window.yourGlobalFunction2 = function() { ... };
yourGlobalFunction1();
privateFunction();
yourGlobalFunction2();
});
yourGlobalFunction1();
// and then at some later point AFTER the document ready has run,
// e.g., in response to some event:
$("#someelement").click(function() {
yourGlobalFunction2();
});

Categories