I have the following code that includes Spread Syntax in JS
const opposite = function (f) { return (function (...args) { return !f(...args) }) };
can it be converted without using Spread Syntax? It seems that if I do use this, some of the js minification tools fail to parse these lines of codes.
You can by using the arguments object in combination with Function.prototype.apply():
The arguments object exist inside every function call and it represents the arguments to the function. Using this we can capture any and all arguments passed to the function.
The apply method on the other hand allow you to call a function by passing an array to represent the arguments.
Combining both we can do this:
const opposite = function (f) {
return (function () {
return !f.apply(null, arguments)
})
};
would be to use the apply method, like this:
const opposite = function (f) {
return function () {
return !f.apply(this, arguments);
}
};
Another way to achieve the same result would be to use the call method and pass the arguments object to the function, like this:
const opposite = function (f) {
return function () {
return !f.call(this, ...arguments);
}
};
Both of these methods allow you to pass an arbitrary number of arguments to the function without using the spread syntax.
Related
Can I call a function with array of arguments in a convenient way in JavaScript?
Example:
var fn = function() {
console.log(arguments);
}
var args = [1,2,3];
fn(args);
I need arguments to be [1,2,3], just like my array.
Since the introduction of ES6, you can sue the spread syntax in the function call:
const args = [1,2,3];
fn(...args);
function fn() {
console.log(arguments);
}
Before ES6, you needed to use apply.
var args = [1,2,3];
fn.apply(null, args);
function fn() {
console.log(arguments);
}
Both will produce the equivalent function call:
fn(1,2,3);
Notice that I used null as the first argument of the apply example, which will set the this keyword to the global object (window) inside fn or undefined under strict mode.
Also, you should know that the arguments object is not an array, it's an array-like object, that contains numeric indexes corresponding to the arguments that were used to call your function, a length property that gives you the number of arguments used.
In ES6, if you want to access a variable number of arguments as an array, you can also use the rest syntax in the function parameter list:
function fn(...args) {
args.forEach(arg => console.log(arg))
}
fn(1,2,3)
Before ES6, if you wanted to make an array from your arguments object, you commonly used the Array.prototype.slice method.
function fn() {
var args = Array.prototype.slice.call(arguments);
console.log(args);
}
fn(1,2,3);
Edit: In response to your comment, yes, you could use the shift method and set its returned value as the context (the this keyword) on your function:
fn.apply(args.shift(), args);
But remember that shift will remove the first element from the original array, and your function will be called without that first argument.
If you still need to call your function with all your other arguments, you can:
fn.apply(args[0], args);
And if you don't want to change the context, you could extract the first argument inside your function:
function fn(firstArg, ...args) {
console.log(args, firstArg);
}
fn(1, 2, 3, 4)
In ES5, that would be a little more verbose.
function fn() {
var args = Array.prototype.slice.call(arguments),
firstArg = args.shift();
console.log(args, firstArg);
}
fn(1, 2, 3, 4);
In ECMAScript 6, you can use spread syntax (...) for that purpose. It's way simpler and easier to understand than Function.prototype.apply().
Code example:
const fn = function() {
console.log(arguments);
}
const args = [1,2,3];
fn(...args);
I am trying to figure out what this piece of code is doing, and how this is working. How is let foo = require('foo') being called when it doesn't take any argument?
foo.js
module.export = async () => { do something and return response(200) }
bar.js
let foo = require('foo')
module.exports = {
foo: async (req) => { return foo(req) }
}
route.js
let api = required('api')
let bar = required('bar')
module.exports = api => {
api.get('/foo', async req => await bar.foo(req))
}
TLDR:
Yes, it is allowed.
How it works
All functions in js have access to a local variable called arguments. The arguments variable is an array-like object (something that looks like an array but is not an instance of the Array class) containing all arguments passed to the function. This is basically the js mechanism supporting variable arguments.
Example:
function a () {
for (x=0; x<arguments.length); x++) {
console.log(arguments[x]);
}
}
In addition to allowing you to pass more arguments than what's defined by a function js also allows you to pass fewer arguments that what's required by a function. The arguments that you don't pass are simply given the value of undefined.
I have found a script example, in it there is a line of code that looks something like this:
fn = (arg1) => (arg2) => {
//do something with arg1 and arg2
}
I am wondering exactly what is happening here, and how would it look as a "normal" function?
fn = (arg1) => (arg2) => {
//do something with arg1 and arg2
}
fn is a name for the first anon function
its basically a function that returns another function
it translated roughly to
var fn = function(arg1){
return function(arg2){
... // do something
}
}
noting that the this value will differ because it is an arrow function .
It looks like two nested function, where the outer function returns the inner function with a closure over arg1.
var fn = function (arg1) {
return function (arg2) {
//do something with arg1 and arg2
};
};
var fn = function (arg1) {
return function (arg2) {
return arg1 + arg2;
};
};
var add4 = fn(4),
add20 = fn(20);
console.log(add4(5)); // 9
console.log(add20(5)); // 25
Arrow function:
An arrow function expression has a shorter syntax than a function expression and does not bind its own this, arguments, super, or new.target. These function expressions are best suited for non-method functions, and they cannot be used as constructors.
I cannot add comments so I write this as an answer. Your example is also known as currying a concept that allows you to pass in a subset of arguments to a function and get a function back that’s waiting for the rest of the arguments.
I'm doing a tutorial on lexical scope handling by Typescript and I've come across the use of a function which I have never seen before. Which looks like an empty function in a forEach statement. In typescript it looks like this:
fns.forEach(fn=>fn());
In javascript it looks like:
fns.forEach(function (fn) { return fn(); });
I've never seen a function used like this. Can someone explain how this works? To be specific, what is fn=>fn() actually executing. In reference to the code below, is it executing the fns.push or for loop? If it is the For Loop there's no reference to this so how does it know?
Below is the full code:
TypeScript:
var fns = [];
for(var i=0;i<5;i+=1){
fns.push(function(){
console.log(i);
})
}
fns.forEach(fn=>fn());
JavaScript
var fns = [];
for (var i = 0; i < 5; i += 1) {
fns.push(function () {
console.log(i);
});
}
fns.forEach(function (fn) { return fn(); });
Check out this example:
var method = a => a+1;
method is a var that holds a reference to a function.
a is the input param.
a+1 is the methods body and return type.
Typescript will transpile this into:
var method = function (a) { return a + 1; };
Check out the typescript playground example and you will understand the concept pretty fast.
In your example fns is an array of functions.
doing fns.forEach(fn=>fn()); means execute each method in the fns array.
fn => fn() is a function definition, in C# they are called Lambda expressions and it is just syntactic sugar for the same function (fn) { return fn(); }.
fn is the input parameter, and => defines it as a function and the fn() is the return value.
Another example is
var add = (a,b) => a + b;
is the same as
function add(a, b) {
return a + b;
}
It is looping through an array of functions, because functions can be stored inside variables just like strings, integers etc. So you're looping through an array of functions and executing them like this: return fn();
I would like to make a generic function wrapper that (for example) prints the called function and its arguments.
Doing so is easy through the arguments quasi-array and simple calls. For example:
function wrap(target, method) {
return function() {
console.log(Array.prototype.slice.call(arguments).join(', '));
return method.apply(target, arguments);
}
}
However, this way of doing of course completely loses the arity of the called function (if you didn't know, one can obtain the arity (number of arguments) of a JavaScript function through its length property).
Is there any way to dynamically create a wrapper function that would copy the arguments of the wrapped function to itself?
I've thought about creating a new Function object, but I don't see any way to statically extract the arguments list, since the arguments property is deprecated.
Here's a solution using Function:
// could also generate arg0, arg1, arg2, ... or use the same name for each arg
var argNames = 'abcdefghijklmnopqrstuvwxyz';
var makeArgs = function(n) { return [].slice.call(argNames, 0, n).join(','); };
function wrap(target, method) {
// We can't have a closure, so we shove all our data in one object
var data = {
method: method,
target: target
}
// Build our function with the generated arg list, using `this.`
// to access "closures"
f = new Function(makeArgs(method.length),
"console.log(Array.prototype.slice.call(arguments).join(', '));" +
"return this.method.apply(this.target, arguments);"
);
// and bind `this` to refer to `data` within the function
return f.bind(data);
}
EDIT:
Here's a more abstract solution, which fixes the closure problem:
function giveArity(f, n) {
return new Function(makeArgs(n),
"return this.apply(null, arguments);"
).bind(f);
}
And a better one, that preserves context when invoked:
function giveArity(f, n) {
return eval('(function('+makeArgs(n)+') { return f.apply(this, arguments); })')
}
Used as:
function wrap(target, method) {
return giveArity(function() {
console.log(Array.prototype.slice.call(arguments).join(', '));
return method.apply(target, arguments);
}, method.length)
}