JavaScript dash throws off ReferenceError - javascript

The following JavaScript...
if (eval('typeof admin_post_css_theme_dark-moon)=='function')) {/**/}
...triggers the following error message...
Error: ReferenceError: moon is not defined
The only thing I can really differentiate in this situation is that other themes don't have a dash in their names...
if (eval('typeof admin_post_css_theme_silver)=='function')) {/**/}
...doesn't trigger any errors.
So how is the dash between 'dark' and 'moon' triggering this error?
Edit: I wanted to take a moment and recommend that others who encounter this should adapt camelCase or something similar. In general I use a 'name' and a 'base' myself. The 'base' is the URL-friendly version of something that the 'name' includes URL unfriendly characters. In example 'My Example' and 'my-example' or 'my_example'.
http://en.wikipedia.org/wiki/CamelCase

JavaScript variable names can't contain dashes, object properties however can. For instance, something like this would work:
var themes = {
'admin_post_css_theme_dark-moon': function () {},
'admin_post_css_theme_silver': function () {}
};
if (typeof themes['admin_post_css_theme_dark-moon'] === 'function') {
/**/
}

Primarily because '-' is not a valid identifier in javascript, its the minus sign. Your eval in essence is trying to get the typeof the expression admin_post_css_theme_dark minus moon. Valid identifiers (i.e. variable, function or object names) in javascript are [A-Za-z0-9_$] but cannot start with a number (note this is a regex, and the hyphens in this context mean range i.e. a to z, just in case it was unclear)
My evolution to the question would be how would you have expected admin_post_css_theme_dark-moon to be defined, as you are expected it to be somehow/where in code, then in turn to test if it is a function.
As it would be absolutely impossible to do this
var admin_post_css_theme_dark-moon = function(){...};
//or
admin_post_css_theme_dark-moon = function(){...};
however it is possible to do this.
window['admin_post_css_theme_dark-moon'] = function(){...};
or preferably use your own object
var Themes = {};
Themes['admin_post_css_theme_dark-moon'] = function(){...};
//or
var Themes = {
'admin_post_css_theme_dark-moon' : function(){...};
}
As object properties if referenced by string index (i.e. between [] as a string) are not bound by the identifier rules.
then of course your eval would have to change also
something like
if (eval("typeof window['admin_post_css_theme_dark-moon']")=='function')) {/**/}
//or
if (eval("typeof Themes['admin_post_css_theme_dark-moon']")=='function')) {/**/}
NOTE the use of alternating " and ' so you don't have to escape

I changed...
eval('typeof '+my_object)=='function')
...to...
eval('typeof \''+my_object+'\'')=='function')
I didn't have the chance to edit this in so the question could be more concise. For those looking at just this answer check out the other answers from OJay and sabof as they are wholly relevant.

Related

Object definition in old Javascript

