JavaScript default parameters for function - javascript

I can fully understand ECMAScript 6 has created a lot of potential way of handling with functions such as arrow functions.
Since I'm not very familiar with the new stuff, when talking about default parameters for a function. How to interpret the differences between the following way of defining functions:
Function 1:
function m1({x = 0, y = 0} = {}) {
return [x, y];
}
Function 2:
function m2({x, y} = { x: 0, y: 0 }) {
return [x, y];
}

The difference is clear when you try passing something to your functions:
m1({}) // [0, 0]
m1({z: 1}) // [0, 0]
m1({x: 1}) // [1, 0]
m2({}) // [undefined, undefined]
m2({z: 1}) // [undefined, undefined]
m2({x: 1}) // [1, undefined]
Your first syntax (m1({x = 0, y = 0} = {})) does three things:
First, it provides a default first argument to the function, which is an empty object. If no first argument is given (m1()) then the default empty object is used (i.e. it becomes m1({}))
Second, your code extracts the x and y properties from that object.
If either is undefined, it is given a default value 0.
m2({x, y} = { x: 0, y: 0 }) does something quite different:
First it provides a default first parameter to the function, which is the object {x: 0, y: 0}. If no first argument is passed, that object is used. If any argument other than undefined is passed, that value is used instead.
Second, the code extracts the x and y properties from that object. If they are undefined, that's what you'll get.
The first option (a parameter with a default value that is destructured with more default values) is almost certainly what you want. The second option means that your code does not have sensible/useful default values for the property if arguments are passed.

m1 provides default values for x and y, whereas m2 merely destructures x and y from a provided object and only provides default values if the object itself isn’t provided:
m1({}) will return [0, 0]
m2({}) will return [undefined, undefined]
Both m1() and m2() will return [0, 0]
m1({x: 10}) will return [10, 0]
m2({x: 10}) will return [10, undefined]
So, if m2 receives an object, it will destructure the available values to the variables x and y. If any of them is missing, it’s undefined. Only if the whole object is missing, it’ll provide a default object ({ x: 0, y: 0 }) from which to get the values.
m1, however, provides default values for both properties even if they’re missing. And if the whole object is missing, it’ll still provide those default values.

Related

Can we set persistent default parameters which remain set until explicitly changed?

