Returning a non-value property - javascript

In javascript, it is possible to define an object's property as a getter/setter rather than just a "plain" value:
Object.defineProperty( obj, 'complex_property', {
get: function getter() { return this.protected; },
set: function setter( value ) { this.protected = value; }
} );
Is there any way to return a non-value property of an object without first having its getter function evaluated so that this (or the like) is possible?
obj.property = function( name ) { return this.[ name ]; };
// Doesn't work:
// ReferenceError: invalid assignment left-hand side
obj.property( 'complex_property' ) = 5;
The last line effectively reads 6 = 5 - obviously an error - since this.complex_property is evaluated first, then returned. Combining closures with getter-setter properties is an effective way to simulate "private" properties and validate assignment values without having to use actual get/set functions: one of the nicer features of modern javascript. It'd be even nicer if there was a way to return an unevaluated getter/setter property: is there a way I've missed or is it just not possible? Are we stuck using set( prop, value ) functions?

Unfortunately, this isn't possible with JavaScript functions. All JavaScript functions created by user code can only return values, not references (and this won't be possible by code within the browser too, starting from ES6).
A ReturnStatement is evaluated as follows (bold my emphasis):
If the Expression is not present, return (return, undefined, empty).
Let exprRef be the result of evaluating Expression.
Return (return, GetValue(exprRef), empty).
The only way to do so is either using a set(property, value) function or creating an actual setter.

You can get a property. Well, a property descriptor at least. And it ain't pretty.
var prop = Object.getOwnPropertyDescriptor(obj /* from question */, 'complex_property');
prop.set.call(obj, 5);
edit:
obj.property = function (name) { return Object.getOwnPropertyDescriptor(this, name); }
obj.property('complex_property').set.call(obj, 5);

Related

JavaScript Object and Primitive

Please I want someone to explain the code below for me:
var f = new Number(44);
f.name = "Yusuf";
f.hello = function() {
console.log("Hello");
};
console.log(typeof f);
f.hello();
console.log(f.name);
console.log(f.toString() + "good");
console.log(Object.prototype.hasOwnProperty(name));
console.log(f.hasOwnProperty(hello));
console.log(f.length);
When I check the variable type. Object gets return and I am sure this is because of the Number object constructor call function. I added two properties, one a member and a method and when I call them , it work but when I used hasOwnProperty(), false is return for the member key and undefined for the method key.
Why is it so?
where are the methods going to if the hasOwnProperty doesn't work as usual when it is supposed to when I am actually checking the property on the containing object.?
I checked Number and object object and they all return false.
The hasOwnProperty method takes the property key as a string:
console.log(Number.prototype.hasOwnProperty("name"));
console.log(Object.prototype.hasOwnProperty.call(f, "name"));
console.log(f.hasOwnProperty("name"));
console.log(f.hasOwnProperty("hello"));
I recommend to always "use strict" mode so that you get exceptions when you try to use undeclared variables.

How do I return a specific value to any property or function calls for an object?

I want to calculate the properties of an object based on a string.
But these property definitions might reference another object called 'actor'.
If this actor exists (which is not always the case) my code works, if the actor does not exist (is undefined) I want the evaluation to return NaN for actor-dependant properties.
Example:
The property definition string might look like:
"name = 'pant leg insurance';
cost = 10;
useful = actor.hasPants();
max = actor._legs;"
These variables are defined before the definition string is run through
eval(propertyString);
If the actor exists everything is fine, but if the actor does not exist I want every actor-dependant value (in this case 'useful' and 'max') to return NaN.
I already researched a bit and found the Proxy object, but this only helps if I want to access a direct property of the actor, neither with functions nor with properties which are properties of properties [...] of the actor.
var actor;
if (actor === undefined) {
actor = new Proxy({}, {
get: function(target, property) {
return NaN;
}
});
}
console.log(actor.anything);
console.log(actor.anyfunction());
console.log(actor.anything.anything);
console.log(actor.anything.anyfunction());
How do I achieve that this snippet would return NaN for any property or function calls on actor?
No, proxies cannot achieve this. When a property is accessed, you don't know whether it will be used directly in the assignment or will be called as a method, so you can't decide whether to return NaN or a function. There is no callable NaN value.
Instead, you will need to process your property definition string (you probably want to anyway, as assigning to undeclared variables is evil) and test each line for usage of actor, and when it does apply your if-not-exists-then-NaN logic.

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: silently fail on undefined assignment

Suppose I am trying to assign a variable in a way that causes an exception
I am going to access a non-existent key of a dictionary:
myObject.property = dictionary['NO_KEY'][0];
Now, because 'NO_KEY' does not exist on dictionary, my program will catch an exception when trying to subscript 0 of undefined - and crash. Is it possible to execute this line above as a no-op so that my script can continue running? I know there is try-catch syntex, but is there a more elegant syntax with ESMA6?
You can use an if condition and statement, Object.hasOwnProperty() or as suggested by #Ryan in operator
if (dictionary.hasOwnProperty("NO_KEY")) {
myObject.property = dictionary["NO_KEY"][0];
}
if ("NO_KEY" in dictionary) {
myObject.property = dictionary["NO_KEY"][0];
}
Object.defineProperty(Object.prototype,
'accessWithSilentFail', {
configurable: false,
enumerable: false,
writable: false,
value: function(key) {
return this[key] ? this[key] : {};
}});
myObject.property = dictionary
.accessWithSilentFail('NO_KEY')
.accessWithSilentFail(0);
That way you get an empty object if at any point the chain fails. You need to get an object so the chain doesn't fail halfway. You can call the function something shorter if you're going to use it a lot.
Although this works, it has many, many limitations, and it changes the Object prototype, which is usually frowned upon. You really should consider just checking for undefined, which is the idiomatic way to do it.
If you ever need to check if the access chain failed, you can use:
function chainFailed(result) {
return Object.keys(result).length === 0;
}
So you could do
myObject.property = dictionary
.accessWithSilentFail('NO_KEY')
.accessWithSilentFail(0);
if (!chainFailed(myObject.property)) {
//keep on
} else {
//handle failure
}
This works as long as your expected return isn't an empty object, on which case chainFailed will always return true. But I'm assuming you really want to fail silently, because if you wanted to handle errors you could just use an exception.
Use ternary operator
myObject.property = dictionary['NO_KEY'] ? dictionary['NO_KEY'][0] : null;
While I believe this is a bad idea that will come back to bite you later, here is a solution for modern browsers using proxies. Yes, you are still checking for the property existence, but it is hidden from your code accessing the dictionary keys.
var dictionary = {a: 42};
dictionary = new Proxy(dictionary, {
get: (target, property) => {
if (target.hasOwnProperty(property)) {
return target[property];
}
return {};
}
});
// Existing properties are passed through unchanged
console.log(dictionary.a);
// Missing properties result in an empty object
console.log(dictionary.b);
// Original test
var lost = dictionary['NO_KEY'][0];
console.log(lost);

