Why does JSHint dislike ternaries for method calls on objects? - javascript

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.)

Related

why a newline between parentheses of two function calls not treat as two statements in js?

why this kind of bad design is made on js? Is there any special reason to design the automatic semicolon insertion like this?
Here is my code, It is not work in js in chrome:
(function(){console.log("abc");})()
(function(){console.log("123");})();
Here is the error:
Uncaught TypeError: (intermediate value)(...) is not a function
I know the right version of this code is :
(function(){console.log("abc");})();
(function(){console.log("123");})();
I just want to know why js syntax is designed so stupid. History reason?
I also add this question as a warning to everybody try to use the automatic semicolon insertion of javascript, please just add ; everywhere it needs, the automatic semicolon insertion of javascript is rubbish. It does not work as your expect.
The exist answer is too complex to my case, so I ask a new one:
https://stackoverflow.com/a/2846298/1586797
Another looks good but not work case 2:
x=1
(function(){console.log("123");})()
The linked question's answers explain the spec's three rules for ASI, for example this one. tl;dr:
If it doesn't work, try with semicolon.
The program should end with a semicolon.
If a statement says "can't put newline here", punish it with semicolon.
Your code does not satisfy any of the criteria.
The first line could return a function, and if so that function should be allowed to be invoked; so ( that the second line begins with is not illegal
The first line is not the last line of the program
There is no restricted syntax here.
Therefore, no automatic semicolon for you.
Some people have thus claimed that while (f(){})() syntax is good IIFE syntax, it might be good to do !f(){}() instead:
!function(){console.log("abc");}()
!function(){console.log("123");}();
This works as intended because ! just negates the (discarded) result of the function application, and also because ! as purely unary operator is an illegal character to continue the first line (i.e. f(){}()! is not a thing). This triggers rule 1, and ASI can take place.
The counterargument is that it is butt-ugly (i.e. for anyone not already familiar with the practice, it takes a while for them to understand the purpose of ! in this idiom).
Your second example is similar in nature: as far as the JS parser is concerned, 1 is a value (the fact that it is an integer and could not possibly be a function is a bit lost to it). Look at this example that syntactically is completely equivalent to yours:
a=function(f) { console.log("A CALLED!"); return f; }
x=a
(function(){console.log("123");})()
# => A CALLED!
123
Here, a is a function, so it can be invoked with function(){console.log("123");} as an argument; it returns function(){console.log("123");} unchanged after printing to the console; then () invokes that return value, and 123 is printed as well. Everything works. Thus, Rule #1 is not triggered, no semicolon for you.
(function(){console.log("abc");})()
(function(){console.log("123");})();
is equivalent to:
(function(){console.log("abc");})()(function(){console.log("123");})();
And is what is usually referred to as function currying.
For IIFEs (immediately invoked function expressions) you need to end with ;
For more on function currying see this post. Obviously your console log functions do not work as currying functions, but the syntax yourFunction(a)(b)(c) is a cool feature of the language that is used for currying.
Your code can be simplified as:
(function(){})()()();
This code will get same error.
The () expect a expression to call.
The first () call the (function(){}), the second () call the (function(){})()'s result, but the result is not a function, so it's wrong.
Without the semicolon those statements are chained. You call the first function and give the second func as argument to the return value of the first one. This could actually work, if the first function had a function as return value.
When you expand the code it becomes more obvious:
var a = function(){console.log("abc");};
var b = function(){console.log("123");}
(a)()
(b)();
the last two lines become:
(a)()(b)();
this is equivalent to
var x = a();
x(b);
since a does not return anything, it cannot call it as function with b as argument.
I think this will be clearer if you use this:
x=1
(function(){console.log("123");})()
The error states 1 is not a function. Makes it clear that (function...) is treated like the argument of a function call to 1:
x=1(function(){console.log("123");})()
Because ()() is self-invoking function and ();() are two differnt functions and interpreter is interpreting it accordingly.
Here two pieces of code are totally different for an interpreter.
(function(){console.log("abc");})()(function(){console.log("123");})();
and
(function(){console.log("abc");})();(function(){console.log("123");})();
however, this code will work fine
var a=12
var b=10
console.log(a+b)
Long answer sort :
(function(){console.log("abc");})()
is trying to immediately-invoke the preceding expression which is
(function(){console.log("123");})();
Which would be the return value of the preceding IIFE.
IIFEs can act differently in the missing semicolon situation. That is why we see code as follows:
;(function() {
console.log('abc');
})()
Please have a look of details description here : https://gist.github.com/khellang/8518964

Trying to shorten a conditional statement in javascript

What I want to do here is use the fewest bytes to call function f only if the variable a is not null. The function n shown will work fine, but according to my web browser, function m won't work. The browser states:
expected expression, got ';'
I then proceeded to take out the colon, figuring maybe that would do the trick, but I get the error "syntax error".
Is there a way I can shorten function n to a smaller size and have it still work or should I just stick with function n?
function g(){}
function f(){}
function m(){a?f()g():;}
function n(){if(a){f();g()}}
Code golfing?
Fore!
*ahem*
You could use && for the conditional, and + to call them both together.
function m(){a&&f()+g()}
Total bytes: 24
Note: If by chance f or g returns an object, any valueOf methods will be called, which could have side-effects, but in this context I think that's unlikely.
You can do some tricks with the comma operator:
function m(){a?(f(),g()):…}
and of course the conditional operator always needs a third operand, you cannot just omit it, but use any (short) value:
function m(){a?(f(),g()):0}
You can work around that restriction by using the short-circuit behaviour of the AND operator (see What is "x && foo()"?):
function m(){a&&(f(),g())}
But don't do that. It makes your code totally unreadable.
Should I just stick with function n?
Yes, definitely.
function m() {
if (a) {
f();
g();
}
}
If you want to minify that, use one of the established minifiers instead of hand-optimising it.

Question mark syntax from CoffeeScript without CoffeeScript

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.

Why is object with duplicated property accepted in JavaScript?

I was expecting JavaScript to reject objects with duplicated properties as invalid but it accepts them in some cases.
{"a":4,"a":5} results in an SyntaxError at least in Firefox and Chrome which seems obvious due to the property a being defined twice.
However ({"a":4,"a":5}) evaluates just fine and results in an object {"a":5} in both Firefox and Chrome.
Why is the expression with the parenthesis accepted?
Summing up the responses: The first example is simply not the construction of an object but a block of labeled statements. Duplicated properities in objects are perfectly valid in which case the last definition wins.
Thanks a lot for your answers!
It is perfectly legal in ECMAScript 3 to declare duplicate properties in an object literal; the SyntaxError you get probably comes from the fact that you used an object literal as a statement, which is not possible due to the confusion with block statements ({ doSomething(); }).
If you want this to be reported as an error, you may want to switch to ECMAScript 5's strict mode: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/Strict_mode.
What you state has no problem if you assign it to a variable, if you don't however, you get the error you mention. Which makes all the difference from a syntax point of view.
When you wrap any structure in parens you are causing that syntax to be evaluated as an expression, the result of which is stored as a temporary variable. The error I get when not doing so in Firefox is unexpected label or invalid label, so it seems without assignment, or parens, this object construction is not treated as an object construction - instead it is treated as a block with multiple label statements that are defined illegally:
{
a: function(){
alert('a');
},
b: function(){
alert('b');
}
}
The above should be totally acceptable as an object, however you get a similar error if you evaluate it without assinging it to some form of variable, or evaluating it with parens. Put simply the duplication of the attribute name is not causing the error :)
Basically imagine your first example, but like this:
function (){
"a": 4,
"b": 5
}
That is roughly how these browsers are treating it, which is now obviously illegal javascript syntax... whereas it wasn't so obvious before.
In the first notation (parentheses-less) the javascript syntax is ambiguous. From ecmascript specification:
An ExpressionStatement cannot start with an opening curly brace
because that might make it ambiguous with a Block.
A block basically evaluates all the statements inside, equivalent to evaluating "a":4,"a":5 which is not valid JS and, in fact, returns the same SyntaxError Unexpected token :
Wrapping that code in parentheses (or, rather, a grouping operator) removes that ambiguity since an assignment expression cannot be followed by a block statement:
var test = {"a":"a","a":"b"}; //test.a === "b"
Furthermore this ambiguity can be removed by any operator or expression that cannot be used with a block statement. A practical scenario hardly comes to mind, maybe if you wanted to return an object literal as part of a conditional operator?
//this *could* come out of a JS minifier:
return x ? (foo(),{"a":"b"}) : (bar(), {"b":"a"});
Why should it not be accepted? You're simply overwriting the values. I think it's rather a feature, than an error. And it works fine for me on various browsers: http://jsbin.com/oculon/1/edit
It's like writing
var a;
a = 4;
a = 5;
alert(a);
it's not Error you just overwrite value with another
I'm guessing (though not certain) that this evaluates as an error because of the difference between the way Firefox and Chrome's JS parsers treat statements and expressions. So because it's wrapped in parentheses the second time, it's considered an expression. Since it's looking for less information in an expression, it can ignore erroneous values. You'll see that if you do...
var a = {'a': 5, 'a': 4};
console.log(a);
It works fine! And also notice that here it's in the right hand side of the statement, giving a hint that it's an expression.

