So I'm using an object to pass in my optional variables like so:
var foo = function (options) {
var options = options || {}; //provide a default, so that
var bar = options.bar || 42; //this doesn't fail on 'foo();'
//do stuff
};
JSLint complains that I'm overwriting options, which is what I want if it's falsy. Actually, I should probably check if it's an Object and elsewise throw an error to let the user know what's up. So - on that note - what would be good form here? Anybody know a good read on this? Additionally, how would I go about writing a bunch of functions with this pattern in a DRY style?
First, you need to not reassign the options argument to a var. Second, be careful with this for certain values:
var foo = function (options) {
options = options || {};
var bar = options.bar || 42;
var baz = options.baz || 'fubar';
};
foo({ bar: 0, baz: "" });
Inside foo, bar == 42 & baz == 'fubar' because 0 and "" are falsy. It's probably better to be more verbose so as to be more precise:
var foo = function (options) {
options = options || {};
var bar = typeof options.bar == 'undefined' ? 42 : options.bar;
var baz = typeof options.baz == 'undefined' ? 'fubar' : options.baz;
};
But, to be DRY, you can create a defaults object and just extend both objects to a settings object:
// generic shallow object extension, mutates obj1
var extend = function (obj1, obj2) {
for (var prop in obj2) {
if (obj2.hasOwnProperty(prop)) {
obj1[prop] = obj2[prop];
}
}
return obj1;
};
var foo = function (options) {
var defaults = {
bar: 42,
baz: 'fubar'
},
settings = extend(defaults, options);
};
Since you won't be using defaults any more in this invocation it doesn't matter that it gets mutated. Using this method:
foo(); // -> settings = { bar: 42, baz: 'fubar' }
foo({ bar: 0 }); // -> settings = { bar: 0, baz: 'fubar' }
foo({ bar: 0, baz: "" }); // -> settings = { bar: 0, baz: '' }
Remove the var from the options line and JSLint will stop complaining.
It's because you have a parameter called options, and then you are trying to declare a local variable called options. You can still set the default value if it's falsey
options = options || {};
To be honest, the null coalescing in JavaScript is pretty fine on it's own.
You can remember that you can be a bit 'exotic' with it, if you really want. So, for your example, if you just wanted options.bar or 42:
(options || {})["bar"] || 42
(remembering that you can access JavaScript properties by dot notation or array style)
It's not pretty - but it's just one example. Alternatively, you could do something like:
(options || {"bar":42})["bar"]
If you had a default set of options, you can do:
(options || defaults)["bar"]
Note : As mentioned in a different answer, you should be careful before certain values are falsely that you might not realise (i.e. an empty string). Of course, sometimes this is exactly what you want but it's just something to remember :)
JSLint is only complaining because you are using the var keyword, but options has already been defined. You can just remove the var and it won't complain.
To test if options is an object you can use typeof options === 'object', but note that arrays and null are also objects, so you may want to test something like:
typeof options === 'object' && options !== null && !(options instanceof Array)
Related
I ran into some code that looks like the following:
const {
foo = []
} = this.options
Assuming in this case that this.options is an JavaScript Object, how does this work? Does all of this.options get assigned to foo and if this.options is undefined, does foo just get initialized to an empty array? I found this code confusing because this.options is not an Array but is instead an Object of key/val pairs.
Sometimes it helps to just try things out. What you'd observe is that a default value is assigned to foo in case it is missing within the to be assigned object
function one() {
const options = {};
const {
foo = []
} = options;
console.log(foo);
}
function two() {
const options = {foo: 'bar'};
const {
foo = []
} = options;
console.log(foo);
}
function three() {
const options = {};
const {
foo = 'foo',
bar = 'bar',
baz = 'baz'
} = options;
console.log(foo, bar, baz);
}
one();
two();
three();
From MDN (emphesis mine ) :
The destructuring assignment syntax is a JavaScript expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables.
Not all of this.options get assigned to foo, it's unpacking foo from the Object :
const options = {
foo: ['foo', 'bar'],
bar: 'hello world'
}
const {
foo = []
} = options;
console.log(foo);
And foo = [] is there to be a fallback to have an empty array if this.options does not have a property foo :
const options = {
bar: 'hello world'
}
const {
foo = []
} = options;
console.log(foo);
If this.options is ` undefined, you'll get errors,
options is not defined
const {
foo = []
} = options;
console.log(foo);
Or:
Cannot destructure property foo of 'undefined' or 'null'.
const options = undefined;
const {
foo = []
} = options;
console.log(foo);
If you run it through babel you'll see that if this.options.foo exists then it will be bound to the name 'foo' in that scope and if it doesn't then foo is set to an empty array.
Here is an in-depths article for you to better understand ES6 Destructuring https://hacks.mozilla.org/2015/05/es6-in-depth-destructuring/
Destructuring assignment allows you to assign the properties of an
array or object to variables using syntax that looks similar to array
or object literals. This syntax can be extremely terse, while still
exhibiting more clarity than the traditional property access.
For your sample script the assignment are looking for an object with property "foo" from right side. If it can not find it, it will assign foo with default value as empty array.
If the left side is null or undefined, the operator will throw an error "Uncaught TypeError: Cannot destructure propertyfooof 'undefined' or 'null'."
// A regular JS object.
this.options = {
'a': 'a',
'foo': 'bar',
'z': 'z'
}
console.log('this.options.foo =', this.options.foo);
// this.options.foo === 'bar'
// Get 'foo' out of this.options. Becomes undefined if 'foo' doesn't exist in this.options.
const { foo } = this.options;
console.log('foo =', foo);
// foo === this.options.foo === 'bar';
const { nope } = this.options;
console.log('nope =', nope);
// typeof nope === 'undefined'
// Get 'zzz' if it exists, otherwise fall back to 1234.
const { zzz = 1234 } = this.options;
console.log('zzz =', zzz);
// zzz === 1234;
// zzz was set by its default fallback value.
I am trying to find a way to dynamically define a constant in Typescript but I'am starting to thing it's not possible.
I tried this :
define(name: string, value: any): boolean {
var undef;
const name = value;
return name == undef;
}
I am supposed to call:
define ('MY_CONST_NAME', 'foo_value);
I get the following error :
Duplicate 'name' identifier.
I think it's normal but i don't know how to achieve my goal.
In short... No. Const is block scoped. When declared it becomes available and not until then. If you want to declare something as immutable it's not that hard, but this question shows a lack of knowledge possibly. I think what you may find more useful is how to deep freeze an object so things can't be added to, taken off of, or changed in it. However it is shallow, so deep changes would be an issue unless you want to freeze it recursively(CAREFUL) or on a path
From the MDN:
var obj = {
prop: function() {},
foo: 'bar'
};
// New properties may be added, existing properties may be
// changed or removed
obj.foo = 'baz';
obj.lumpy = 'woof';
delete obj.prop;
// Both the object being passed as well as the returned
// object will be frozen. It is unnecessary to save the
// returned object in order to freeze the original.
var o = Object.freeze(obj);
o === obj; // true
Object.isFrozen(obj); // === true
// Now any changes will fail
obj.foo = 'quux'; // silently does nothing
// silently doesn't add the property
obj.quaxxor = 'the friendly duck';
// In strict mode such attempts will throw TypeErrors
function fail(){
'use strict';
obj.foo = 'sparky'; // throws a TypeError
delete obj.quaxxor; // throws a TypeError
obj.sparky = 'arf'; // throws a TypeError
}
fail();
// Attempted changes through Object.defineProperty;
// both statements below throw a TypeError.
Object.defineProperty(obj, 'ohai', { value: 17 });
Object.defineProperty(obj, 'foo', { value: 'eit' });
// It's also impossible to change the prototype
// both statements below will throw a TypeError.
Object.setPrototypeOf(obj, { x: 20 })
obj.__proto__ = { x: 20 }
This question made no sense but there is a workaround to achieve this using types:
type Dynamyc = Record<string, string>
const myDynamicsVars: Dynamyc = {}
myDynamicsVars.name = "toto"
console.log(myDynamicsVars.name)
In PHP we can define the argument value for the functions if it's not set(called), so for example:
<?php
function blah($arg = false){
var_dump($arg);
}
?>
In the above example if we call the function like:
<?php
blah();
// ==> out put will be: false;
blah(true);
// ==> out put will be: true;
?>
So we can define a value for the arguments if they are not settled while we call the function, how this could be achieved in javascript functions?
I have it exactly like PHP:
<script>
function blah(arg = false){
//...
}
</script>
The above code works just fine in Mozilla Firefox, but in Chrome, the function is not working and gets fixed when I remove = false in the parenthesis, Chrome developer tools says:
Uncaught Syntax Error: Unexpected token =
This is not possible in Javascript.
Try this Conditional Operator statement instead:
<script>
function blah(arg){
arg = typeof arg !== 'undefined' ? arg : 'someValue';
}
</script>
Where 'someValue' is the default value that the arg variable will get when there are no arguments passed to the blah() function.
This is the cleanest pattern for implementing default arguments in javascript IMO.
function (arg) {
arg = arg || 'defaultVale';
}
However this can fail if you expect the value of arg to be some falsy value, e.g 0, false, NaN, null using it is not really recommended.
This version protects against this case by explicitly comparing with undefined.
function (arg) {
arg = arg === undefined ? 'defaultVale' : arg;
// As T.J Crowder pointer out if not in strict mode or if the code will run
// in an IFrame use typeof arg === "undefined" instead of directly
// comparing with undefined
}
Another nice pattern is using objects for arguments instead. This has two benefits
Order of arguments is not important
It's easy to implement default arguments
Code
var defaults = {
arg1: 10,
arg2: 20
};
var f = function (args) {
args = jQuery.extend(true, args, defaults); //jQuery
args = _.defaults(args, defaults); // Underscore
};
f({
a: 25 //Use a non default value
});
In JavaScript there is no default parameter.
Just write the code like:
function test(arg) {
if(arg) {
// do something as arg is defined.
} else {
// do something as arg is undefined.
}
}
test(true); // arg is defined
test(); // arg is undefined
Simple variation
function defaulter(p1) {
p1 = p1 || "default";
}
In Js you can't have default values for parameters. You can check if the data is of a known type with typeof operator:
function blah(arg)
{
if (typeof arg === 'undefined')
{
arg = false;
}
}
or setting his value in a short-circuit way
function blah(arg)
{
arg = arg || false;
}
For example in coffeescript you can set it by using
blah = (arg = "mydefaultvalue") ->
that is translated into
blah = function(arg)
{
if (arg == null)
{
arg = "mydefaultvalue";
}
}
When using object constructors, properties can be directly assigned to the value of previously defined properties:
var foo = new (function() {
this.bar = 5;
this.baz = this.bar;
})();
alert(foo.baz) // 5
I would like to refer to a previously defined property within an OBJECT LITERAL:
var foo = {
bar : 5,
baz : bar
}
alert (foo.baz) // I want 5, but evaluates to undefined
I know that I could do this:
var foo = {
bar : 5,
baz : function() {
alert(this.bar); // 5
}
But I want to assign baz directly to a value rather than a function. Any ideas?
No, you won't be able to use any properties of the object literal before it has been created. Your closest option is probably to use a temporary variable like so:
var tmp = 5,
foo = {
bar : tmp,
baz : tmp
}
If you are free to use ECMAScript 5 features, you could write a getter function for the baz property that instead returns the value of bar:
var yourObject = {
bar: 5
};
Object.defineProperty(yourObject, 'baz', {
get: function () { return yourObject.bar; }
});
You can also just build a literal by parts:
var foo = {bar:5};
foo.baz = foo.bar;
If you need to fit this inside an expression (instead of through multiple statements) you can try abusing the comma operator or you can make a helper function:
(Warning: untested code)
function make_fancy_object(base_object, copies_to_make){
var i, copy_from, copy_to_list;
for(copy_from in copies_to_make){
if(copies_to_make.hasOwnProperty(copy_from)){
copy_to_list = copies_to_make[copy_from];
for(var i=0; i<copy_to_list.length; i++){
base_object[copy_to_list[i]] = base_object[copy_from];
}
}
}
}
var foo = make_fancy_object(
{bar: 5},
{bar: ["baz", "biv"]}
);
//foo.baz and foo.biv should be 5 now as well.
If I have the function:
function(foo, bar, baz);
And I want to allow for both named arguments and normal function calls, what is the best way of handling this? In php you can extract the variables into the local namespace but as far as I know the only way to handle this in javascript is by handling both scenarios separately. I've given a code example below:
function(foo, bar, baz)
{
if(typeof(foo) == 'object') // Named args
{
alert(foo.foo);
alert(foo.bar);
alert(foo.baz);
}
else
{
alert(foo);
alert(bar);
alert(baz);
}
}
myFunc('a', 'b', 'c');
myFunc({ foo: 'a', bar: 'b', baz: 'c' });
Any javascript gurus out there who can teach me the ways of javascriptFu?
Since you cannot access the local scope dynamically (without evil eval), you should consider the following approach:
var myFunc = function (foo, bar, baz) {
if (typeof(foo) === 'object') {
bar = foo.bar;
baz = foo.baz;
foo = foo.foo; // note: foo gets assigned after all other variables
}
alert(foo);
alert(bar);
alert(baz);
};
You simply translate the named args to regular variables manually. After that, your code will run for both cases without changes.
Do it with elegance:
var myFunc = (function (foo, bar, baz) {
// does whatever it is supposed to do
}).
withNamedArguments({foo:"default for foo", bar:"bar", baz:23 });
myFunc({foo:1}); // calls function(1, "bar", 23)
myFunc({}); // calls function("default for foo", "bar", 23);
myFunc({corrupt:1}); // calls function({corrupt:1})
myFunc([2,4], 1); //calls function([2,4], 1)
Even this one works
Array.prototype.slice =
Array.prototype.slice.withNamedArguments({start:0, length:undefined});
[1,2,3].slice({length:2}) //returns [1,2]
[1,2,3].slice(1,2) //returns [2,3]
... or here, parseInt()
parseInt = parseInt.withNamedArguments({str:undefined, base:10});
parseInt({str:"010"}); //returns 10
Just enhance the Function object:
Function.prototype.withNamedArguments = function( argumentList ) {
var actualFunction = this;
var idx=[];
var ids=[];
var argCount=0;
// construct index and ids lookup table
for ( var identifier in argumentList ){
idx[identifier] = argCount;
ids[argCount] = identifier;
argCount++;
}
return function( onlyArg ) {
var actualParams=[];
var namedArguments=false;
// determine call mode
if ( arguments.length == 1 && onlyArg instanceof Object ) {
namedArguments = true;
// assume named arguments at the moment
onlyArg = arguments[0];
for ( name in onlyArg )
if (name in argumentList ) {
actualParams[idx[name]] = onlyArg[name];
} else {
namedArguments = false;
break;
}
}
if ( namedArguments ) {
// fill in default values
for ( var i = 0; i < argCount; i++ ) {
if ( actualParams[i] === undefined )
actualParams[i] = argumentList[ids[i]];
}
} else
actualParams = arguments;
return actualFunction.apply( this, actualParams );
};
};
This is always awkward and not very rigourous but it's far safer to check the arguments for the absence of data than for a particular positive expectation, especially typeof on object.
Some variation on the below, the strategy here being to translate a DTO style input into a named argument style input (the opposite is also reasonable but I find less obvious). The advantage of this strategy is once you've passed this translation block, the rest of the code doesn't care how you got there.
// translate to named args - messy up front, cleaner to work with
function(foo, bar, baz)
{
// Opt 1: default to named arg, else try foo DTO
bar = (typeof(bar) != 'undefined' ? bar : foo.bar);
// Opt 2: default to named arg, else check if property of foo, else hard default (to null)
baz = (typeof(baz) != 'undefined' ? baz : typeof(foo.baz) != 'undefined' ? foo.baz : null);
// the first argument is always a problem to identify in itself
foo = (foo != null ? typeof(foo.foo) != 'undefined' ? foo.foo : foo : null);
}
// translate to object - cleaner up front, messier to work with
function(foo, bar, baz)
{
var input = (typeof(foo.foo) != 'undefined' ? foo : { 'foo' : foo, 'bar' : bar, 'baz' : baz });
}
The first arg (foo here) is always a problem because you expect it to be in one of two complex states (where the other args are always a single complex state or undefined) and you cannot process it until you've dealt with all the other args because obviously once you've changed it it's unreliable to use it for initialising anything else.