This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Javascript closure inside loops - simple practical example
Javascript infamous Loop problem?
I have a base function, that I want to control what ends up being like a "bumpbox". My goal is to instantiate multiple instances of this and give each of the declared variables a custom config.
The object looks like this:
Project.Modules.bumpbox = function(in_trigger, container) {
var config = {
'speed': 500,
'easing' : false,//will
'in_callback': false,
'out_callback' : false,
'visible': false,
'out_trigger' : $('#bumpbox_out_trigger'),//this is set by default only change if you know what you are doing!
};
this.test = function() {
//this should be the default function.
};
And then, from another file, I want to instantiate an instance like new Project.Modules.Bumpbox() and overwrite the test function.
var bumpbox_controllers = {
"map" : new Project.Modules.bumpbox($('#navigation_top li.map'), $('.bumpbox.map')),
"contact" : new Project.Modules.bumpbox($('#navigation_top li.contact'), $('.bumpbox.contact')),
"about" : new Project.Modules.bumpbox($('#navigation_left li.about'), $('.bumpbox.about')),
"team" : new Project.Modules.bumpbox($('#navigation_left li.team'), $('.bumpbox.team')),
"careers" : new Project.Modules.bumpbox($('#navigation_left li.careers'), $('.bumpbox.careers')),
"services" : new Project.Modules.bumpbox($('#navigation_left li.services'), $('.bumpbox.services'))
};
and then I want to loop through each of those and set a custom test() function in each like this:
bumpbox_controllers[i]['test'] = function() {
alert(i);
}
But when I run this code, it will switch all of the elements to the last i value called, in this case "service", not giving each a unique element.
You seem to need a closure for your loop:
for (var controller in bumpbox_controllers) {
bumpbox_controllers[controller] = (function(i) {
// creating a new context for i
return function() {
alert(i); // access the i in scope, not the controller
}
})(controller);
}
To allow each test() to have its own unique i, try:
bumpbox_controllers[i]['test'] = (function (i) {
return function () {
alert(i);
};
}(i));
Related
This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 1 year ago.
I'm working on a workaround for another problem I'm having but with this I got a "this.setState is not a function" error. I found this answer which advises to bind it within the constructor, which I did.
This is part of my constructor:
this.newProject = this.newProject.bind(this);
this.openProject = this.openProject.bind(this);
this.saveProject = this.saveProject.bind(this);
And this is my function:
// Open a file, set data as session item and reload page
openProject(FileObject) {
var read = new FileReader();
read.readAsBinaryString(FileObject);
read.onloadend = function() {
//sessionStorage.setItem("reloading", "true");
//sessionStorage.setItem("data", read.result);
//document.location.reload();
// Fix for missing data.
var jsonData = JSON.parse(read.result);
for (var i = 0; i < jsonData.blocks.length; i++) {
var name = jsonData.blocks[i].name;
var id = jsonData.blocks[i].id;
var ip = jsonData.blocks[i].ip;
var port = jsonData.blocks[i].port;
this.setState( { blockCount: (i + 1), });
// Add block to the list
this.setState({
blocks: this.state.blocks.concat({
id: id,
name: name,
ref: React.createRef(),
positionX: window.innerWidth*0.4 - 125 / 2,
positionY: 75 + ( 50 * this.state.blocks.length),
links:[],
requires: this.state.parameters.blockRequires
})
});
}
}
}
What would be a solution to this?
this belongs to the closest function context, which in your case is read.onloadend = function()...NOT the class.
You can solve this problem by assigning the class-level this to a new variable before you enter that ad-hoc function:
openProject(FileObject) {
var read = new FileReader();
read.readAsBinaryString(FileObject);
var that = this;
read.onloadend = function() {
// ...
that.setState(/*... etc*/
And of course you'll want to change all instances of this within your onloadend callback function to that (or whatever variable name you choose).
*Edit: as #MDTabishMahfuz describes, you can also use an arrow function, because unlike the function declaration, an arrow function:
Does not have its own bindings to this or super
Binding this is necessary for functions of the class but you have an additional callback (onloadend function) which is a different function and the component's this is not available there.
Conventional Functions in JS have their on this in their context. You can either use the method suggested by #David784 or use an arrow function for the onloadend handler like so:
read.onloadend = () => {
....
this.setState(....);
....
}
Arrow functions have this from the parent's context, that is the React component in your case.
Examples on node-opcua # https://github.com/node-opcua/node-opcua say that I need to rewrite code for every variable added to the OPC server, this is achieved calling 'addressSpace.addVariable()'... But if I have 1000 variables it could be an hard task... and eventually each custom user want a code rewrite, it could be tedious... so I'm trying to do it dynamically.
The opc read 'tags' from another custom server (not OPC).
With this 'tags' the opc server needs to add them to node 'device'.
When the OPC server node-opcua find a get or set of a variable coming from the net, it call the get or set of the correct variable:
for (var i = 0; i < tags.GetTags.length; i++)
{
variables[tags.GetTags[i].Tag] = {"value" : 0.0, "is_set" : false};
addressSpace.addVariable({
componentOf: device, // Parent node
browseName: tags.GetTags[i].Tag, // Variable name
dataType: "Double", // Type
value: {
get: function () {
//console.log(Object.getOwnPropertyNames(this));
return new opcua.Variant({dataType: opcua.DataType.Double, value: variables[this["browseName"]].value }); // WORKS
},
set: function (variant) {
//console.log(Object.getOwnPropertyNames(this));
variables[this["browseName"]].value = parseFloat(variant.value); // this["browseName"] = UNDEFINED!!!
variables[this["browseName"]].is_set = true;
return opcua.StatusCodes.Good;
}
}
});
console.log(tags.GetTags[i].Tag);
}
As I say I tried to use the 'this' in get and set functions with half luck, the get has a 'this.browseName' (the tag name) property that can be used to dynamic read my variables and it currently works.
The problem is with the set, in set 'this.browseName' and 'this.nodeId' don't exist! So it gives 'undefined' error. It also doesn't exist in variant variable.
Do you know a work-around to use dynamic variables with the above code? I need to have one for loop with one get and one set definitions for all variables (tags), that read and write a multi-property object or an array of objects, like 1 get and 1 set definitions that write the right variable in a n records array.
PS: I found on stack overflow this:
var foo = {
a: 5,
b: 6,
init: function() {
this.c = this.a + this.b;
return this;
}
}
But in my case node-opcua Variable doesn't has a 'this' working like the example. In the 'set' (like init): this.browseName (like a) and this.nodeId (like b) are not reachable.
Gotcha,
you need to cast get and set properties as functions like:
addressSpace.addVariable({
componentOf: device,
browseName: _vars[i].Tag,
dataType: "Double",
value: {
get: CastGetter(i),
set: CastSetter(i)
}
});
with
function CastGetter(index) {
return function() {
return new opcua.Variant({dataType: opcua.DataType.Double, value: opc_vars[index].Value });
};
}
function CastSetter(index) {
return function (variant) {
opc_vars[index].Value = parseFloat(variant.value);
opc_vars[index].IsSet = true;
return opcua.StatusCodes.Good;
};
}
you will use an index to get and set values in the array, casting function like this will provide index to be "hard coded" in those get and set properties.
I have a problem with the following code:
// At the beginning
var prog = {}
// some attributes of prog and other methods
// ...
prog.stateChange = function(state)
{
var functionOfState =
{
onState1: function()
{
// some code
},
onState2: function()
{
// some code
}
}['on'+state]()
}
Which purpose have these square brackets after the creation of the object functionOfState? Is this an array of possible methods?
Sorry, I'm a total newbie in JS and I haven't found any information about this.
I really appreciate any help.
This code does almost the same as:
var functionOfState =
{
onState1: function()
{
// some code
},
onState2: function()
{
// some code
}
}
functionOfState['on'+state]();
It simply creates an object which stores different functions. Then, it calls one of them according to the current value of state.
Maybe, this one will be even easier:
var functionOfState = {};
functionOfState['onState1'] = function() {
// someCode
};
functionOfState['onState2'] = function() {
// someCode
};
functionOfState['on'+state](); // when state is 'State2', 'onState2' will be called
The difference that in your code it doesn't store this object with functions, but calls it directly.
This is (not the most clear) way to extract field from an object.
in JS, the subscript operator ([]) can extract a property out of an object just like the dot operator (.), so the following expressions are equal:
var obj = { field : value };
obj.field == obj["field"]; //returns true
on your example an object with the fields onState1, onState2 is created. then, using the subscript operator the correct property is extract. it is equivilant on writing
prog.stateChange = function(state)
{
var temp =
{
onState1: function()
{
// some code
},
onState2: function()
{
// some code
}
};
var functionOfState = state == onState1 ? temp.onState1 : temp.onState2;
functionOfState();
}
This is not a legit way to extract a property/method out of an object. basically if someone changes the name of the method, the code breaks. it is much better to simply use a switch case.
This question already has answers here:
passing index from for loop to ajax callback function (JavaScript)
(3 answers)
Closed 8 years ago.
I'm sure this has been asked before, but I don't know what to search for.
So I want function to be called with a string that corresponds with the item clicked, but I want to simply add any new items to an array of strings.
var menuList = ["overview", "help", "search"];
var functionCalls = [
function() { toggleMenu(menuList[0]); },
function() { toggleMenu(menuList[1]); },
function() { toggleMenu(menuList[2]); },
];
which is used like this in a loop: $("something").click(functionCalls[i])
This is what I want to do (but obviously it doesn't work):
for (var i in menuList) {
// This does not work because the closure references 'i'
// which, at the end, is always the index of the last element
$("something").click(function() {
toggleMenu(menuList[i]);
});
// this works, but I have to define each closure
$("something").click(functionCalls[i]);
}
How can I create an anonymous function that accepts a value based on a variable - but doesn't retain the reference to the variable?
You could use an IIFE like this:
for (var i=0; i<menuList.length; i++) {
!function( index ) {
$("something").click(function() {
toggleMenu( menuList[index] );
});
}( i );
}
By calling the anonymous function, you create a local copy of the current value for i with the name index. Hence, all handlers receive their respective version of i.
I create a context-menu from an array like this:
var menu1 = [
{
'OPTION1':function(menuItem,menu) {
// code for OPTION1
}
},
{
'OPTION2':function(menuItem,menu) {
// code for OPTION2
}
}
];
When the user right-clicked on my webpage, a menu appears with the options OPTION1 and OPTION2.
I need to change dynamically the function name, because it's the context-menu option text. Is there any way to declare the function name as a variable?
This is what I want:
var optionsletters = {};
optionsletters['option1'] = 'option_one';
optionsletters['option2'] = 'option_two';
var menu1 = [
{
optionsletters['option1']:function(menuItem,menu) {
// code for OPTION1
}
},
{
optionsletters['option2']:function(menuItem,menu) {
// code for OPTION2
}
}
];
EDIT#1: This is the plugin I've been using jQuery ContextMenu Plugin
EDIT#2: I need this to allow change language from spanish to english and viceversa.
You can't use the object literal notation to set arbritrary properties as you seem to try in the second example. This doesn't stop you from setting the property manualy:
function make_menu_item(name, func){
var item = {}; //Create an empty object
item[name] = func; //Assign the property with the name you choose
//(obj['option1'] is equivalent to obj.option1 in Javascript)
return item;
}
var menu = [
make_menu_item('option1', function () {...}),
make_menu_item('option2', function () {...})]
What about something like:
var name = 'option_one',
optionsletters = {};
optionsletters[name] = function() { ... };
Are you asking if you can change the key from 'OPTION1' : function() {} to 'option1' : function() {} ? If so you can just copy the function to a new variable onclick. If what you're asking is to be able to call something 'option1' as a string like option1 but actually call OPTION1(){} you can try a few different things. One you could make your array like so arr[{'name' : 'option1', 'func' : function(){} }] Then you could reference array[0].name and array[0].func.
With your current code it looks like you're blowing out your functions with strings.
JS is super expressive so there are a million ways to do anything. Functions are first-class objects so can be passed as parameters and even returned from functions.