I came accross a following piece of code and i got confused and unable to understand the syntax used.
I would need help to break some hightlighted
let HT = HT || {}; //What is this syntax and what does it do ?
//Are we adding an Hexagon object to the object HT ?
HT.Hexagon = function(id, x, y) {
}
//Are we adding an object named Orientation to the object Hexagon ?
HT.Hexagon.Orientation = {
Normal: 0,
Rotated: 1
};
//Are we adding an object named Static to the object Hexagon ?
HT.Hexagon.Static = {
HEIGHT:91.14378277661477,
WIDTH:91.14378277661477,
SIDE:50.0,
ORIENTATION:HT.Hexagon.Orientation.Normal,
DRAWSTATS: false
};
How can one convert this piece of code to the modern ES6 classes ?
The first line code is using the or operator.
The operator checks first the expression on its left side - in this case the HT (it's probably an existing object.) If the expression is true e.g. holds a value like an object a number or a string etc, the expression will be assigned to the variable.
If HT does not hold any value which means it is false. Than it assign's the expression on its righte side - in this case an empty object.
The answer for your other three question is yes.
And it's not old JS at all.

Sanitizing `eval` to prevent it from changing any values

This is front-end only, and not back-end. I also acknowledge that this is a bad idea. At this point I'm just curious.
I have a table of records. I would like the user to be able to enter a JavaScript conditional statement, which is then applied to the table to filter the records.
For example, to filter out records with a name that's less than 6 characters, I might enter:
record.name.length < 6
Without using an external library, the easiest way I've found to do this is with eval. However, in using eval, I of course introduce the risk of the user breaking the code (not a huge concern since this is front-end only, but still a user experience issue).
I would like to sanitize the user input so that it cannot change any values. So far, I believe I only need to do these two things to make eval "safe":
Turn any single equals signs = into double or triple equals signs
Remove or escape parentheses ( )
With these two items taken care of, is there anything else I need to do to prevent the user input from changing values?
One way of doing this which is safer than eval is using the Function constructor. As far as I know, this answer is totally safe, but it's quite possible there's some caveat I don't know or have forgotten, so everyone feel free to reply if I'm wrong.
The Function constructor allows you to construct a function from its string and a list of argument names. For example, the function
function(x, y) {
return x + y;
}
could be written as
new Function('x', 'y', 'return x + y;')
or simply
Function('x', 'y', 'return x + y;')
Note that although the function body has access to variables declared in the function definition, it cannot access variables from the local scope where the Function constructor was called; in this respect it is safer than eval.
The exception is global variables; these are accessible to the function body. Perhaps you want some of them to be accessible; for many of them, you probably don't. However, there is a way round this: declare the names of globals as arguments to the function, then call the function overriding them with fake values. For example, note that this expression returns the global Object:
(function() { return Object; })()
but this one returns 'not Object':
(function(Object) { return Object; })('not Object')
So, to create a function which does not have access to any of the globals, all you have to do is call the Function constructor on the javascript string, with arguments named after all the globals, then call the function with some innocuous value for all the globals.
Of course, there are variables (such as record) which you do want the javascript code to be able to access. The argument-name arguments to Function can be used for this too. I'll assume you have an object called myArguments which contains them, for example:
var myArguments = {
record: record
};
(Incidentally, don't call it arguments because that's a reserved word.) Now we need the list of names of arguments to the function. There are two kinds: arguments from myArguments, and globals we want to overwrite. Conveniently, in client-side javascript, all global variables are properties in a single object, window. I believe it's sufficient to use its own properties, without prototype properties.
var myArgumentNames = Object.keys(myArguments);
var globalNames = Object.keys(window);
var allArgumentNames = myArgumentNames.concat(globalNames);
Next we want the values of the arguments:
var myArgumentValues = myArgumentNames.map(function(key) {
return myArguments[key];
};
We don't need to do the values part for the globals; if we don't they'll just all be set to undefined. (Oh, and don't do Object.keys(myArguments).map(...), because there's a (small) chance that the array will come out in the wrong order, because Object.keys doesn't make any guarantees about the order of its return value. You have to use the same array, myArgumentNames.) Then call the Function constructor. Because of the large number of arguments to Function it's not practical to list them all explicitly, but we can get round this using the apply method on functions:
var myFn = Function.apply(null, allArgumentNames.concat([jsString]))
and now we just call this function with the argument list we've generated, again using the apply method. For this part, bear in mind that the jsString may contain references to this; we want to make sure this doesn't help the user to do something malicious. The value of this inside the script is the first argument to apply. Actually that's not quite true - if jsString doesn't use strict mode, then trying to set this to undefined or null will fail, and this will be the global object. You can get round this by forcing the script into strict mode (using '"use strict";\n' + jsString), or alternatively just set this to an empty object. Like this:
myFn.apply({}, myArgumentValues)
I am sharing my implementation (based on #David's answer).
Some of the keys of the Window object might break the Function.apply. This is why I've filtered the ones that break. Explanations in the code below as a comment.
// Why is windowKeys not inside function scope? No need. It won't
// be changing on each call. Creating array with +270 items for each eval
// might effect performance.
const windowKeys = Object.keys(window).filter((key) => {
// Why is window filtered?
// There are some cases that parameters given here might break the Function.apply.
// Eg. window keys as numbers: '0', (if there is iframe in the page)
// the ones that starts with numbers '0asdf',
// the ones that has dash and special characters etc.
try {
Function.apply(null, [key, "return;"]);
return true;
} catch (e) {
return false;
}
});
/**
* evaluates
* #param {string} code
* #param {object} context
* #returns
*/
const safeEval = (code, context) => {
const keys = Object.keys(context);
const allParams = keys.concat(windowKeys, [`"use strict"; return ${code}`]);
try {
const fn = Function.apply(null, allParams);
const params = keys.map((key) => context[key]);
return fn(...params);
} catch (e) {
console.log(e);
}
};
// simple expression evaluation
const res = safeEval("a + b", { a: 1, b: 2 });
console.log(res);
// try to access window
const res1 = safeEval("{a, b, window, document, this: this}", { a: 1, b: 2 });
console.log(res1);
Idk. if this approach can be exploited, if it does. I think another approach can be running eval on cross-domain iframe and get the result with window messages.

JavaScript : Expected and assignment or function call and instead saw an expression

I am using JSHint to ensure my JavaScript is "strict" and I'm getting the following error:
Expected an assignment or function call and instead saw an expression
On the following code:
var str = 'A=B|C=D'
var data = {};
var strArr = str.split( '|' );
for (var i = 0; i < strArr.length; i++) {
var a = strArr[i].split('=');
a[1] && (data[a[0].toLowerCase()] = a[1]); // Warning from JSHint
}
Any ideas why I'm getting such an error or how I can code to remove the error.
Here is a simplified version that gives the same warning:
var a, b;
a && (b = a);
Expected an assignment or function call and instead saw an expression
This means that you have an expression but do not assign the result to any variable. jshint doesn't care about what the actual expression is or that there are side effects. Even though you assign something inside of the expression, you are still ignoring the result of the expression.
There is another error by jslint if you care about it:
Unexpected assignment expression
This warns you that you may want to use == instead of = inside logical expressions. It's a common error, therefore you are discouraged to use assignments in logical expressions (even though it is exactly what you want here).
Basically, jshint/jslint do not like misuse of shortcut evaluation of logical operator as replacement for if statements. It assumes that if the result of an expression is not used, it probably shouldn't be an expression.
http://jshint.com/docs/options/#expr - JSHINT says, Expr warnings are part of relaxing options. So, if you write /* jshint expr: true */, it won't give you the warning. But, you have to know the scope of function too. If you just type this line on top of everything, it will apply this rule globally. So, even if you made a mistake on the other lines, jshint will ignore it. So, make sure you use this wisely. Try to use if for particular function ( i mean inside one function only)

How does this JavaScript augmented method work?

Similar to this question, I am following Douglas Crockford's JavaScript, The Good Parts. In chapter 4, he talks about augmenting types, which I find very confusing. He writes this sample code:
Function.prototype.method = function (name, func) {
this.prototype[name] = func;
return this;
};
Number.method('integer', function ( ) {
return Math[this < 0 ? 'ceil' : 'floor'](this);
});
He then tests the new integer method:
document.writeln((-10 / 3).integer()); // -3
I don't understand the syntax of this.prototype[name] (in particular, the brackets) or Math[this < 0 ? 'ceiling' : 'floor'](this) (again, the brackets and also where Math came from). More importantly, can someone explain how the code in general works, and why the test code works?
By extending Function.prototype you add a method available to every function. Remember that functions in JavaScript are objects, and as such they can have properties and methods eg. call or apply.
The method function method lets you add a method to the prototype of any given function. Number is a function; the constructor of numbers, which has a prototype. Functions that you add to the prototype are available for all instances of the object, in this case a number.
Math is a built-in object in JavaScript, by using bracket notation you can dynamically access the property of an object. Remember that object keys are just strings, even if you don't write them as string, they can still be accessed with bracket notation with a string, for example:
var obj = {
key: 'hello'
};
var k = 'key';
obj[k]; //=> 'hello'
In the case of Math it's simply deciding if it should use ceil (not "ceiling") or floor based on a condition, you could write like this:
if (this < 0) {
Math.ceil(this);
} else {
Math.floor(this);
}
Javascript is weird.
In most languages you do foo = someObject.someMember and JS allows that too.
But it also lets you do foo = someObject["someMember"] which seems silly... until you realize that the string could be replaced with a variable
var memberName="someMember"
foo = someObject[memberName]
Now you're able to write very dynamic code that can leverage object members without knowing the names of those members when you write the code. This is what the code you posted is doing. It feels dirty at first but that feeling will pass ;)

Is it possible to call a method from an object using a string?

Is it possible to call a method from an object using a string?
var elem = $('#test'); //<div id="test"></div>
var str = "attr('id')";
//This is what I'm trying to achieve
elem.attr('id'); //test
//What I've tried so far
elem.str; //undefined
elem.str(); //Object [object Object] has no method 'str'
var fn = eval(str); //attr is not defined
eval(elem.toString()+'.'+str); //Unexpected identifier
//Only solution I've found so far,
//but is not an option for me
//because this code is in a function
//so the element and method call
//get passed in and I wouldn't know
//what they are
eval($('#test').attr('id')); //test
UPDATE
This is my final, working answer:After running this code in the console
theMethod = 'attr("id","foo")'.match(/^([^(]+)\(([^)]*)\)/);
jQuery('#post-form')[theMethod[1]].apply(jQuery('#post-form'),JSON.parse('['+theMethod[2]+']'));
The post-form element now has a new ID, no problems at all. This works for methods that take multiple arguments, a single argument or no arguments at all. Recap:
theMethod = theInString.match(/^\.?([^(]+)\(([^)]*)\)/);
//added \.? to trim leading dot
//made match in between brackets non-greedy
//dropped the $ flag at the end, to avoid issues with trailing white-space after )
elem[theMethod[1]].apply(elem,JSON.parse('['+theMethod+']'));
That's the safest, most reliable approach I can think of, really
What ever you do DON'T USE EVAL:
var theMethod = 'attr(\'id\')';
//break it down:
theMethod = theMethod.match(/^([^(]+)\(.*?([^)'"]+).*\)$/);
//returns ["attr('id')", "attr", "id"]
elem[theMethod[1]](theMethod[2]);//calls the method
It's the same basic principle as you'd use with any objects (remember that functions are objects all on their own in JS - and jQuery objects are, well, objects, too). This means that methods can be accessed in the exact same way as properties can:
$('#foo').attr('id') === $('#foo')['attr']('id');
So just break the string apart, and use the method name like you would an object property and you're all set to go.
Just remember: When all you have is the eval hammer, everything looks like your thumb.
Brendan Eich
If there is a chance of multiple arguments being passed to whatever method, you can sort of work your way around that, too (I think - well: logic dictates, but it's rather late and logic is getting beat up by Gin pretty bad now):
theMethod = theMethod.match(/^([^(]+)\(([^)]+)\)$/);
//["attr('id','foo')", "attr", "'id','foo'"] --> regex must now match quotes, too
elem.theMethod[1].apply(elem,JSON.parse('['+theMethod[2]+']'));
This applies the method of whatever element/object you're dealing with to itself, thus not changing the caller context (this will still point to the object within the method) and it passes an array of arguments that will be passed to the called method.
You should use one of these methods:
apply
var result = function.apply(thisArg[, argsArray]);
call
var result = fun.call(thisArg[, arg1[, arg2[, ...]]]);
Here is the sample:
var Sample = function() {
var that = this;
this.sampleMethod = function() {
return alert("Hello!");
};
this.sampleMethod2 = function(){
that["sampleMethod"].apply(that);
};
};
var objImpl = new Sample();
objImpl.sampleMethod2(); //you will get a message from 'sampleMethod()'
Eval does what you want to do. Eval is evil, however, because you should not do what you want to do.
Why is using the JavaScript eval function a bad idea?

Categories

Resources