In C you can scope a variable to a switch case, like this.
With javascript I get unexpected token using the following:
const i = 1
switch (i) {
// variables scoped to switch
var s
var x = 2342
case 0:
s = 1 + x
break
case 1:
s = 'b'
break
}
Is there another way to do this or should I just declare my variables outside the switch?
EDIT:
This is a workaround I considered but it didn't end up working. The reason being that each case has its own scope.
const i = 1
switch (i) {
case i:
// variables scoped to switch
var s
var x = 2342
case 0:
s = 1 + x
break
case 1:
s = 'b'
break
}
some alternative:
/* curly braces inside the case */
const i = 1
switch (i) {
case 0: {
let x = 2342;
let s = 1 + x;
console.log(x+' & '+s+' from inside');
} break;
case 1: {
let x = 2342;
let s = 'b';
console.log(x+' & '+s+' from inside'); /* 2342 & b from inside */
} break;
}
console.log(x+' & '+s+' from outside'); /* Uncaught ReferenceError */
or
/* curly braces outside the switch */
const i = 1
{
let x = 2342;
let s;
switch (i) {
case 0:
s = 1 + x;
break;
case 1:
s = 'b';
break;
}
console.log(x+' & '+s+' from inside'); /* 2342 & b from inside */
}
console.log(x+' & '+s+' from outside'); /* Uncaught ReferenceError */
Since var creates variables at function scope anyway, using it is pretty pointless. For this to work at a granularity below function scopes you'll have to use let and a browser/compiler which supports it, and then introduce a new block which you can scope things to (within switch it's simply invalid syntax):
if (true) {
let s;
switch (i) {
...
}
}
This scopes s to the if block, which for all intents and purposes is identical to the "switch scope" here.
If you cannot support let, you'll need to use an IIFE:
(function () {
var s;
switch (...) ...
})();
No, this is invalid syntax. A case or default statement is expected within a switch. You can check the specification here: http://www.ecma-international.org/ecma-262/5.1/#sec-12.11
You can also try entering your code in a JSLinter and see that this an error: http://jslint.com/
Expected 'case' and instead saw 'var'.
The workaround that you're considering would be the same thing as putting them outside the switch statement. Remember, var has function-level scope, not block-level scope. That means they are bound to the entire function containing the switch. You should declare them outside of the switch because that is where they are accessible.
const i = 1;
var s;
var x = 2342;
switch (i) {
case 0:
s = 1 + x;
break;
case 1:
s = 'b';
break;
default:
break;
}
console.log("s is " + s);
It should be declared outside the switch. The below might help:
var i = 1, x = 2342;
var s;
switch (i)
{
case 0:
s = 1 + x;
break;
case 1:
s = 'b';
break;
}
console.log("s is " + s);
JavaScript defines 3 levels of scope:
Global - Anything not delcared in a function
Function - Anything declared in a function using the var keyword
Block - Anything declared in a block container ({}) using let
So, to creae a scope an entire construct, you have two choices: Function or Block
In order to get the the behavior you are looking for with a function:
const i = 1
function doSwitch(data){
// variables are not scoped to switch, but are
// scoped to the function, which only contains
// the switch.
var s;
var x = 2342;
switch (data) {
case 0:
s = 1 + x;
break;
case 1:
s = 'b';
break;
default:
s = "other";
}
console.log("s is " + s)
}
doSwitch(18);
Or, in order to get the functionality with a block using let
const i = 1;
// Wrapping the switch in its own block allows for let to work:
{
// variables are not scoped to switch, but are
// scoped to the function, which only contains
// the switch.
let s;
let x = 2342;
switch (i) {
case 0:
s = 1 + x;
break;
case 1:
s = 'b';
break;
default:
s = "other";
}
console.log("s is " + s)
}
// Test:
console.log(s, x); // ReferenceError
Related
I am new to javascript and I am trying to figure out how to dynamically pass on functions with switch?
For example, I can pass on function manually like following
var select =1 ;
function add (val){ return val+512};
function subtract(val) {return val-300};
function multiply(val){return val*584};
var value = 290;
var text = `the value is ${add(value)}`
console.log(text);
How can I pass on a function with a switch based on the select value? The following does not work.
var select =1 ;
function add (val){ return val+512};
function subtract(val) {return val-300};
function multiply(val){return val*584};
var value = 290;
//switch statement
switch(select){ case 1: add(value); break; case 2: subtract(value); break; case 3: multiply(value); break};
var string = `this value is ${switch(select){ case 1: add(value); break; case 2: subtract(value); break; case 3: multiply(value); break};}`
console.log(string)
I suggest moving the switch bloc inside a function, like the following:
var select = 1;
function add(val) { return val + 512 };
function subtract(val) { return val - 300 };
function multiply(val) { return val * 584 };
var value = 290;
var string = `this string in ${chooseFunction(select, value)}`
console.log(string)
function chooseFunction (select, value) {
// switch statement
// Note that the break statement is optional
// if you return a value at the end of a case block
switch (select) {
case 1: return add(value);
case 2: return subtract(value);
case 3: return multiply(value);
}
}
You write a function that has the switch returning the result of calling functions based on the input, and use that in the template string.
function add(val) {
return val + 512;
}
function subtract(val) {
return val - 300;
}
function multiply(val) {
return val * 584;
}
function run(value, select) {
switch (select) {
case 1: return add(value);
case 2: return subtract(value);
case 3: return multiply(value);
}
}
const value = 290;
const string1 = `This string is ${run(value, 1)}`;
const string2 = `This string is ${run(value, 2)}`;
const string3 = `This string is ${run(value, 3)}`;
console.log(string1);
console.log(string2);
console.log(string3);
You cannot pass a switch directly, as you cannot pass a if statement without a ternary operator. You need to encapsulate your switch statement in a function like this :
var select =1 ;
function add (val){ return val+512};
function subtract(val) {return val-300};
function multiply(val){return val*584};
var value = 290;
const getValue = (s, v) => {
switch(s) {
case 1:
return add(v);
case 2:
return subtract(v);
case 3:
return multiply(v);
};
}
var string = `this value is ${getValue(select, value)}`
console.log(string)
An other way to do this is to put your functions into an array with the index referencing the choice :
var select =1 ;
function add (val){ return val+512};
function subtract(val) {return val-300};
function multiply(val){return val*584};
var value = 290;
const fn = [add, subtract, multiply];
var string = `this value is ${fn[select - 1](value)}`
console.log(string)
I have in the global space a variable set as follows:
let hoverMessageLock = 0;
This allows the function below to operate how I want. Its function is to add a message upon button hover and then delete said message and replace it with a new message upon another hover.
The issue I'm having is I would like to NOT use the global space for variables and instead put it inside the function itself. However the function itself is getting called over and over again per each hover event, so if I set hoverMessageLock = 0 in the function scope, it just keeps getting set to 0 over and over again. Therefor my switch statement doesn't work as intended as when its in the global scope.
I know in other programming languages that utilizes classes, you can set variables in the class scope and then alter them in the same way you would global scope inside your functions. Is there a way to do this as well here in Javascript?
let macroManager = {
hoverMessage: function(message, style = "hovertext noTextSelect"){
switch (hoverMessageLock){
case 0:
uiManager.messageDirector.addMessage(""+message+"", 0, 0, style);
hoverMessageLock = 1;
break;
case 1:
uiManager.messageDirector.removeMessage();
uiManager.messageDirector.addMessage(""+message+"", 0, 0, style);
break;
}
},
}
You already have an object, and that works nicely, just as you describe in class-based languages. Classes are simply a way of creating objects, and objects are all you need here.
Just add the property to your object:
let macroManager = {
hoverMessageLock: 0,
hoverMessage: function(message, style = "hovertext noTextSelect"){
switch (this.hoverMessageLock){
case 0:
uiManager.messageDirector.addMessage(""+message+"", 0, 0, style);
this.hoverMessageLock = 1;
break;
case 1:
uiManager.messageDirector.removeMessage();
uiManager.messageDirector.addMessage(""+message+"", 0, 0, style);
break;
}
},
}
Note that, depending on how you use the hoverMessageLock method, it's possible its this context could be lost (eg if you use it as an event handler) - this is easily fixed by defining it as an arrow function instead: hoverMessage: (message, style = "hovertext noTextSelect") => { ... }.
I would also recommend, if 0 and 1 are the only intended values of the hoverMessageLock variable/property, using boolean false/true values instead.
You can use an Immediately Executed Function Expression (IIFE) for this.
let macroManager = (function(hoverMessageLock) {
return {
hoverMessage: function(message, style = "hovertext noTextSelect") {
switch (hoverMessageLock) {
case 0:
uiManager.messageDirector.addMessage("" + message + "", 0, 0, style);
hoverMessageLock = 1;
break;
case 1:
uiManager.messageDirector.removeMessage();
uiManager.messageDirector.addMessage("" + message + "", 0, 0, style);
break;
}
},
};
})(0);
class macroManger{
constructor(){
this.hoverMessageLock = 0;
}
hoverMessage(message, style = "hovertext noTextSelect"){
switch (this.hoverMessageLock) {
case 0:
uiManager.messageDirector.addMessage("" + message + "", 0, 0, style);
this.hoverMessageLock = 1;
break;
case 1:
uiManager.messageDirector.removeMessage();
uiManager.messageDirector.addMessage("" + message + "", 0, 0, style);
break;
}
}
}
macroManger1 = new macroManger();
macroManger1.hoverMessage(...);
You could create a local scope using the IIFE expression:
var macroManager = getMacroManager();
function getMacroManager() {
return (function () {
var hoverMessageLock = 0;
return {
hoverMessage: function (message, style = "hovertext noTextSelect") {
switch (hoverMessageLock) {
case 0:
uiManager.messageDirector.addMessage("" + message + "", 0, 0, style);
hoverMessageLock = 1;
break;
case 1:
uiManager.messageDirector.removeMessage();
uiManager.messageDirector.addMessage("" + message + "", 0, 0, style);
break;
}
},
};
})();
}
So each time you call getMacroManager() you will get a separate instance, thus, hoverMessageLock will work as a local variable, which is what you expect to happen.
Here's more info about IIFE:
https://developer.mozilla.org/en-US/docs/Glossary/IIFE.
I'm relatively new to coding and working on a text based RPG game in JavaScript. The following code allows me to progress through the different scenarios where you are approached with a different bad guy.
I used a For loop in conjunction with a Switch statement and had it working prior, but then I re factored my code to make it more OO & prototypal. Now my For loop continues looping and does not exit. I checked the value of [i] throughout and see it properly goes 0-4, but then it restarts at 0 and I can't figure out why?
var scenario = new Array();
//simple function to create the number of scenarios
function Scenario () {
howManyScenarios = function(number) {
for (i=0; i <= number; i++) {
scenario[i] = ("Scenario " + (1 + i));
};
};
howManyScenarios(4); //if you change the argument, add additional switch cases
//iterating through my howManyScenarios function to build out scenarios using a switch case
createScenarios = function () {
var ii = scenario.length;
for (i=0; i < ii; i++) {
switch(scenario[i]) {
case 'Scenario 1':
alert("You run into a troll");
b = 0;
break;
case 'Scenario 2':
alert("You find a store to purchase goods from");
ItemShop();
break;
case 'Scenario 3':
alert("You run into a ogre");
b = 1;
break;
case 'Scenario 4':
alert("You run into a warewolf");
b = 2;
break;
case 'Scenario 5':
alert("You run into a wizard");
b = 3;
return;
break;
}; //close out switch cases
}; //close out my for loop
}; //close out createScenarios function
createScenarios();
}; //close out Scenario function
Scenario();
Your loop will obviously still continue because you just only ended a case of every loop of i and will still test each value in the array of scenario[i].
How about using variable b as a handler that if an event like you run into a troll has been executed, then set b to a number greater than 0 then check if a value has been inserted into b before switching into the array again using if (b) break; where if b has a value greater than 0 then it will be set as true.
var scenario = new Array();
var b;
//simple function to create the number of scenarios
function Scenario() {
howManyScenarios = function (number) {
for (i = 0; i <= number; i++) {
scenario[i] = ("Scenario " + (1 + i));
};
};
howManyScenarios(4); //if you change the argument, add additional switch cases
console.log(scenario[i]);
//iterating through my howManyScenarios function to build out scenarios using a switch case
createScenarios = function () {
var ii = scenario.length;
for (i = 0; i < ii; i++) {
if (b) break;
switch (scenario[i]) {
case 'Scenario 1':
alert("You run into a troll");
b = 1;
break;
case 'Scenario 2':
alert("You find a store to purchase goods from");
b = 2;
ItemShop();
break;
case 'Scenario 3':
alert("You run into a ogre");
b = 3;
break;
case 'Scenario 4':
alert("You run into a warewolf");
b = 4;
break;
case 'Scenario 5':
alert("You run into a wizard");
b = 5;
return;
break;
}; //close out switch cases
}; //close out my for loop
}; //close out createScenarios function
createScenarios();
}; //close out Scenario function
Scenario();
function ItemShop() {}
ANSWER 2
This one is one way on how we game developers make a functional game by using a series of object arrays, object classes and the like.
I remade your code into something easier to read, hope you learn something from this. :)
var numberofscenarios = 5;
var scenario = []; //array where scenarios will be
//this will be the accessible properties of scenario[] array
var _scenario = function(){
this.name = ""; //name of scenario
this.message = "";
this.doSomething = 0;
this.status = 0 ;//1 = finished and 0 = false
};
var _event = function(mobname){
this.mobname = mobname;
this.battle = function(){//doSomething
console.log("Battle VS "+ this.mobname +" Start!");
};
this.itemShop = function(){//doSomething
console.log(this.mobname + ": Welcome to the shop! How may I help you?");
};
};
//generate the scenarios in the scenario[] array
function generateScenarios() {
for (i = 0; i <= numberofscenarios; i++) {
scenario[i] = new _scenario();
scenario[i].name = i;
switch (scenario[i].name) {
case 1:
scenario[i].message = "You run into a Troll";
scenario[i].doSomething = new _event("Troll");
break;
case 2:
scenario[i].message = "You find a store to purchase goods from";
scenario[i].doSomething = new _event("Shop Keeper");
break;
case 3:
scenario[i].message = "You run into a Ogre";
scenario[i].doSomething = new _event("Ogre");
break;
case 4:
scenario[i].message = "You run into a Werewolf";
scenario[i].doSomething = new _event("Werewolf");
break;
case 5:
scenario[i].message = "You run into a Wizard";
scenario[i].doSomething = new _event("Wizard");
break;
}
}
}
generateScenarios(); //generate the scenarios
//test the array of scenario class
//test the battle with Troll
console.log(scenario[1].message);
scenario[1].doSomething.battle();
//test the shop
console.log(scenario[2].message);
scenario[2].doSomething.itemShop();
//attempt to fight the Shopkeeper
console.log(scenario[2].message);
scenario[2].doSomething.battle();
Does anyone know what typo I made, because my editor, and I can't seem to make it work
number=5;
switch (number) {
case 5:
box.onclick = function1;
break;
case 50:
box.onclick = function2;
break;
}
I've also tried doing it in switch.
Try not using the reserved word var as a variable name:
var x=5;
if (x==5){
box.onclick = function1;
}
if(x==50){
box.onclick = function2;
}
var is the reserved word to create variables. You can't use it as a name. This is the correct syntax:
var x = 5;
if (x == 5) {
box.onclick = function1;
}
if (x == 50) {
box.onclick = function2;
}
With this code:
number=5;
switch (number) {
case 5:
box.onclick = function1;
break;
case 50:
box.onclick = function2;
break;
}
case 50 will never be hit because you set number to 5 before entering the switch block. Therefore box onclick will never equal function2. Therefore, when you click the box, function2 will not be run. Is this really an accurate representation of your actual code, or is it a simplification that has left out important information?
You can't use reserved JavaScript words for declaring variables.
Try to replace var=5 to myVar=5 for example.
var myVar = 5;
if (myVar ==5){
box.onclick = function1;
}
if(myVar ==50){
box.onclick = function2;
}
Also, check out this reference: JavaScript Reserved Words
Let's say you have the following function
var action = (function () {
var a = 42;
var b = 2;
function action(c) {
return a + 4 * b + c;
}
return action;
}());
// how would you parse action into it's serialized LISP / AST format?
var parsed = parse(action);
Is it possible to have a function that takes a reference to the function action and outputs say the LISP format (lambda (c) (plus (plus 42 (multiply 4 2)) c))
We're allowed to put some restrictions on what action can be.
the body should only be a single expression
it should be a pure function
any free variables are constants
The main question is given a function you can invoke with a range of inputs and it's source code can you discover the correct value to substitute the free variables with?
For the above example you know that a and b are constant and you could intellectually plot the output for a few values and see the pattern and then just know what the constants are.
Question:
How would you write a function that takes a function reference and it's source code and produces some form of AST for the function with any free variables substituted for their run-time values.
An example of an AST format would be the LISP equivalent of the code.
I basically want to serialize and deserialize the function and have it behave the same
It should be noted that the problem becomes trivial if you pass { a: a, b: b } to the analysis function. That would be cheating.
Use-case:
I want to generate a language agnostic form of a pure JavaScript function so I can effectively pass it to C++ without requiring the user of my library to use a DSL to create this function
Let's imagine you had a database driver
var cursor = db.table("my-table").map(function (row) {
return ["foo", row.foo]
})
You want to determine at run-time what the function is and convert it into an AST format so that you can use your efficient query builder to convert it into SQL or whatever query engine your database has.
This means you don't have to write:
var cursor = db.table("my-table").map(function (rowQueryObject) {
return db.createArray(db.StringConstant("foo"), rowQueryObject.getProperty("foo"))
})
Which is a function the DB library can execute with a query object and have you build the query object transformation without verbose methods.
Here is a full solution (using catalog of variables which is accessible by the parse function):
var CONSTANTS = {
a: 42,
b: 2,
c: 4
};
function test() {
return a + 4 * b + c;
}
function getReturnStatement(func) {
var funcStr = func.toString();
return (/return\s+(.*?);/g).exec(funcStr)[1];
}
function replaceVariables(expr) {
var current = '';
for (var i = 0; i < expr.length; i += 1) {
while (/[a-zA-Z_$]/.test(expr[i]) && i < expr.length) {
current += expr[i];
i += 1;
}
if (isNumber(CONSTANTS[current])) {
expr = expr.replace(current, CONSTANTS[current]);
}
current = '';
}
return expr;
}
function isNumber(arg) {
return !isNaN(parseInt(arg, 10));
}
function tokenize(expr) {
var tokens = [];
for (var i = 0; i < expr.length; i += 1) {
if (isWhitespace(expr[i])) {
continue;
} else if (isOperator(expr[i])) {
tokens.push({
type: 'operator',
value: expr[i]
});
} else if (isParentheses(expr[i])) {
tokens.push({
type: 'parant',
value: expr[i]
});
} else {
var num = '';
while (isNumber(expr[i]) && i < expr.length) {
num += expr[i];
i += 1;
}
i -= 1;
tokens.push({
type: 'number',
value: parseInt(num, 10)
});
}
}
return tokens;
}
function toPrefix(tokens) {
var operandStack = [],
operatorStack = [],
current,
top = function (stack) {
if (stack) {
return stack[stack.length - 1];
}
return undefined;
};
while (tokens.length) {
current = tokens.pop();
if (current.type === 'number') {
operandStack.push(current);
} else if (current.value === '(' ||
!operatorStack.length ||
(getPrecendence(current.value) >
getPrecendence(top(operatorStack).value))) {
operatorStack.push(current);
} else if (current.value === ')') {
while (top(operatorStack).value !== '(') {
var tempOperator = operatorStack.pop(),
right = operandStack.pop(),
left = operandStack.pop();
operandStack.push(tempOperator, left, right);
}
operatorStack.pop();
} else if (getPrecendence(current.value) <=
getPrecendence(top(operatorStack).value)) {
while (operatorStack.length &&
getPrecendence(current.value) <=
getPrecendence(top(operatorStack).value)) {
tempOperator = operatorStack.pop();
right = operandStack.pop();
left = operandStack.pop();
operandStack.push(tempOperator, left, right);
}
}
}
while (operatorStack.length) {
tempOperator = operatorStack.pop();
right = operandStack.pop();
left = operandStack.pop();
operandStack.push(tempOperator, left, right);
}
return operandStack;
}
function isWhitespace(arg) {
return (/^\s$/).test(arg);
}
function isOperator(arg) {
return (/^[*+\/-]$/).test(arg);
}
function isParentheses(arg) {
return (/^[)(]$/).test(arg);
}
function getPrecendence(operator) {
console.log(operator);
switch (operator) {
case '*':
return 4;
case '/':
return 4;
case '+':
return 2;
case '-':
return 2;
default:
return undefined;
}
}
function getLispString(tokens) {
var result = '';
tokens.forEach(function (e) {
if (e)
switch (e.type) {
case 'number':
result += e.value;
break;
case 'parant':
result += e.value;
break;
case 'operator':
result += getOperator(e.value);
break;
default:
break;
}
result += ' ';
});
return result;
}
function getOperator(operator) {
switch (operator) {
case '+':
return 'plus';
case '*':
return 'multiplicate';
case '-':
return 'minus';
case '\\':
return 'divide';
default:
return undefined;
}
}
var res = getReturnStatement(test);
console.log(res);
res = replaceVariables(res);
console.log(res);
var tokens = tokenize(res);
console.log(tokens);
var prefix = toPrefix(tokens);
console.log(prefix);
console.log(getLispString(prefix));
I just wrote it so there might be some problems in the style but I think that the idea is clear.
You can get the function body by using the .toString method. After that you can use regular expression to match the return statement
(/return\s+(.*?);/g).exec(funcStr)[1];
Note that here you must use semicolons for successful match! In the next step all variables are transformed to number values using the CONSTANTS object (I see that you have some parameters left so you may need little modifications here). After that the string is being tokenized, for easier parsing. In next step the infix expression is transformed into a prefix one. At the last step I build a string which will make the output looks like what you need (+ - plus, - - minus and so on).
Since I'm not sure you're able to get the method's body after having invoked it, here is an alternative solution:
var a = 42;
var b = 2;
function action(c) {
return a + 4 * b + c;
}
/**
* get the given func body
* after having replaced any available var from the given scope
* by its *real* value
*/
function getFunctionBody(func, scope) {
// get the method body
var body = func.toString().replace(/^.*?{\s*((.|[\r\n])*?)\s*}.*?$/igm, "$1");
var matches = body.match(/[a-z][a-z0-9]*/igm);
// for each potential var
for(var i=0; i<matches.length; i++) {
var potentialVar = matches[i];
var scopedValue = scope[potentialVar];
// if the given scope has the var defined
if(typeof scopedValue !== "undefined") {
// add "..." for strings
if(typeof scopedValue === "string") {
scopedValue = '"' + scopedValue + '"';
}
// replace the var by its scoped value
var regex = new RegExp("([^a-z0-9]+|^)" + potentialVar + "([^a-z0-9]+|$)", "igm");
var replacement = "$1" + scopedValue + "$2";
body = body.replace(regex, replacement);
}
}
return body;
}
// calling
var actionBody = getFunctionBody(action, this);
// log
alert(actionBody);
Prints:
return 42 + 4 * 2 + c;
DEMO
You would then have to implement your own function toLISP(body) or any function else you may need.
Note that it won't work for complex scoped variables such as var a = {foo: "bar"}.