Is it not possible to set arguments that have not been provided, using arguments property

I was testing things. And discovered that i could not set arguments if i have not provided it.
Using arguments[1] etc.
Example:
function abc (a,b) {
arguments[1] = 'new value';
console.log(b);
}
abc('a');
It won't work.
I know i could set value like if (b=='undefined') b='default'; but why i can't like this. In other words this behavior is unexpected, isn't it?
On the other hand, if you do provide argument it will get changed!
calling function like this will output new value
abc('a','b');
is there a solution, if you wanted to set value using arguments[2] and pass argument when calling function.
after more testing it seems: b doesnt get connected with arguments[1] if not called.
My understanding is argument is dynamic whose length is set by number of arguments / parameters provided to the function.
In first case, you have provided only one parameter so size of argument is 1 and hence argument[1] should be out of bound, while in second case, you have argument of length 2 so you can change either parameter value by using its index.
After posting a terribly wrong answer, I took a look at the ECMA Spec and here is in code, what JavaScript does when the arguments object will be created:
function abc () {
var names = ["a", "b"]; //as normally provided in the function declaration: function (a, b)
var a, b;
var args = {};
var mappedNames = [];
args.length = arguments.length;
var index = args.length - 1;
//this is the crucial part. This loop adds properties to the arguments object
//and creates getter and setter functions to bind the arguments properties to the local variables
//BUT: the loop only runs for the arguments that are actually passed.
while (index >= 0) {
var val = arguments[index];
if (index < names.length) {
var name = names[index];
if (mappedNames.indexOf(name) === -1) {
mappedNames.push(name);
var g = MakeArgGetter(name);
var p = MakeArgSetter(name);
Object.defineProperty(args, index.toString(), {get : g, set : p, configurable : true});
}
}
args[index.toString()] = val;
index--;
}
function MakeArgGetter(name) {
return function zz(){
return eval(name);
};
}
function MakeArgSetter(name) {
return function tt(value) {
eval(name + " = value;");
};
}
console.log(args[0]); //ab
args[0] = "hello";
args[1] = "hello";
console.log(a); //hello
console.log(b); //undefined
}
abc('ab');
Note, that there is some more going on in the Spec and I simplyfied it here and there, but this is essentially what is happening.
What are you trying to achieve by this?
If you want to have "dynamic arguments" like abc('a) and abc('a', 'b') are both valid, but the first will set the missing argument to a default value, you either have to check for the arguments length and/or for all arguments values.
Suppose the following: abc('a', null) is given, what then? - depends on logic, can be good but can also be bad.
Given this, checking the argument vector isn't smarter but more cryptic and having something like if (b=='undefined') b='default' much straighter and better to understand.
TL;DR: Not possible per ECMA spec since arguments is binded to only formal parameters that match the provided arguments. Its recommended to only reference arguments and not alter it.
Examples: http://jsfiddle.net/MattLo/73WfA/2/
About Arguments
The non-strict arguments object is a bidirectional and mapped object to FormalParameterList, which is a list generated based on the number of provided arguments that map to defined parameters in the target function defintion/expression.
When control enters an execution context for function code, an arguments object is created unless (as specified in 10.5) the identifier arguments occurs as an Identifier in the function’s FormalParameterList or occurs as the Identifier of a VariableDeclaration or FunctionDeclaration contained in the function code. ECMA 5.1 spec (as of 03/26/2014)
arguments to variables / variables to arguments
Argument-to-variable mappings exist only internally when the method is invoked. Mappings are immutable and are created when a target function is invoked, per instance. The update bindings between the two is also disabled when the use strict flag is available within the scope of the function. arguments becomes strictly a reference and no longer a way to update parameters.
For non-strict mode functions the array index (defined in 15.4) named data properties of an arguments object whose numeric name values are less than the number of formal parameters of the corresponding function object initially share their values with the corresponding argument bindings in the function’s execution context. This means that changing the property changes the corresponding value of the argument binding and vice-versa. This correspondence is broken if such a property is deleted and then redefined or if the property is changed into an accessor property. For strict mode functions, the values of the arguments object‘s properties are simply a copy of the arguments passed to the function and there is no dynamic linkage between the property values and the formal parameter values. ECMA 5.1 spec (as of 03/26/2014)
What about length?
arguments.length can be an indicator initially to determine how many formal parameters were met but it's unsafe since arguments can be changed without length ever updating automatically. length doesn't update because it's not a real array and therefore doesn't adhere to the Array spec.

Categories

Resources