The below is a function fn where expected result is for a, b, c to defined at every call of fn, whether an object parameter is passed or not. If object is passed which sets property, property should be set only for that object.
const fn = (opts = {a:1, b:2, c:3}) => console.log(opts);
when called without parameters the result is
fn() // {a: 1, b: 2, c: 3}
when called with parameter, for example {b:7}, the expected result is
fn({b:7}) // {a: 1, b: 7, c: 3}
however, the actual result is
fn({b:7}) // {b: 7}
Was able to get expected result by defining an object outside of function and using Object.assign() within function body
const settings = {a: 1, b: 2, c: 3};
const fn = opts => {opts = Object.assign({}, settings, opts); console.log(opts)}
fn({b: 7}) // {a: 1, b: 7, c: 3}
fn(); // {a: 1, b: 2, c: 3}
/*
// does not log error; does not return expected result
const fn = (opts = Object.assign({}, settings, opts)) => console.log(opts)
*/
Can the above result be achieved solely utilizing default parameters, without defining an object to reference outside of function parameters or within function body?
Maybe I misunderstood the question, but you seem to be looking for default initialisers for each separate property. For that, you have to use destructuring:
const fn = ({a = 1, b = 2, c = 3} = {}) => console.log({a, b, c});
If you want to keep arbitrary properties, not just those that you know of up front, you might be interested in the object rest/spread properties proposal that allows you to write
const fn = ({a = 1, b = 2, c = 3, ...opts} = {}) => console.log({a, b, c, ...opts});
Can an opts variable as the single object reference be achieved solely utilizing default parameters, without defining an object to reference outside of function parameters or within function body?
No. Parameter declarations are only able to initialise variables with (parts of) the arguments, and possibly (as syntactic sugar) with default values when no or undefined argument (parts) are passed. They are not able to carry out unconditional computations and create variables inialised from the results - which is what you attempt to achieve here.
You are supposed to use the function body for that.
No
The best that can be done is either your own answer or this:
const fn = (default_parameters) => {
default_parameters = Object.assign({}, {a: 1, b: 2, c: 3},default_parameters);
console.log('These are the parameters:');
console.log(default_parameters);
}
fn();
fn({b: 7});
fn({g: 9, x: 10});
The default parameter block is only executed if the value is not set, so your own answer is the best that is on offer ie use two parameters
You can convince yourself of this by creating a code block that will fail if executed and testing that passing a parameter works (to show that the code block is not executed) and testing that not passing a parameter fails (showing that the code block is only executed when no parameter is passed).
This should demonstrate clearly that any paramter passed will prevent the default parameter from being evaluated at all.
const fn = (default_parameters = (default_parameters = Object.assign({}, {a: 1, b: 2, c: 3},default_parameters))) => {
console.log('These are the parameters:');
console.log(default_parameters);
}
fn({b: 7});
fn();
fn({g: 9, x: 10});
We can set fn as a variable which returns an arrow function expression. When called set a, b, c and rest parameters reference using spread element at new object, which is returned when the function is invoked.
const fn = ((...opts) => ({a:1,b:2,c:3, ...opts.pop()}));
let opts = fn();
console.log(opts);
opts = fn({b: 7});
console.log(opts);
opts = fn({g: 9, x: 10});
console.log(opts);
Using rest element, Object.assign(), spread element, Array.prototype.map(), setting element that is not an object as value of property reflecting index of element in array.
const fn = ((...opts) => Object.assign({a:1,b:2,c:3}, ...opts.map((prop, index) =>
prop && typeof prop === "object" && !Array.isArray(prop)
? prop
: {[index]:prop}))
);
let opts = fn([2,3], ...[44, "a", {b:7}, {g:8, z: 9}, null, void 0]);
console.log(opts);
Though code at OP uses single default parameter, until we locate or develop a procedure for using only single parameter, we can utilize setting two default parameters to achieve expected result.
The first parameter defaults to a plain object, at second default parameter we pass parameter identifier from first parameter to Object.assign() following pattern at Question.
We reference second parameter identifier of function fn to get the default parameters when called without parameters; when called with first parameter having properties set to properties of object passed at first parameter and default parameters, the former overwriting the latter at the resulting object.
const fn = (__ = {}, opts = Object.assign({}, {a: 1, b: 2, c: 3}, __)) =>
console.log(opts);
fn();
fn({b: 7});
fn({g: 9, x: 10});

Value for optional parameter by name (Javascript)

I have a javascript function like this
function drawImageOnCanvas(canvas, texturePath, color, type, onCall, modelPath, scene, pX, pY, pZ, scale, transform) {}
Here onCall, modelPath, scene, pX, pY, pZ, scale, transform are optional parameters. How can i pass value only for scale. Is there any way to define parameter name and pass value?
You can use an object as the function's parameter.
function drawImageOnCanvas(canvas, options){
var scale = options.scale;
}
And in call site:
drawImageOnCanvas(canvas, {scale: 2});
Also to handle optional parameters, you can check their existence using an if or || or ?:.
var color = /*default color*/;
if (options.color){
color = options.color;
}
Or
var color = options.color || /*default color*/;
Or
var color = options.color ? options.color : /*default color*/;
Note: If options contains parameters having false, 0, etc values then the methods above are not suitable anymore. For example, assume we have a parameter called isActive, then passing {isActive: false} will lead to the /*default isActive*/. To address this problem, you can use .hasOwnProperty or in.
var options = {scale: 2, isActive: false};
console.log('isActive' in options); // true
console.log(options.hasOwnProperty('isActive')); // true
console.log(options.isActive); // false
Anytime you have a function with many optional parameters, a much better option is to use a single parameter for all the optional parameters as an object.
In ES6, this case be more easily accomplished using destructuring and default parameters.
function drawImageOnCanvas(canvas, {scale = 1} = {}) {
...
}
Then you can call the function like this:
drawImageOnCanvas(canvas, { scale: 2 });
function test({ x = 10 } = {}) {
console.log(x);
}
test({ x: 100 }); // passing x
test({ y: 200 }); // not passing x
test(); // not passing anything still works
You create an array having .length equal to the number of expected parameters to function, set parameters passed to function as elements of an array, set index 10 of array to value for scale, use rest element to pass array of parameters to function, destructuring assignment within function to define named variables
function drawImageOnCanvas(canvas, texturePath, color, type, onCall
, modelPath, scene, pX, pY, pZ, scale, transform) {
console.log(canvas, texturePath, color, type, scale);
}
var args = Array.of(document.createElement("canvas"), 1, 2, 3, ...Array(7));
args[10] = {scale:456};
drawImageOnCanvas(...args);

