CoffeeScript has such syntax sugar:
item.getFoo?().fooParam?.bar
Which translates into long javascript equivalent with getFoo==null and fooParam==null checks. The question is: are there any ways to use this syntax in vanilla javascript with a library/translator/compiler other than CoffeeScript? We use Traceur in our project, but it doesn't have such syntax because it is not ES6 compliant (although I wish it to). Maybe some way to implement it within Traceur fork?
If you don't want the exact CoffeeScript semantics, you can cheat a bit:
return item.getFoo ? (item.getFoo().fooParam || {}).bar : undefined;
There are a few tricks going on here.
The ternary operator is used to test the truthiness of item.getFoo
If fooParam is missing, falsey, or absent, we substitute it with an empty object. CoffeeScript would have bailed out here.
We return the value of bar regardless of whether it exists. If it does exist, you get the value you want. If it doesn't exist but fooParam is set, you get undefined. If it doesn't exist because fooParam was undefined and we fell back to {}, you still get undefined.
You can write some helpers if the ternary operator gets in the way:
function defaultObject(input) { // A helper to put somewhere
return input || {};
}
return defaultObject((item.getFoo || defaultObject)().fooParam).bar;
This is even trickier: defaultObject will return {} when called with getFoo, so you don't need a ternary operator around the function call. If fooParam isn't truthy, defaultObject will return another empty object, eliminating the need for another ||. If fooParam is truthy, defaultObject behaves like the identity function and returns it.
I'm sure this could be golfed further down, but I'd recommend avoiding this pattern. Anyone reading your code will be fairly confused and blame you for making a mess in the codebase.
I had this same question recently, and I came here hoping for a better solution than my current one. If you're doing this frequently, it's easier to make a function to do it for you:
var qm = function(arg) {
if (arg instanceof Object) return arg;
return function(){};
};
Then to use it, you wrap your objects in it to make sure no error is raised. It starts to look ugly if there are many question marks on a line
qm(qm(item.getFoo)().fooParam).bar
The optional chaining operator ?. was introduced in ES2020.
obj.val?.prop
obj.val?.[expr]
obj.arr?.[index]
obj.func?.(args)
It is supported by the browsers of 91.81% of internet users as of 29 November 2021 according to https://caniuse.com/mdn-javascript_operators_optional_chaining.
Related
This question already has answers here:
Null-safe property access (and conditional assignment) in ES6/2015
(11 answers)
Closed 2 years ago.
I've been programming a lot in Swift recently. Today I did some work in JavaScipt when question popped up to me:
Is there something similar to optional chaining in JavaScript? A way to prevent undefined is not an object without any variables?
Example:
function test(){
if(new Date() % 2){
return {value: function(){/*code*/}};
}
}
test().value();
will fail half of time because sometimes test returns undefined.
The only solution I can think of is a function:
function oc(object, key){
if(object){
return object[key]();
}
}
oc(test(), 'value');
I would like to be able to do something like:
test()?.value()
The part after the question mark is only executed if test returned an object.
But this is not very elegeant. Is there something better? A magic combination of operators?
Edit I know I could rewrite test to return something. But I'm wondering if there's something like optional chaining. I'm not interested in a particular solution to the above example. Something that I also can use if have no control over the function returning undefined.
This is currently a Stage 4 proposal you can check on the progress of it here:
https://github.com/tc39/proposal-optional-chaining
You can use the babel plugin today:
https://www.npmjs.com/package/babel-plugin-transform-optional-chaining
Update 11th January 2020:
Babel now supports optional chaining by default
https://babeljs.io/blog/2020/01/11/7.8.0
The Optional Chaining operator is spelled ?.. It may appear in three positions:
obj?.prop // optional static property access
obj?.[expr] // optional dynamic property access
func?.(...args) // optional function or method call
Notes:
In order to allow foo?.3:0 to be parsed as foo ? .3 : 0 (as required for backward compatibility), a simple lookahead is added at the level of the lexical grammar, so that the sequence of characters ?. is not interpreted as a single token in that situation (the ?. token must not be immediately followed by a decimal digit).
Also worth checking out:
https://github.com/tc39/proposal-nullish-coalescing
https://github.com/babel/babel/tree/master/packages/babel-plugin-proposal-nullish-coalescing-operator
In plain JavaScript you have to do type checks or structure your code so that you know an object will exist.
CoffeeScript, a language that compiles down to JavaScript, provides an existential operator ?. for safe chaining if you're willing to consider a preprocessed language.
There's another discussion here about why you can't reproduce this behavior in JS.
There is also a discussion on the ESDiscuss forums about adding an existential operator to a future version of JavaScript. It doesn't seem very far along though, certainly nowhere close to practical use. More of an idea at this point.
Optional chaining has landed in JS. We can use optional chaining via the ?. operator in object property access. It allows us to try accessing properties of objects which might not exists (i.e. are undefined) without throwing an error.
Here is a code example:
const obj = {
foo: {
bar: 1
}
};
// If we try to access property which doesn't exists
// it just returns undefined
console.log(obj.baz);
try {
// Now we try to access a property of undefined which throws an error
obj.baz.foz;
} catch (e) {
console.dir(e.message);
}
// Now we use the optional chaining operator ?.
// We get undefined instead of an error
console.log(obj.baz?.foz);
console.log(obj.foo?.bar);
You can use
test() && test().value();
or
var testResult = test();
testResult && testResult.value();
If you ask me this is most similar to Swift's optional chaining.
var Obj = {Prop: {name: 'peter'}}
console.log(Obj.Prop.name)
console.log(Obj?.Prop?.name)
In the first sentence, you're just accessing object properties. The problem with that is that if you find Prop to be something other than an object, it will throw an exception. That's the reason of the optional chainig operator.
Lets say you try to do Obj.Prop2.name.
You'll get Uncaught TypeError: Cannot read property 'name' of undefined
if Instead you did Obj.Prop2?.name, You'll only receive undefined as a value, instead of an exception.
This is particularly useful when accessing deeply nested properties.
WARNING: This is a relatively new JS feature that's not yet implemented in all browsers, so be careful while using it for production applications.
Optional Chaining is finally in the JavaScript standard!
Here are a few examples:
// properties
foo?.bar
foo?.bar()
foo?.bar.baz()
foo?.bar?.baz()
// indexing
foo?.[0]
foo?.['bar']
// check if a function is defined before invoking
foo?.()
foo.bar?.()
foo?.bar?.()
And this is way better than what most people use for manually checking for nulls
Instead of evaluating
foo?.bar
to this little code snippet we are all used to writing
foo ? foo.bar : null
it actually evaluates to
foo == null ? undefined : foo.bar
which works for all the falsey values like an empty string, 0 or false.
Unrelated to the question, but you might also be interested in the ?? operator.
It has a similar purpose as || except it only checks for null or undefined.
For example:
foo ?? bar
would be the same as:
foo != null ? foo : bar
This is a very new feature, so even thought a lot of users already use a browser that supports this you will still want to use a tool to convert it to an older version of javascript.
What about returning a noop function that does nothing when the condition isn't met?
function test(){
if(new Date() % 2){
return {value: function(){/*code*/}};
}
return {value: function(){ /* just return a type consistent with the function above */ }
}
You can always return this; if test is a object method.
But why do you want to prevent such errors by creating mock functions?
This question already has answers here:
Null-safe property access (and conditional assignment) in ES6/2015
(11 answers)
Closed 2 years ago.
I've been programming a lot in Swift recently. Today I did some work in JavaScipt when question popped up to me:
Is there something similar to optional chaining in JavaScript? A way to prevent undefined is not an object without any variables?
Example:
function test(){
if(new Date() % 2){
return {value: function(){/*code*/}};
}
}
test().value();
will fail half of time because sometimes test returns undefined.
The only solution I can think of is a function:
function oc(object, key){
if(object){
return object[key]();
}
}
oc(test(), 'value');
I would like to be able to do something like:
test()?.value()
The part after the question mark is only executed if test returned an object.
But this is not very elegeant. Is there something better? A magic combination of operators?
Edit I know I could rewrite test to return something. But I'm wondering if there's something like optional chaining. I'm not interested in a particular solution to the above example. Something that I also can use if have no control over the function returning undefined.
This is currently a Stage 4 proposal you can check on the progress of it here:
https://github.com/tc39/proposal-optional-chaining
You can use the babel plugin today:
https://www.npmjs.com/package/babel-plugin-transform-optional-chaining
Update 11th January 2020:
Babel now supports optional chaining by default
https://babeljs.io/blog/2020/01/11/7.8.0
The Optional Chaining operator is spelled ?.. It may appear in three positions:
obj?.prop // optional static property access
obj?.[expr] // optional dynamic property access
func?.(...args) // optional function or method call
Notes:
In order to allow foo?.3:0 to be parsed as foo ? .3 : 0 (as required for backward compatibility), a simple lookahead is added at the level of the lexical grammar, so that the sequence of characters ?. is not interpreted as a single token in that situation (the ?. token must not be immediately followed by a decimal digit).
Also worth checking out:
https://github.com/tc39/proposal-nullish-coalescing
https://github.com/babel/babel/tree/master/packages/babel-plugin-proposal-nullish-coalescing-operator
In plain JavaScript you have to do type checks or structure your code so that you know an object will exist.
CoffeeScript, a language that compiles down to JavaScript, provides an existential operator ?. for safe chaining if you're willing to consider a preprocessed language.
There's another discussion here about why you can't reproduce this behavior in JS.
There is also a discussion on the ESDiscuss forums about adding an existential operator to a future version of JavaScript. It doesn't seem very far along though, certainly nowhere close to practical use. More of an idea at this point.
Optional chaining has landed in JS. We can use optional chaining via the ?. operator in object property access. It allows us to try accessing properties of objects which might not exists (i.e. are undefined) without throwing an error.
Here is a code example:
const obj = {
foo: {
bar: 1
}
};
// If we try to access property which doesn't exists
// it just returns undefined
console.log(obj.baz);
try {
// Now we try to access a property of undefined which throws an error
obj.baz.foz;
} catch (e) {
console.dir(e.message);
}
// Now we use the optional chaining operator ?.
// We get undefined instead of an error
console.log(obj.baz?.foz);
console.log(obj.foo?.bar);
You can use
test() && test().value();
or
var testResult = test();
testResult && testResult.value();
If you ask me this is most similar to Swift's optional chaining.
var Obj = {Prop: {name: 'peter'}}
console.log(Obj.Prop.name)
console.log(Obj?.Prop?.name)
In the first sentence, you're just accessing object properties. The problem with that is that if you find Prop to be something other than an object, it will throw an exception. That's the reason of the optional chainig operator.
Lets say you try to do Obj.Prop2.name.
You'll get Uncaught TypeError: Cannot read property 'name' of undefined
if Instead you did Obj.Prop2?.name, You'll only receive undefined as a value, instead of an exception.
This is particularly useful when accessing deeply nested properties.
WARNING: This is a relatively new JS feature that's not yet implemented in all browsers, so be careful while using it for production applications.
Optional Chaining is finally in the JavaScript standard!
Here are a few examples:
// properties
foo?.bar
foo?.bar()
foo?.bar.baz()
foo?.bar?.baz()
// indexing
foo?.[0]
foo?.['bar']
// check if a function is defined before invoking
foo?.()
foo.bar?.()
foo?.bar?.()
And this is way better than what most people use for manually checking for nulls
Instead of evaluating
foo?.bar
to this little code snippet we are all used to writing
foo ? foo.bar : null
it actually evaluates to
foo == null ? undefined : foo.bar
which works for all the falsey values like an empty string, 0 or false.
Unrelated to the question, but you might also be interested in the ?? operator.
It has a similar purpose as || except it only checks for null or undefined.
For example:
foo ?? bar
would be the same as:
foo != null ? foo : bar
This is a very new feature, so even thought a lot of users already use a browser that supports this you will still want to use a tool to convert it to an older version of javascript.
What about returning a noop function that does nothing when the condition isn't met?
function test(){
if(new Date() % 2){
return {value: function(){/*code*/}};
}
return {value: function(){ /* just return a type consistent with the function above */ }
}
You can always return this; if test is a object method.
But why do you want to prevent such errors by creating mock functions?
JSHint give the following error:
Expected an assignment or function call and instead saw an expression.
For the following line of code:
(aFunctionOrNull) ? aFunctionOrNull() : someObject.someMethod();
It highlights the final ) on someMethod so I assume the error is there. The code works and JSHint doesn't have a problem when I change it to if () {} else {} syntax. I don't mind the longer syntax but I'd like to learn why JSHint says this and if this is a bad practice.
The biggest piece of confusion may come from the terminology. Is someObject.someMethod() not a function call?
Well, in general it's considered bad practice to call a function using the ternary operator(s), without assigning the return value (which is what you seem to be doing).Also, it could be worth checking what JSHint has to say about the following code:
(aFunctionOrNull || someObject.someMethod)();
If aFunctionOrNull is undefined (or null, or falsy), the logical-or-bit will cause the expression to evaluate to someObject.someMethod, and the resulting value of that is invoked (a reference to a function object, hopefully). This gives you the opportunity to write your code more "fail-safe" without the bulk of a nested ternary:
(aFunctionOrNull || someObject.someMethod || function(){})();
The grouped expression is now bound to evaluate to a truthy value, so no errors are thrown there.
To avoid JSHint nagging about your not doing anything with the return value, either assign it to a variable (which I don't really like doing), or add a little operator to the mix:
~(aFunctionOrNull || someObject.someMethod || function(){})();//bitwise not
!(aFunctionOrNull || someObject.someMethod || function(){})();//logical not, doesn't really matter which one
On your last question: someObject.someMethod is indeed a function call. More specifically, it's a call to a function object in the someObject's context.
For those who don't know this: JS functions are objects, and the called context is either explicitly set using the bind method (defined on the Function.prototype) or ad-hoc:
var referenceToMethod = someObject.someMethod;
referenceToMethod();//<-- inside the function objects, this now points to the global object
An easy way to think of it is that JS functions just float around aimlessly in memory/space/time, until they are called via a reference, the context of that reference is then passed to the function object, to determine what object it'll interact with. This is, sadly, the global object by default, or null in strict mode.
JSHint says about expressions, or expr:
This option suppresses warnings about the use of expressions where
normally you would expect to see assignments or function calls. Most
of the time, such code is a typo. However, it is not forbidden by the
spec and that's why this warning is optional.
While JSLint says:
An expression statement is expected to be an assignment or a
function/method call or delete. All other expression statements are
considered to be errors.
AFAIK, there's no problem in doing what you're doing only that it will issue a warning because it would expect you to use an if..else statement, but you can turn this off in JSHint with:
/*jshint expr:true */
There error is because a ternary is an expression. You could use it to set a variable:
var result = a ? b : c;
Notice that the ternary evaluates to either b or c. It's an expression.
That said, the warning (I believe) comes from the notion that ternaries suffer poorer readability than an if...else block. The code above can be rewritten
var result;
if (a) {
result = b;
} else {
result = c;
}
Which is easier to read than a ternary. JSHint does as much to promote readable code as it does valid code. If you're comfortable including these expressions in your code, go ahead and disable the warnings for expressions. (It's what I would do.)
I've just been looking at the _.isEqual function of Underscore.js and a section of the code goes something like this:
if (a === b) return true;
if (typeof a !== typeof b) return false;
if (a == b) return true;
I'm just wondering if there's any case where the third statement could be reached and evaluate to true?
Edit: Just to be clear, this isn't my own code I'm talking about, I'm reading the source of Underscore, in particular, this line and I was curious about why they're doing that.
I've just been browsing through the Underscore repo and came across a short discussion where someone asked the exact same thing, and it looks like it is actually unnecessary.
Following the algorithm defined by the ECMAScript Language Specification in section 11.9.6 and section 11.9.3 seems to show that no pair of values should return true in the above case.
So, in short, no, that situation is not possible.
The only situation where == and === react unexpectedly is when comparing a literal string ("123") to a constructed string (new String("123")), which would fail the first test.
However, on the second test it gets caught because the constructed string has the type object, but the literal has the type string.
Based on that, I'd say no, the third statement can never be reached, and evaluate to true.
When you use the == operator and the expressions are of different types, JavaScript will generally convert the two into the same type before comparing.
For example, this can happen with null and undefined. null == undefined is true, even though null === undefined is false. However typeof null is "object", while typeof undefined is "undefined". So, in this case you should return false on the second statement.
You can read all the details in the spec (section 11.9.3), it is very involved:
http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf
My initial guess was that it was to work around a broken browser implementation.
However after digging into the git logs for that file it looks like the corresponding line was in the very first underscore.js checkin. (I'm not gonna hunt in the parent documentcloud core.js repo...) You can see it at line 334 of https://github.com/documentcloud/underscore/commit/02ede85b539a89a44a71ce098f09a9553a3a6890 .
So now my guess is that its just cruft that got left in, never completely tested and never cleaned out.
I'm working on a script that accepts a settings object, but uses default values where settings are not provided.
I just wrote the following line of CoffeeScript:
iframe_width = settings?.iframe?.width? ? $iframe_body.width()
My intent, in plain English, is:
If the settings object is defined, and it defines a property iframe, and the iframe property defines a property width, then assign the value of the latter property to the variable iframe_width. Otherwise, assign the value returned by calling $iframe_body.width().
Does my CoffeeScript code reflect my intent, and is it the most effective way to express that? It seems awkward with all of the existential operators (?), so I wanted to put it out there for some feedback (the compiled JavaScript is very terse and cryptic, so it's hard to tell if should work as intended).
Also, I'm not sure whether there's any redundancy in using both the standard existential operator (?) and its accessor variant (?.) together.
Thanks!
Update:
The code above doesn't seem to work as expected; however, this does:
iframe_width = settings?.iframe?.width ? $iframe_body.width()
That makes sense, since I don't think the former code actually accesses the width property, but rather just checks for its existence (twice, even?). In this code, I removed the ? just after the width property, since I think that's redundant with the ? operator between the two expressions. Does that seem correct?
(Note: This answer was written before the question was updated. As the questioner realized, foo? ? bar isn't just redundant—it actually won't work, because foo? evaluates to true or false, and is therefore always non-null.)
You have two good options for simplifying this: One, you could replace the existential binary operator with an or:
iframe_width = settings?.iframe?.width? or $iframe_body.width()
Two, you could ditch the ? after width—it's redundant:
iframe_width = settings?.iframe?.width ? $iframe_body.width()
Now, you could do both if and only if iframe.width will never be 0 (since 0 or x is x). If you can be sure of that, go ahead and make it
iframe_width = settings?.iframe?.width or $iframe_body.width()