Related
I have the same question as this one, but in the context of JavaScript.
From Wikipedia:
[a pure function's] return value is the same for the same arguments
It's further claimed there that a pure function is not allowed to have a variation in return value with "mutable reference arguments". In JavaScript, every normal object is passed as a "mutable reference argument". Consider the following example:
const f = (arr) => arr.length
const x = []
console.log( f(x) ) // 0
x.push(1);
console.log( f(x) ) // 1
Is the above proof that f is impure?
Or would you argue that we're not calling f with the "same" argument in the two cases?
I can see how it would make sense to call f impure in a language/environment where other threads could potentially mess with the mutable reference argument while f is executing. But since f is not async, there is no way for this to happen. x is going to stay the same from the moment f is called to when it's done executing. (If I'm understanding correctly, this interpretation seems to be supported by the definition of "same" put forth in § 4.1 of Verifiable Functional Purity in Java.)
Or am I missing something? Is there an example in JavaScript where a function containing no asynchronous code loses the property of referential transparency simply because it's taking a mutable reference, but it would be pure if we used e.g. an Immutable.js data structure instead?
When taking the Wikipedia definition to the letter, a function that takes as argument a reference to a mutable data structure (such as a native Array) is not pure:
Its return value is the same for the same arguments (no variation with local static variables, non-local variables, mutable reference arguments or input streams from I/O devices).
Equivalence
Although this clearly says "no variation with mutable reference arguments", we could maybe say this is open to interpretation and depends on the meaning of "same" and "variation". There are different definitions possible, and so we enter the area of opinion. Quoted from the paper your referred to:
There is not a single obviously right answer to these questions. Determinism is thus a parameterized property: given a definition of what it means for arguments to be equivalent, a method is deterministic if all calls with equivalent arguments return results that are indistinguishable from within the language
The functional purity proposed in the same paper, uses the following definition of equivalence:
Two sets of object references are considered equivalent if they result in identical object graphs
So with that definition, the following two arrays are considered equivalent:
let a = [1];
let b = [1];
But this concept can not really be applied to JavaScript without adding more restrictions. Nor to Java, which is the reason why the authors of the paper refer to a trimmed-down language, called Joe-E:
objects have identity: conceptually, they have an “address”, and we can compare whether two object references point to the same “address” using the == operator. This notion of object identity can expose nondeterminism.
Illustrated in JavaScript:
const compare = (array1, array2) => array1 === array2;
let arr = [1];
let a = compare(arr, arr);
let b = compare(arr, [1]);
console.log(a === b); // false
As the two calls return a different result, even though the arguments had the same shape and content, we should conclude (with this definition of equivalence) that the above function compare is not pure. While in Java you can influence the behaviour of the == operator (Joe-E forbids calling Object.hashCode), and so avoid this from happening, this is not generally possible in JavaScript when comparing objects.
Unintended side effects
Another issue is that JavaScript is not strongly typed, and so a function cannot be certain that the arguments it receives are what they are intended to be. For instance, the following function looks pure:
const add = (a, b) => a + b;
But it can be called in way to give side effects:
const add = (a, b) => a + b;
let i = 0;
let obj = { valueOf() { return i++ } };
let a = add(1, obj);
let b = add(1, obj);
console.log(a === b); // false
The same problem exists with the function in your question:
const f = (arr) => arr.length;
const x = { get length() { return Math.random() } };
let a = f(x);
let b = f(x);
console.log(a === b) // false
In both cases the function unintentionally called an impure function and returned a result that depended on it. While in the first example it is easy to still make the function pure with a typeof check, this is less trivial for your function. We can think of instanceof or Array.isArray, or even some smart deepCompare function, but still, callers can set a strange object's prototype, set its constructor property, replace primitive properties with getters, wrap the object in a proxy, ...etc, etc, and so fool even the smartest equality checkers.
Pragmatism
As in JavaScript there are just too many "loose ends", one has to be pragmatic in order to have a useful definition of "pure", as otherwise almost nothing can be labelled pure.
For example, in practice many will call a function like Array#slice pure, even though it suffers from the problems mentioned above (including related to the special argument this).
Conclusion
In JavaScript, when calling a function pure, you will often have to agree on a contract on how the function should be called. The arguments should be of a certain type, and not have (hidden) methods that could be called but that are impure.
One may argue that this goes against the idea behind "pure", which should only be determined by the function definition itself, not the way it eventually might get called.
I just saw a code snippet in MDN about destructuring rest parameters like so:
function f(...[a, b, c]) {
return a + b + c;
}
f(1) // NaN (b and c are undefined)
f(1, 2, 3) // 6
f(1, 2, 3, 4) // 6 (the fourth parameter is not destructured)
the code snippet is in this page: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters
Although the common use case for rest parameters is very clear to me (function foo(...params){/*code*/}) I could not think about a real world use case to use rest parameters like the way presented in that code snippet. Instead, I think that in that case, I should just use a common function definition:
function f(a, b, c) {
return a + b + c;
}
f(1) // NaN (b and c are undefined)
f(1, 2, 3) // 6
f(1, 2, 3, 4) // 6 (the fourth parameter is not defined)
Your function f(a, b, c) { … } is indeed the proper way to write this. The only difference between that and the rest+destructuring syntax is that rest parameters do not add to number of parameters, i.e. f.length == 0.
There really is no good use case for putting an array destructuring pattern as the target of a rest parameter. Just because the syntax allows it doesn't mean that it's useful somewhere. The MDN example probably should've made that more clear.
The example illustrates that rest and destructuring syntaxes are flexible enough to be combined even in such a way.
It is known that neither TypeScript nor Babel stable versions currently support this syntax, primarily because it's of no practical use.
let's say that we have a function that returns an object such like that:
function getCustomer(id) {
return fetch(`http://myapi.com/customer/${id}`);
}
and let's say I have a response like that:
{
"customer": {
"id": 1234,
"name": "John Doe",
"latestBadges": [
"Platinum Customer",
"100 Buys",
"Reviewer"
]
}
}
In a more traditional approach I could write a function to show the latest 3 badges like so:
function showLatestBadges(a, b, c) {
console.log(a, b, c);
}
and to use that function, I would need to to:
getCustomer(1234).then((customer) => {
showLatestBadges(
customer.latestBadges[0],
customer.latestBadges[1],
customer.latestBadges[2]
);
});
With this new spread operator, I could do this instead:
getCustomer(1234).then((customer) => {
showLatestBadges(...customer.latestBadges);
});
So, using the spread operator in the function definition may look like it's a little useless. But, in fact, it CAN be useful in a VERY specific situation:
Let's say we have a legacy system, and let's say that the call to the showLatestBadges function is being made in hundreds of places without using the spread operator, just like the old days. Let's also assume that we are using a linting tool that prevents unused variables, and let's also assume that we are running a build process that do cares about the linting results, and if the linting says that something is not right, the build fails.
Let's ALSO ASSUME that for some weird business rule, we now have to show only the first and third badges.
Now, assuming this function call being made in hundreds of places in the legacy system, and we do not have much time available to deliver the implementation of this new business rule, we do not have time to refactor the code for ALL those hundreds of calls.
So, we will now change the function as so:
function showLatestBadges(a, b, c) {
console.log(a, c);
}
But now we have a problem: the build fails because of the unused b variable, and we have to deliver this change for YESTERDAY!!! We have no time to refactor all the hundreds of calls to this function, and we cannot just do a simple find and replace in all the spots, because we have such a messy code, and there are evals all over the place, and unpredictable behavior can happen.
So, one solution is: change the function signature using the spread operator, so the build succeeds, and create a task on the board to do the refactoring.
So, we can change the function as so:
function showLatestBadges(...[a,,c]) {
console.log(a, c);
}
Ok, I know this is a VERY specific situation and that this is very unlike to happen, but, who knows? ¯\_(ツ)_/¯
Actually the ... operator is two ways. It's both called rest and spread depending on your use case. They are both very powerful operators especially for functional approaches. You may always use spread operator as,
var a = [1,2,3],
b = [4,5,6];
a.push(...b);
which would yield a to be [1,2,3,4,5,6] all at once. At this moment one could say that .concat() could do the same. Yes concat has a built in spread functionality but a.concat(b) wouldn't effect a. I just creates and returns a new array. In fact in proper functional languages treating a as an immutable object is nice for the sake of purity. Yet JS is a weird language. It's believed to be functional but at the same time deeply embraces reference types. So long story short if you want to keep the references to a intact while mutating it then you can not use a.concat(b) but a.push(...b). Here i have to mention that .push() is not perfectly designed because it returns a stupid length property which is totally useless. It should have returned a. So I end up using the comma operator like (a.push(...b),a) most of the times.
OK apart from simple use cases you may stretch ... further for a little more complicated but cool looking implementations. Such as you may do an Haskellesque pattern matching to split head and tail of an array and recurse accordingly.
Here is a useful case of spread and rest operators working hand to hand to flatten an arbitrary nested array.
var flat = (x,...xs) => x ? [...Array.isArray(x) ? flat(...x) : [x], ...flat(...xs)] : [];
var na = [[1,2],[3,[4,5]],[6,7,[[[8],9]]],10];
fa = flat(na);
console.log(fa);
This is one of the use-cases I got to use this
const tail = function([, ...xs]) {
return xs;
}
tail([1,2]); // [2]
const head = ([a]) => a
head([1,2,3,4]) // 1
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.
OK lets take an example:
function student(name,age){
document.write("I am " + name + "and I am " + age + " years old." );
}
var student_name = "Divyansh";
var student_age = 17;
student(student_name,student_age);
// now my question is what do we call the arguments of the function student :
(a) is it the variable: student_name & student_age respectively,
(b) or is it the value contained by the variables: divyansh & 17 respectively
PS- I have also posted the same question on codechef, but it seems nobody is interested in giving answer to my stupid and childish question. But, what do i do i am trying to study Javascript myself by the book head first javascript programming and got strucked due to this doubt in one of the question, Please Help.
If I understood your question correctly, you want to know whether you should say
I called student with student_name and student_age.
or
I called student with "Divyansh" and 17.
right?
Well, that depends on the whole context, and sometimes it's possible to use both.
And this is to some extent a question of opinion, but I'll try to justify my opinion as good as I can.
I believe you should refer to the arguments by their names (i.e. student_name and student_age if
their value is of no significance for what you're saying.
Unnecessary information usually leads to confusion.
their value is unknown.
their value is long and you previously mentioned it.
Long values such as Math.pow((5*x+7*y-35*v*x*z)/(42*m/35*k+47*f+99*l), 35*22*88*k) tend to make written text messy and are really annoying to speak out many times in a row, so you'll want to refer to them by name.
You should refer to the arguments by their value when their value matters, e.g. when you have a bug that only occurs when certain values are passed to your function, but not some others.
Now there is a gap between those, where I believe you can use both.
For that case, I'd apply the golden rule
As short as possible, but as long as required.
If you had the following code:
var name = "Divyansh";
console.log(name);
var name = "Marc";
console.log(name);
var name = "Jon Skeet";
console.log(name);
Then I believe all of the following statements are perfectly valid:
I called console.log three times with name as argument.
or
I called console.log three times with "Divyansh", "Marc" and "Jon Skeet" as argument.
But it really depends on how much information you need or want to give.
An argument of a function is an expression with which the function is called.
In this case, the function is called with two expressions as arguments: student_name and student_age.
These expressions yield a value when evaluated: the value of the variables.
The function is called with two parameters. These parameters are the variables. And the function get the values of those variables and assign it in its local variable namely 'name' and 'age'.
Have you used console.log() method? Have you ever wondered what's the maximum number of parameters it takes? Answer is as many as it can.
Lets take an example.
function print(x,y){
console.log(x);
console.log(y);
}
Well this is an easy function, takes 2 arguments and print them in the console.
Lets say I have 5 parameters passed to function print. a,b,c,d,e. How would you modify the print method? May be something like this. (Don't write programs like this. It ain't effective).
function print(a,b,c,d,e){
console.log(a);
console.log(b);
console.log(c);
console.log(d);
console.log(e);
}
How about if I pass 100 parameters like, a1, a2, ... a100. What if I pass 1000 parameters. You can't write 1000 console.log statements to print them. So you would think of a for loop to print all the parameters.
But wait!
How do I access all the parameters inside a for loop unless if they are an array (use for-loop or for-in to iterate) or an object (use for-in to iterate)?
Now comes the object "arguments". This is a built-in object implicitly created by javascript for every function you declare.
What does this store? It stores all the parameters you passed to the function in the form of object.
arguments {
0: a,
1: b,
2: c,
3: d,
4: e
};
Now I have something like an object that holds all the parameters passed to the function, it will be easier to access them in a for-in loop. Now my print method will look like:
function print(a,b,c,d,e,f) {
for (var prop in arguments) {
console.log(arguments[prop]);
}
}
This will loop through all the properties of the arguments object and prints the value stored in each property.
What is stored in each property of argument object?
fn parameter object-property value stored in property
a 0 value of parameter a
b 1 value of parameter b
c 2 value of parameter c
d 3 value of parameter d
e 4 value of parameter e
f 5 value of parameter f
In your case you are passing two arguments named student_name and student_age to student function which contains value Divyansh and 17 respectively.
Is there a way to allow "unlimited" vars for a function in JavaScript?
Example:
load(var1, var2, var3, var4, var5, etc...)
load(var1)
Sure, just use the arguments object.
function foo() {
for (var i = 0; i < arguments.length; i++) {
console.log(arguments[i]);
}
}
In (most) recent browsers, you can accept variable number of arguments with this syntax:
function my_log(...args) {
// args is an Array
console.log(args);
// You can pass this array as parameters to another function
console.log(...args);
}
Here's a small example:
function foo(x, ...args) {
console.log(x, args, ...args, arguments);
}
foo('a', 'b', 'c', z='d')
=>
a
Array(3) [ "b", "c", "d" ]
b c d
Arguments
0: "a"
1: "b"
2: "c"
3: "d"
length: 4
Documentation and more examples here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters
Another option is to pass in your arguments in a context object.
function load(context)
{
// do whatever with context.name, context.address, etc
}
and use it like this
load({name:'Ken',address:'secret',unused:true})
This has the advantage that you can add as many named arguments as you want, and the function can use them (or not) as it sees fit.
I agree with Ken's answer as being the most dynamic and I like to take it a step further. If it's a function that you call multiple times with different arguments - I use Ken's design but then add default values:
function load(context) {
var defaults = {
parameter1: defaultValue1,
parameter2: defaultValue2,
...
};
var context = extend(defaults, context);
// do stuff
}
This way, if you have many parameters but don't necessarily need to set them with each call to the function, you can simply specify the non-defaults. For the extend method, you can use jQuery's extend method ($.extend()), craft your own or use the following:
function extend() {
for (var i = 1; i < arguments.length; i++)
for (var key in arguments[i])
if (arguments[i].hasOwnProperty(key))
arguments[0][key] = arguments[i][key];
return arguments[0];
}
This will merge the context object with the defaults and fill in any undefined values in your object with the defaults.
It is preferable to use rest parameter syntax as Ramast pointed out.
function (a, b, ...args) {}
I just want to add some nice property of the ...args argument
It is an array, and not an object like arguments. This allows you to apply functions like map or sort directly.
It does not include all parameters but only the one passed from it on. E.g. function (a, b, ...args) in this case args contains
argument 3 to arguments.length
Yes, just like this :
function load()
{
var var0 = arguments[0];
var var1 = arguments[1];
}
load(1,2);
As mentioned already, you can use the arguments object to retrieve a variable number of function parameters.
If you want to call another function with the same arguments, use apply. You can even add or remove arguments by converting arguments to an array. For example, this function inserts some text before logging to console:
log() {
let args = Array.prototype.slice.call(arguments);
args = ['MyObjectName', this.id_].concat(args);
console.log.apply(console, args);
}
Although I generally agree that the named arguments approach is useful and flexible (unless you care about the order, in which case arguments is easiest), I do have concerns about the cost of the mbeasley approach (using defaults and extends). This is an extreme amount of cost to take for pulling default values. First, the defaults are defined inside the function, so they are repopulated on every call. Second, you can easily read out the named values and set the defaults at the same time using ||. There is no need to create and merge yet another new object to get this information.
function load(context) {
var parameter1 = context.parameter1 || defaultValue1,
parameter2 = context.parameter2 || defaultValue2;
// do stuff
}
This is roughly the same amount of code (maybe slightly more), but should be a fraction of the runtime cost.
While #roufamatic did show use of the arguments keyword and #Ken showed a great example of an object for usage I feel neither truly addressed what is going on in this instance and may confuse future readers or instill a bad practice as not explicitly stating a function/method is intended to take a variable amount of arguments/parameters.
function varyArg () {
return arguments[0] + arguments[1];
}
When another developer is looking through your code is it very easy to assume this function does not take parameters. Especially if that developer is not privy to the arguments keyword. Because of this it is a good idea to follow a style guideline and be consistent. I will be using Google's for all examples.
Let's explicitly state the same function has variable parameters:
function varyArg (var_args) {
return arguments[0] + arguments[1];
}
Object parameter VS var_args
There may be times when an object is needed as it is the only approved and considered best practice method of an data map. Associative arrays are frowned upon and discouraged.
SIDENOTE: The arguments keyword actually returns back an object using numbers as the key. The prototypal inheritance is also the object family. See end of answer for proper array usage in JS
In this case we can explicitly state this also. Note: this naming convention is not provided by Google but is an example of explicit declaration of a param's type. This is important if you are looking to create a more strict typed pattern in your code.
function varyArg (args_obj) {
return args_obj.name+" "+args_obj.weight;
}
varyArg({name: "Brian", weight: 150});
Which one to choose?
This depends on your function's and program's needs. If for instance you are simply looking to return a value base on an iterative process across all arguments passed then most certainly stick with the arguments keyword. If you need definition to your arguments and mapping of the data then the object method is the way to go. Let's look at two examples and then we're done!
Arguments usage
function sumOfAll (var_args) {
return arguments.reduce(function(a, b) {
return a + b;
}, 0);
}
sumOfAll(1,2,3); // returns 6
Object usage
function myObjArgs(args_obj) {
// MAKE SURE ARGUMENT IS AN OBJECT OR ELSE RETURN
if (typeof args_obj !== "object") {
return "Arguments passed must be in object form!";
}
return "Hello "+args_obj.name+" I see you're "+args_obj.age+" years old.";
}
myObjArgs({name: "Brian", age: 31}); // returns 'Hello Brian I see you're 31 years old
Accessing an array instead of an object ("...args" The rest parameter)
As mentioned up top of the answer the arguments keyword actually returns an object. Because of this any method you want to use for an array will have to be called. An example of this:
Array.prototype.map.call(arguments, function (val, idx, arr) {});
To avoid this use the rest parameter:
function varyArgArr (...var_args) {
return var_args.sort();
}
varyArgArr(5,1,3); // returns 1, 3, 5
Use the arguments object when inside the function to have access to all arguments passed in.
Be aware that passing an Object with named properties as Ken suggested adds the cost of allocating and releasing the temporary object to every call. Passing normal arguments by value or reference will generally be the most efficient. For many applications though the performance is not critical but for some it can be.
Use array and then you can use how many parameters you need. For example, calculate the average of the number elements of an array:
function fncAverage(sample) {
var lenghtSample = sample.length;
var elementsSum = 0;
for (var i = 0; i < lenghtSample; i++) {
elementsSum = Number(elementsSum) + Number(sample[i]);
}
average = elementsSum / lenghtSample
return (average);
}
console.log(fncAverage([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])); // results 5.5
let mySample = [10, 20, 30, 40];
console.log(fncAverage(mySample)); // results 25
//try your own arrays of numbers