ES6 destructuring object assignment function parameter default value

Hi I was going through examples of object destructuring use in passing function parameters here Object Destructuring Demo
function drawES6Chart({size = 'big', cords = { x: 0, y: 0 }, radius = 25} = **{}**) {
console.log(size, cords, radius);
// do some chart drawing
}
// In Firefox, default values for destructuring assignments are not yet
implemented (as described below).
// The workaround is to write the parameters in the following way:
// ({size: size = 'big', cords: cords = { x: 0, y: 0 }, radius: radius =
25} = **{}**)
drawES6Chart({
cords: { x: 18, y: 30 },
radius: 30
});
Can anybody let me know what is reason of using empty object assignment at the end of function parameter which I have marked in bold(embedded in double stars) above?
If you use it, and call the function with no parameters, it works:
function drawES6Chart({size = 'big', cords = { x: 0, y: 0 }, radius = 25} = {}) {
console.log(size, cords, radius);
// do some chart drawing
}
drawES6Chart();
if not, an error is thrown:
TypeError: can't convert undefined to object
function drawES6Chart({size = 'big', cords = { x: 0, y: 0 }, radius = 25}) {
console.log(size, cords, radius);
// do some chart drawing
}
drawES6Chart();
The destructuring with defaults only does its thing when you pass an object which doesn't have the respective properties. The = {} default for the whole parameter allows to not pass an (empty) object at all.
It makes drawES6Chart() equivalent to drawES6Chart({}).
You have an object with your default values, but that object is an argument too, so it needs an empty object as a default value for the first argument, which is the object with the filled in values.
function drawES6Chart({size = 'big', cords = { x: 0, y: 0 }, radius = 25} = {}) {
}
That, in pseudo code, would be:
function drawES6Chart({**first argument**} = {**default value for first argument**}) {
}
Here is a (much longer than I originally intended) description of the phenomenon
you are observing from a more rigorous point of view. Why more rigorous? I
wanted to investigate this question because I wasn't sure if there was some
special rule regarding function default arguments, or if there was something
fundamental about destructuring that I didn't understand. Turns out, it was the
latter.
I'll describe my findings using pseudo-grammar that somewhat mirrors what you'll
see in ECMA-262. That is my only reference.
Key Points:
There are the Destructuring Assignments and Destructuring Binding
Patterns. The purpose of both is to introduce names and assign
values.
Destructuring Assignment:
ObjectAssignmentPattern : '{' AssignmentPropertyList '}' = AssignmentExpression
AssignmentPropertyList : AssignmentProperty [',' AssignmentProperty]
These two just state the general form of the Destructuring Assignment.
AssignmentProperty : IdentifierReference [Initializer]
This is a "default value" for a name in the LHS.
AssignmentProperty : PropertyName ':' AssignmentElement
AssignmentElement : LeftHandSideExpression [Initializer]
This lets the destructuring nest recursively, but the semantics need to be
defined.
Semantics
If you look at
DestructuringAssignmentEvaluation,
you can see who gets assigned to what. ObjectAssignmentPattern is not very
interesting, it gives the basic '{' assignments '}' structure of the LHS,
what's more interesting is 12.15.5.3,
PropertyDestructuringAssignmentEvaluation. This shows what happens when you
actually assign default values, and when you bind more deeply nested names.
AssignmentProperty : IdentifierReference [Initializer]
Step 3 is important in this algorithm, where GetV is called. In this call, it
is attempting to get the value of the name that is currently being assigned to
(LHS) from value (RHS). This can throw, and is why the following snippet
throws:
y = Object.defineProperty({},'foo',{get: () => {throw new Error("get foo");}})
{foo} = y;
The next step, step 4, just evaluates the initializer if it exists and the
value obtained from the RHS is undefined. For example:
y = Object.defineProperty({},'foo',{get: () => undefined})
{foo = 3} = y; // foo === 3
Note that this step, and the step of actually "putting" the value where it needs
to go, can both throw. The next item is more tricky, and is where confusion
most certainly arises:
AssignmentProperty : PropertyName ':' AssignmentElement
The semantics here are to kick the can down the road to
KeyedDestructuringAssignmentEvaluation,
passing the PropertyName and current value (RHS). Here's the header for its
runtime semantics:
AssignmentElement : DestructuringAssignmentTarget [Initializer]
The steps of the ensuing algorithm are somewhat familiar, with a few surprises
and indirections. Almost any step in this algorithm can throw, so it won't be
pointed out explicitly. Step 1 is another "base of recursion," saying that if
the target is not an object or array literal (e.g. just an identifier), then let
lref be that (note that it doesn't have to be an identifier, just something
that can be assigned to, e.g.
w = {}
{x:w.u = 7} = {x:3} // w == {u:3}
Then, an attempt to retrieve the "target value" value.propertyName is made,
using GetV. If this value is undefined, an attempt is made to get the
intializer value, which in step 6 is put into lref. Step 5 is the recursive
call, peeling off as many layers as necessary to achieve the base case for the
destructured assignment. Here are a few more examples that I think illustrate
the point.
More clarifying Examples:
{x={y:1}} = {} // x == {y:1}
{x:{y=1}} = {} // error, {}.x is undefined, tried to find {}.x.y
x = 'foo'
{x:{y=1}} = {x} // x == 'foo', y == 1.
// x doesn't get assigned in this destructuring assignment,
// RHS becomes {x:x} === {x:'foo'} and since 'foo'.y is
// undefined, y gets the default 1
{x:{y=1}} = {x:{y}} // error, tried to give object value {y} === {y:y} to x
// in RHS, but y is undefined at that point
y = 'foo'
{x:{y=1}} = {x:{y}} // y == 'foo', gave {y} === {y:y} === {y:'foo'} to x in RHS
{x:{y=1}} = {x:{y:2}} // y == 2, maybe what you wanted?
// exercises:
{x=1} = undefined // error
{x=1} = null // error
{x=1} = null || undefined // error
{x=1} = null | undefined // can you guess? x == 1
Function Declarations
I actually started looking into destructuring after seeing the following code in
the source for react-redux:
export function createConnect({
connectHOC = connectAdvanced,
mapStateToPropsFactories = defaultMapStateToPropsFactories,
mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories,
mergePropsFactories = defaultMergePropsFactories,
selectorFactory = defaultSelectorFactory
} = {}) {
So, the first place I started digging was:
14.1 Function Definitions
Here is a little "stack trace" trying to track down the relevant productions to
get me to the binding stuff.
FunctionDeclaration
FormalParameters
FormalParameterList
FormalParameter
-> 13.3.3 Destructuring Binding Patterns
BindingElement
+SingleNameBinding
++BindingIdentifier, Initializer
+BindingPattern
+ObjectBindingPattern
+BindingPropertyList
+BindingProperty
+SingleNameBinding,
+PropertyName ':' BindingElement
Semantics: Destructuring Binding vs. Destructuring Assignment
As far as I can tell, the only difference between Destructuring Binding and
Destructuring Assignment is where they can be used and how different lexical
environments are handled. Destructuring Binding outside of formal parameter
lists (and the like) require initializers, and Destructuring Binding is passed
an environment explicitly, while assignments, which by their definition implie
an initializer," get their values from the "ambience." I'd be very happy to hear
why that's wrong, but here is a quick demonstration:
var {x}; // syntax error
function noInit({x}) { return x; }
// ok
noInit() // runtime error
noInit({}) // undefined
noInit({x:4}) // 4
function binding({x:y} = {x:y}){ return y; }
function assigning(){({x:y} = {x:y}); return y}
binding() // error, cannot access y before initialization
assigning() // error, y is not defined
y = 0
binding() // still error
assigning() // 0 - now y is defined
Conclusion:
I conclude the following. The purpose of destructuring binding and assignment
is to introduce names into the current lexical environment, optionally assigning
them values. Nested destructuring is to carve out the shape of the data you
want, and you don't get the names above you for free. You can have initializers
as default values, but as soon as you use them you can't carve any deeper. If
you carve out a particular shape (a tree, in fact), what you attempt to bind to
may have undefined leaves, but the branch nodes must match what you've
described (name and shape).
Addendum
When I started this I found it helpful and interesting to see what tsc (the typescript compiler) would transpile these things into, given a target that does not support destructuring.
The following code:
function f({A,B:{BB1=7,BB2:{BBB=0}}}) {}
var z = 0;
var {x:{y=8},z} = {x:{},z};
Transpiles (tsc --target es5 --noImplicitAny false) into:
function f(_a) {
var A = _a.A,
_b = _a.B,
_c = _b.BB1,
BB1 = _c === void 0 ? 7 : _c,
_d = _b.BB2.BBB,
BBB = _d === void 0 ? 0 : _d;
}
var z = 0;
var _a = { x: {}, z: z },
_b = _a.x.y,
y = _b === void 0 ? 8 : _b,
z = _a.z;
That's a default value for the function parameter. Without using = {} JavaScript interpreter throws an error when there is no object passed to the function as it can't destructure an undefined value.

Set value to a referenced JavaScript object

For example, I have the following JS object:
var obj = {
n: 0,
o: {}
};
var nGlobal = obj.n;
var oGlobal = obj.o;
And want to use global variables in order to make it look like this:
var obj = {
n: 5,
o: {
x: 7
}
};
Obviously, I am able to assign a value to a property of oGlobal:
oGlobal.x = 7;
However, is there any way to change the value of obj.n through nGlobal, without mentioning obj?
Just something like this:
console.log(obj); // {n: 1, o: {}}
nGlobal.set(5);
// or
nGlobal.value = 5;
console.log(obj); // {n: 5, o: {}}
Short answer:
No, there is not.
Long answer:
In JS primitive types are immutable and passed by values.
JS implements pass-by-value strategy, which means that ALL data is always passed by value (in case if you pass a reference to object - the reference is passed by value)
One important consequence from the items above: in JS there is no way to change the original object using the = operation. That is: you can modify an object when you have a reference to it, but you cannot swap one object with something else so that all other references also "were modified".
Related:
Evaluation strategy - Call by sharing
Credits:
Felix Kling

JavaScript nasted default parameters

Lets say I want to process some property x of objects in collection array. But collection may contain objects without such property or even undefineds. For example
let array = [
{x: 1},
{x: 2},
{},
{x: 4},
undefined
]
The idea is protect my self from such edge cases with default parameter. Let it be 0. I was trying to solve this as
array.map(({x: x = 0}) => process(x))
But it fails on undefined. Is there any way to solve this issue with default parameters and destructuring without writing check/set code inside of map function?
You can give the default object a default value
array.map(({x : x = 0} = 0) => process(x));
You can use .filter before .map in order to clean all falsy values like null, 0, '', false
array = array
.filter((el) => el)
.map(({x: x = 0}) => process(x));
Example
In MDN there is good description
Default function parameters allow formal parameters to be initialized with default values if no value or undefined is
passed.
so null is value., so if you pass null to function default value does not used, for example
function test(x = 10) {
console.log(x);
}
test(undefined); // 10
test(); // 10
test(null); // null
test(0); // 0
test(''); // ''
Example

Categories

Resources