Reference to Originating Conditional Value within a Javascript IF Statement

I often find that I write IF statements which immediately reference the value of the conditional statement. For example, let's say I need to check to see if a string matches a pattern:
if (mystring.match(/mypattern/) {
var mymatch = mystring.match(/mypattern/)[1];
...
};
I suspect that what I'm looking for doesn't exist, but I've wondered whether you can reference the conditional statement's value within the if block, the way you can reference "arguments" within a function. In many cases, of course, I can rewrite it like this:
var mymatch = mystring.match(/mypattern/)[1];
if (mymatch) { ... };
But that's often not possible if there's a series of methods called. For example:
var mymatch = $('.myclass')[0].text().match(/mypattern/)[1];
... that would throw an exception if there were no item [0] on which to call .text(). Is there some convenient shorthand I'm missing out on? Or a better way to organize things? Just curious, really — I'll go on living if the answer is no.
In cases where relevant you can use the fact that the assignment operator returns a value in JavaScript, so for instance you can write things like:
if (assignedTest = testedValue) {
//value of assignedTest is now available
//and conditional will only be executed if true
This could be used if the RHS was compatible or properly set-up but it's also a huge readability concern since it's very easy to confuse the assignment = with comparison ==/===.
If you were particularly motivated to pursue this you could extract this type of functionality into a function that would behave in a reliable way: such as assigning the result of a closure to a named variable, and you could further tune the behavior to do other things (such as optionally evaluating to a different value within the test). Ultimately it would primarily be making a simple structure more complex though.

Categories