Correct scope reference to object within window object - javascript

Since my previous question was not answered i thought i give it another try only formulated better.
(function() {
var ns = {};
for(var i = 0; i < 2; i++){
ns['someName'] = 'na' + i;
//Logs ns0 and ns1 like i want
console.log(ns["someName"]);
window.addEventListener('load', function(){
//Logs ns1 twice
console.log(ns["someName"]);
});
}
})();
So the question being how to keep the correct scope within the window eventListener.
Thanks!

You can use the magic of closures
(function(value) {
window.addEventListener('load', function(){
//Logs ns1 twice
console.log(value);
});
})(ns["someName"]);
Defined an inline function and called it inmediatly using the current value for ns["someName"]

I simplified the code to show how you can 'preserve' the scope within each load-callback-function. You can use a self-execution function to return a closure:
(function() {
for(var i = 0; i < 2; i++) {
window.addEventListener('load', (function(i){
return function(evt) {
// i is as expected: 0 and then 1
console.log(i);
// the variable 'evt' contains the event-object
};
})(i));
}
})();

thank you all for your input much appreciated! ive been battling this for a two days now and i was about to pull out my hair.
here is the final code:
(function() {
for(var i = 0; i < 2; i++){
var ns = {};
ns['someName' + i] = 'ns' + i;
//Logs ns0 and ns1 like i want
console.log(ns);
(function(value) {
window.addEventListener('load', function(event){
//Logs ns0 and ns1 like i want
console.log(value);
//preseved my events
console.log(event);
});
})(ns);
}
})();

(function() {
var ns = {};
for(var i = 0; i < 2; i++){
ns['someName'] = 'na' + i;
console.log(ns["someName"]);
(function(value){
window.addEventListener('load', function(){
console.log(value);
});
}(ns['someName'] ));
}
})();

You can achieve it with the help of closures
(function() {
var ns = {};
for(var i = 0; i < 2; i++){
ns['someName'] = 'na' + i;
console.log(ns["someName"]);
(function(value){
window.addEventListener('load', function(){
return (function(){
console.log(value);
})()
});
}(ns["someName"]));
}
})();
This will do the trick :)

Related

How to make a prototype within a closure?

Here, I have this two code :
var mod = function() {
var a = function() {
this.fucname = 'hello';
};
a.prototype.build = function() {
return 'before '+this.fucname;
};
return new a();
};
for( var i=0; i<10000; i++ ){
var newfuc = mod();
};
and
var a = function() {
this.fucname = 'hello';
};
a.prototype.build = function() {
return 'before '+this.fucname;
};
for( var i=0; i<10000; i++ ){
var newfuc = new a();
};
After I check both in chrome dev, the second code take a JS HEAP 3.0MB,
the first code take a JS HEAP 10MB.
Is that mean, the build function has been created 10000 time in the first code? and how can I refine it without remove the cover mod?
I have to pass something into the function...
If you want to hide the constructor but also only evaluate it once, you can make use of an IIFE to create a new scope:
var mod = (function() {
var a = function() {
this.fucname = 'hello';
};
a.prototype.build = function() {
return 'before ' + this.fucname;
};
return function() {
return new a();
};
})();
for (var i = 0; i < 10000; i++) {
var newfuc = mod();
}

Get var from another function?

Hello im trying to get a variable from another function
Code:
$(document).ready(function() {
var totalFiles;
$(".filesUpload").on("change", function() {
var files = $(".filesUpload").prop("files");
var names = $.map(files, function(val) { return val.name; });
$("#uploadBar").modal('show');
var totalFiles = names.length;
});
$("#uploadBar").on("shown.bs.modal", function() { var test = totalFiles; alert(test); });
But right now i only get undefined on the totalFiles, but if i have a alert right after var totalFiles = names.length i get the right result, anyone knows what i can do and what am i doing wrong?
PS. I've checked other threads aswell and it still doesn't work.
Check your references to totalFiles. You have one declared globally and then declared it locally in $(".filesUpload").on("change", function() {.
This is what it needs to be :
$(document).ready(function() {
var totalFiles;
$(".filesUpload").on("change", function() {
var files = $(".filesUpload").prop("files");
var names = $.map(files, function(val) { return val.name; });
$("#uploadBar").modal('show');
totalFiles = names.length;
});
$("#uploadBar").on("shown.bs.modal", function() { var test = totalFiles; alert(test); });
Just remove 'var' from the 1st function.
Do not declare the variable twice. In your onChange function, just use totalFiles rather than declaring it again with the var keyword.
$(".filesUpload").on("change", function() {
...
totalFiles = names.length;
...
});
You are declaring a local variable inside your callback. totalFiles = names.length; is the right way to update the global variable.
var totalFiles;
$(".filesUpload").on("change", function() {
var files = $(".filesUpload").prop("files");
var names = $.map(files, function(val) { return val.name; });
$("#uploadBar").modal('show');
totalFiles = names.length;
});

JavaScript - "this" pointing to Window instead of object

I'm facing for the first time OOP in JavaScript and all the troubles that comes with it...
I have this function/Object/class/whatever which has a method mainLoop() that should display some falling text - just like in the movie The Matrix. When I call it though I get undefined variables errors and using the debugger I see that inside mainLoop() this is pointing to Window instead of the object that called the method.
Here's the code:
function Matrix(config) {
return {
//[...lots of other vars...],
drops: [],
lines: [],
//final string to put in the container
str: "",
mainLoop: function(){
var tmp = "";
//randomly create a "character drop"
//(not if there's already a drop)
for(var i = 0; i < this.cols; i++){
if(this.drops[i] == 0 && Math.random() < this.freq){
this.drops[i] = irandom(this.rows) + 1;//new drop
tmp += randomChar();//output drop
}
else tmp += lines[0].charAt(i);
}
this.lines[0] = tmp; // <-------------- ERROR
//update already created drops
tmp = "";
for(var j = 0; j < this.cols; j++){
if(this.drops[j] > 0){
tmp += this.randomChar();
this.drops[j]--;
}
else tmp += " ";
}
this.lines[this.rowIndex] = tmp;
this.rowIndex = (this.rowIndex+1) % this.rows;
//render the entire text
this.str = "";
for(var l in this.lines)
this.str += l + "<br/>";
$(container).html = this.str;
},
start: function(){
for(var i = 0; i < this.cols; i++)
this.drops[i] = 0;
timer = setInterval(this.mainLoop ,this.delay);
},
stop: function(){
clearInterval(this.timer);
},
randomChar: function(){
return this.chars.charAt(irandom(this.chars.length));
},
irandom: function(x){
return Math.floor(Math.random()*x);
}
}
};
And then I call this function like this:
var config = {
container: "#container",
rows: 20,
cols: 20,
delay: 2000
};
var m = Matrix(config);
m.start();
The browser console says:
TypeError: this.lines is undefined
(code comment shows the exact point of the error). Furthermore, the debugger says that, at that point, this points to Window, not to m as I would expect... what's wrong with my reasoning? Thanks in advance for any help.
Alter your start function:
start: function(){
var self = this;
for(var i = 0; i < this.cols; i++)
this.drops[i] = 0;
timer = setInterval(function() {
self.mainLoop();
}, this.delay);
}
this was poiting at window because the scope has changed.
Since JavaScript is prototype-based, maybe (if you haven't already) try doing it following this model:
function Matrix(config) {
this.property = config.firstmember;
this.property2 = config.secondmember;
return function() { console.log('hello world') };
}
Matrix.prototype = {
someMethod: function() {
//do something
},
start: function() {
//console.log('hello world');
},
stop: function() {
//do something
}
}
var config = {
firstMember: 'foo',
secondMember: 'bar'
}
var m = new Matrix(config);
//console output: "hello world"
/*var m = {
property: 'foo',
property2: 'bar',
____proto___: Matrix: {
someMethod: function() {
//do something
},
start: function() {
//console.log('hello world');
},
stop: function() {
//do something
}
}
}*/
Also, see the answer to this question regarding setInterval.
setInterval callback functions are members of the Window object; therefore, 'this' refers to the window. You will need to pass in a parameter of the current object to the callback that is inside setInterval. See the link above for more details.
If you need a reference to the calling object, I'd suggest passing it down as a parameter to the function.

Using closures to capture onclick events

I'd like to correct this example:
var $foo = $('#foo');
for (var i = 0; i < 10; i++) {
$foo.append($('<li></li>').text(i).on('click', function() {
alert(i);
}));
}
so that it alerts the correct value of i when clicked.
How can I do this? I know that the answer is to use a closure, but I'm not sure how to implement it.
I've tried doing
$foo.append($('<li></li>').text(i).on('click', function() {
return (function(i) {
alert(i);
})();
}));
but that returns undefined.
JSFiddle here: http://jsfiddle.net/tmcw/4phm7/1/
The standard idea is to have an immediately invoked function :
var $foo = $('#foo');
for (var i = 0; i < 10; i++) {
(function(i){
$foo.append($('<li></li>').text(i).on('click', function() {
alert(i);
}));
})(i);
}
You need to create a copy of the i variable for each function:
$foo.append($('<li></li>').text(i).on('click', function(copy_i) {
return (function() {
alert(copy_i);
})(i);
}));

Javascript function doesn't receive an argument correctly [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
JavaScript closures and variable scope
Assign click handlers in for loop
I have this script:
var MyClass = {
MyArray: new Array(0, 1, 2, 3, 4),
MyFunc1: function() {
var i = 0;
for (i = MyClass.MyArray.length - 1; i>=0; i--) {
var cen = document.getElementById("cen_" + i); // It is an img element
cen.src = "col.png";
cen.className = "cen_act";
cen.onclick = function() { MyClass.MyFunc1(i); };
} else {
cen.src = "no.png";
cen.className = "cen";
cen.onclick = null;
}
}
},
MyFunc2: function(id) {
alert(id);
}
}
My problem is that, at this line :cen.onclick = function() { MyClass.MyFunc1(i); }; the argument sent to MyFunc2 is always -1. The MyFunc1 function should create four images, each one with an onclick event. When you click on each image, the MyFunc2 function should show the corresponding i value. It looks like the i value is not "saved" for each event and image element created, but only its "pointer".
Thanks!
You should be familiar with the concept of JavaScript closures to understand why this happens. If you are, then you should remember that every instance of the
function() { MyClass.MyFunc1(i); };
function closure contains i's value of -1 (since it is the final value of this variable after the entire loop finishes executing.) To avoid this, you might either use bind:
cen.onclick = (function(i) { MyClass.MyFunc1(i); }).bind(null, i);
or use an explicitly created closure with the proper i value.
It's a normal case and misunderstand of closures, see this thread and you may get some clue, the simply way to fix this problem is to wrap your for loop body with an Immediate Invoked Function Expression
MyFunc1: function() {
var i = 0;
for (i = MyClass.MyArray.length - 1; i>=0; i--) {
(function(i) {
var cen = document.getElementById("cen_" + i); // An img element
cen.src = "col.png";
cen.className = "cen_act";
cen.onclick = function() { MyClass.MyFunc2(i); };
} else {
cen.src = "no.png";
cen.className = "cen";
cen.onclick = null;
}
}(i));
}
}
You are capturing a variable that changes inside the loop, so you always get the last value of i.
You can easily fix that by creating a closure:
MyFunc1: function() {
var i = 0;
for (i = MyClass.MyArray.length - 1; i>=0; i--) {
(function(i) {
var cen = document.getElementById("cen_" + i); // An img element
cen.src = "col.png";
cen.className = "cen_act";
cen.onclick = function() { MyClass.MyFunc2(i); };
} else {
cen.src = "no.png";
cen.className = "cen";
cen.onclick = null;
}
})(i);
}
},

Categories

Resources