Related
I read this question about the "comma operator" in expressions (,) and the MDN docs about it, but I can't think of a scenario where it is useful.
So, when is the comma operator useful?
The following is probably not very useful as you don't write it yourself, but a minifier can shrink code using the comma operator. For example:
if(x){foo();return bar()}else{return 1}
would become:
return x?(foo(),bar()):1
The ? : operator can be used now, since the comma operator (to a certain extent) allows for two statements to be written as one statement.
This is useful in that it allows for some neat compression (39 -> 24 bytes here).
I'd like to stress the fact that the comma in var a, b is not the comma operator because it doesn't exist within an expression. The comma has a special meaning in var statements. a, b in an expression would be referring to the two variables and evaluate to b, which is not the case for var a, b.
The comma operator allows you to put multiple expressions in a place where one expression is expected. The resulting value of multiple expressions separate by a comma will be the value of the last comma separated expression.
I don't personally use it very often because there aren't that many situations where more than one expression is expected and there isn't a less confusing way to write the code than using the comma operator. One interesting possibility is at the end of a for loop when you want more than one variable to be incremented:
// j is initialized to some other value
// as the for loop executes both i and j are incremented
// because the comma operator allows two statements to be put in place of one
for (var i = 0; i < items.len; i++, j++) {
// loop code here that operates on items[i]
// and sometimes uses j to access a different array
}
Here you see that i++, j++ can be put in a place where one expression is allowed. In this particular case, the multiple expressions are used for side affects so it does not matter that the compound expressions takes on the value of the last one, but there are other cases where that might actually matter.
The Comma Operator is frequently useful when writing functional code in Javascript.
Consider this code I wrote for a SPA a while back which had something like the following
const actions = _.chain(options)
.pairs() // 1
.filter(selectActions) // 2
.map(createActionPromise) // 3
.reduce((state, pair) => (state[pair[0]] = pair[1], state), {}) // 4
.value();
This was a fairly complex, but real-world scenario. Bear with me while I explain what is happening, and in the process make the case for the Comma Operator.
This uses Underscore's chaining to
Take apart all of the options passed to this function using pairs
which will turn { a: 1, b: 2} into [['a', 1], ['b', 2]]
This array of property pairs is filtered by which ones are deemed to be 'actions' in the system.
Then the second index in the array is replaced with a function that returns a promise representing that action (using map)
Finally the call to reduce will merge each "property array" (['a', 1]) back into a final object.
The end result is a transformed version of the options argument, which contains only the appropriate keys and whose values are consumable by the calling function.
Looking at just
.reduce((state, pair) => (state[pair[0]] = pair[1], state), {})
You can see the reduce function starts with an empty state object, state, and for each pair representing a key and value, the function returns the same state object after adding a property to the object corresponding to the key/value pair. Because of ECMAScript 2015's arrow function syntax, the function body is an expression, and as a result, the Comma Operator allows a concise and useful "iteratee" function.
Personally I have come across numerous cases while writing Javascript in a more functional style with ECMAScript 2015 + Arrow Functions. Having said that, before encountering arrow functions (such as at the time of the writing of the question), I'd never used the comma operator in any deliberate way.
Another use for the comma operator is to hide results you don't care about in the repl or console, purely as a convenience.
For example, if you evaluate myVariable = aWholeLotOfText in the repl or console, it will print all the data you just assigned. This might be pages and pages, and if you'd prefer not to see it, you can instead evaluate myVariable = aWholeLotOfText, 'done', and the repl/console will just print 'done'.
Oriel correctly points out† that customized toString() or get() functions might even make this useful.
Comma operator is not specific to JavaScript, it is available in other languages like C and C++. As a binary operator this is useful when the first operand, which is generally an expression, has desired side effect required by second operand. One example from wikipedia:
i = a += 2, a + b;
Obviously you can write two different lines of codes, but using comma is another option and sometimes more readable.
I'd disagree with Flanagan, and say, that comma is really useful and allows to write more readable and elegant code, especially when you know what you're doing:
Here's the greatly detailed article on comma usage:
Several examples from out from there for the proof of demonstration:
function renderCurve() {
for(var a = 1, b = 10; a*b; a++, b--) {
console.log(new Array(a*b).join('*'));
}
}
A fibonacci generator:
for (
var i=2, r=[0,1];
i<15;
r.push(r[i-1] + r[i-2]), i++
);
// 0,1,1,2,3,5,8,13,21,34,55,89,144,233,377
Find first parent element, analogue of jQuery .parent() function:
function firstAncestor(el, tagName) {
while(el = el.parentNode, el && (el.tagName != tagName.toUpperCase()));
return el;
}
//element in http://ecma262-5.com/ELS5_HTML.htm
var a = $('Section_15.1.1.2');
firstAncestor(a, 'div'); //<div class="page">
I haven't found practical use of it other than that but here is one scenario in which James Padolsey nicely uses this technique for IE detection in a while loop:
var ie = (function(){
var undef,
v = 3,
div = document.createElement('div'),
all = div.getElementsByTagName('i');
while ( // <-- notice no while body here
div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->',
all[0]
);
return v > 4 ? v : undef;
}());
These two lines must to execute :
div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->',
all[0]
And inside comma operator, both are evaluated though one could have made them separate statements somehow.
There is something "odd" that can be done in JavaScript calling a function indirectly by using the comma operator.
There is a long description here:
Indirect function call in JavaScript
By using this syntax:
(function() {
"use strict";
var global = (function () { return this || (1,eval)("this"); })();
console.log('Global === window should be true: ', global === window);
var not_global = (function () { return this })();
console.log('not_global === window should be false: ', not_global === window);
}());
You can get access to the global variable because eval works differently when called directly vs called indirectly.
I've found the comma operator most useful when writing helpers like this.
const stopPropagation = event => (event.stopPropagation(), event);
const preventDefault = event => (event.preventDefault(), event);
const both = compose(stopPropagation, preventDefault);
You could replace the comma with either an || or &&, but then you'd need to know what the function returns.
More important than that, the comma separator communicates intent -- the code doesn't care what the left-operand evaluates to, whereas the alternatives may have another reason for being there. This in turn makes it easier to understand and refactor. If the function return type ever changes, the code above would not be affected.
Naturally you can achieve the same thing in other ways, but not as succinctly. If || and && found a place in common usage, so too can the comma operator.
One typical case I end up using it is during optional argument parsing. I think it makes it both more readable and more concise so that the argument parsing doesn't dominate the function body.
/**
* #param {string} [str]
* #param {object} [obj]
* #param {Date} [date]
*/
function f(str, obj, date) {
// handle optional arguments
if (typeof str !== "string") date = obj, obj = str, str = "default";
if (obj instanceof Date) date = obj, obj = {};
if (!(date instanceof Date)) date = new Date();
// ...
}
Let's say you have an array:
arr = [];
When you push onto that array, you are rarely interested in push's return value, namely the new length of the array, but rather the array itself:
arr.push('foo') // ['foo'] seems more interesting than 1
Using the comma operator, we can push onto the array, specify the array as the last operand to comma, and then use the result -- the array itself -- for a subsequent array method call, a sort of chaining:
(arr.push('bar'), arr.push('baz'), arr).sort(); // [ 'bar', 'baz', 'foo' ]
It saves you from using return in nested conditionals and it's very handy especially with the ternary operator. Such as;
function insert(v){
return this.node > v ? this.left.size < this.right.size ? ( this.left.insert(v)
, this
)
: ( this.left.insert(this.node)
, this.node = this.right.popmin()
, this.insert(v)
, this
)
: this.left.size < this.right.size ? ( this.right.insert(this.node)
, this.node = this.left.popmax()
, this.insert(v)
, this
)
: ( this.right.insert(v)
, this
)
}
I just came across this today looking at the proposals for pipeline operator proposal and partial application...
(https://github.com/tc39/proposal-pipeline-operator
(https://github.com/tc39/proposal-partial-application#hack-style-pipelines)
Also, Hack-style pipelines are already feasible without introducing new syntax today:
let $; // Hack-style topic variable
let result = (
$= books,
$= filter($, _ => _.title = "..."),
$= map($, _ => _.author),
$);
The use of comma expressions here can kind of fake the pipeline operator that isn't in the language yet.
Eliminating the space between $= simulates the feeling of a proper pipe token, |>. Note that the "topic" variable, $, can be anything here and that it's just shorthand for repeatedly overwriting the variable. So something more akin to ...
// blocking inside an IIFE
let result = (() => {
let $;
$ = books;
$ = filter($, _ => _.title = "..."),
$ = map($, _ => _.author),
return $;
})()
The "comma" version successfully cuts out some of the noise, getting you closer to what the proposal would be:
let result = books
|> filter($, _ => _.title = "..."
|> map($, _ => _.author)
Here's another example using it to compose functions:
const double = (x) => 2 * x;
const add = (x, y) => x + y;
const boundScore = (min, max, score) => Math.max(min, Math.min(max, score));
const calculateScore = ($) => (
$= double($),
$= add($, 20),
$= boundScore(0, 100, $),
(console.log($), $)
)
const score = calculateScore(28)
The comma operator (,) evaluates each of its operands (from left to right) and returns the value of the last operand. This lets you create a compound expression in which multiple expressions are evaluated, with the compound expression's final value being the value of the rightmost of its member expressions. This is commonly used to provide multiple parameters to a for loop.
let x = 1;
x = (x++, x);
console.log(x);
// expected output: 2
x = (2, 3);
console.log(x);
// expected output: 3
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator
Another area where comma operator can be used is Code Obfuscation.
Let's say a developper writes some code like this:
var foo = 'bar';
Now, she decides to obfuscate the code. The tool used may changed the code like this:
var Z0b=(45,87)>(195,3)?'bar':(54,65)>(1,0)?'':'baz';// Z0b == 'bar'
Demo: http://jsfiddle.net/uvDuE/
I read this line in Eloquent JavaScript and would love some clarity.
...braces have two meanings in JavaScript. At the start of a statement, they start a block fo statements. In any other position, they describe an object. Fortunately, it is almost never useful to start a statement with a brace object, and...
So, braces in let's say a..... an 'if statement' create a block of statements to execute, but braces that appear in let's say a function call (as a parameter) or in a variable assignment becomes an object literal.
Is that right? What are all the other cases? I'm not sure I understand the rule for when braces bundle up statements and for when they describe an object.
as object literals
var a = {field1:value1, field2:value2}
as function bodies
function func() {
// do something
}
var f = function() { /* do something */ };
var f = ()=>{}; // ditto
where the first item in the statement is an object literal
// {}.toString(); // syntax error, first brace of statement = code block
({}).toString(); // correct
as destructured assignment
var obj = {a:1, b:2, c:3};
var {a:x, c:y} = obj; // assign obj.a to x, and obj.c to y
({a:x, c:y} = obj); // ditto
Note - this has a lot of forms so I won't cover them all, full info found here (thanks RobG)
how this is interpreted
You can assume that all your JS code is inside some {} block. So the start of your code is immediately after a { always.
Wherever a value is expected, {} does not mean a function body. At the start of a statement this is ambiguous because you can have anonymous code blocks like so:
var x = 1;
{
var x = x+2;
// x = 3
}
// x = 3 (!)
This is archaic from C-style syntax where this would influence scope, but in testing this in JS it doesn't seem to have that effect, so for all intents it's rather useless syntax except to identify a code block. If you wanted such behavior you'd need to do this:
var x = 1;
(()=>{
var x = x+2;
// x = 3
})()
// x = 1
If we need an object first in some statement, we need to clarify to JS that we want a value. This is why we use ({}) instead of {}, because the former is unambiguously an object literal inside parens.
a simpler explanation
Rather than examine when {} is parsed as a value, let's look at when it isn't. There are two cases in general where we don't treat {} as an object literal: as a function body or as a statement group (my own term).
Consider the general control statements - if, for, while, with etc. These can all* be used in a way that completely avoids {}. In this respect {} should be thought of as statement groups hence the term.
if (x) x++; else x--;
if (x) {x++;} else {x--;}
{if (x) {x++;} else {x--;}}
*note: switch is an exception, switch(1); gives an error SyntaxError: missing { before switch body
Using this rule it then makes sense why we must use () to denote an object literal if it's the start of a statement - we can't start a statement in (), so you can't start a statement group there either, leaving only one option (object literal or related syntax).
This leaves function bodies.
function bodies
First, consider a function declaration statement:
function f () {}
It doesn't need a semicolon (;). This means the entire thing is a single statement. This explains why the following gives a syntax error in the first form but not the second:
function(){return 1;}(); // error: function statement requires name
var x = function(){return 1;}(); // fine
This is because the first is parsed as a statement, and a function declaration statement cannot be anonymous. However the second is in a value context and is treated as such. The situation is identical as with object literals, if it could be a statement it cannot be a value, but if we're already knee deep in value land, it has to be a value.
The => notation is, with one exception, parsed identically to function. The ()=>{} form is identical but in practice differs because this type of function cannot have a this object - it cannot be an object method (doesn't make much sense to) and it cannot construct new objects (it has no prototype), and other quirks as a result. otherwise it's straightforward to see how it's the same as function(){}.
()=>... however is a little different. It's treated as ()=>{return ...}. But, without the explicit } to finish the return statement, the syntax greedily captures the largest expression that would parse as such (not necessarily work). Case in point:
()=>1; // statement; = function that returns "1"
()=>1(); // statement; = function that returns "1()"
(()=>1()); // TypeError: 1 is not a function
(()=>1)(); // what was intended in above (you'd hope)
I have found this code snippet:
;
100% function($) { // WTF?
var _true_ = true; // WTF?
var _false_ = false; // WTF?
var go = function(location, date) {
location || (location = {});
var result = _false_;
if (date && date.day) {
result = geoService.go(location, date);
}
return !!result;
}
var process = function(func) {
var args = [].prototype.slice.call(arguments, 1);
return function() {
return func.apply(this, args);
}
}
// ...
}(jQuery, undefined);
In here: http://www.dofactory.com/products/javascript-jquery-design-pattern-framework
(sorry, no id-s have been found on the page)
I don't understand what these parts are doing:
the "100%" in the second line
the var _true_ = true; and var _false_ = false; assignments in the 3-4 lines
I'm curious, what is the purpose of these.
the "100%" in the second line
It's the number 100 followed by a modulus operator. It's not used for anything (since the result isn't captured) other than to force the right hand side to be treated as a function expression instead of a function declaration.
It's a very uncommon and unintuitive approach that I've never seen before.
It is more usual to wrap the function expression in parens or precede it with a not operator.
the var true = true; and var false = false; assignments in the 3-4 lines
The author appears to be trying to draw attention to the uses of true and false by copying them to variables that include non-alpha numerica characters in the name instead of using literals throughout. Again, this is very odd and not something I've ever seen before.
It looks like it is a collection of wrongly used "best practices" which not led to exceptions but definitely odd and obscured. Look at second and last lines. There is best practice used exactly vice versa:
(function ($, undefined){
// do the stuff
})(jQuery);
undefined here will be the real undefined because when function call there is no second argument. But what on Earth can be the reason pass the "undefined" argument to the function and do not use it? It looks like a prank.
The same thing is on 5 line: it looks (and actually acts) as "default argument" assigning but done in strange manner (traditionally and more obviously it used as location = location || {};). I beleive that only reasons to write it that way it done can be obfuscation, joke or misunderstanding.
The same thing is with 100%. You can use any operators to indicate function expression. The most common way is to use parenthesis. But often you can also meet:
!function(){
}();
or:
+function(){
}();
but you can also write
42 * function(){
}();
it all acts the same way only parenthesis are most obvious and common.
I'm still very new to JavaScript (not to coding though), so feel free to nit-pick and moron-map things for me.
I've attempted to create something that will accept user input. If the first character is an exclamation point, it'll attempt to create an object with that name and run the "action" method of that object. Else it'll treat it like regular text (for now an alert)
<script type="text/javascript">
function GetInput(input){
// Trim the spaces off the beginning and end
input = input.trim();
if(input.charAt(0) != "!"){
// Just some normal text
alert(input);
return true;
}
/* Cut off the exclamation point, replace multiple spaces with one,
* and get the arguments if any. args[0] = the command. */
var args = input.substr(1).replace(/\s{2,}/g, " ").split(" ");
// Make sure the function is an object with a method named "action"
if(eval("typeof "+args[0]+";") === "function"
&& eval("typeof "+args[0]+".prototype.action;") === "function"){
eval("var command = new "+args[0]+"();");
command.action(args);
}else{
alert('"'+args[0]+'" is not a command.');
}
return true;
}
</script>
The only problem I'm noticing with this so far is the eval statements. I know I could use a switch/case and ditch eval all together, or even make an array containing the names of the allowed functions and compare the input with that array prior to eval, but I'm sure there's got to be a better way.
I just want to be able to make the object and method and not update anything (which I believe is one of the major uses of of duck-typing?). Is this possible without eval? If not, is there an easy way to sanitize input for strings to avoid things like "!eval(alert('u b haxed'))" or "!a;alert('u b haxed')"?
Thanks in advance
You should use eval only once to get the function, then do everything with it in a variable.
var args = input.substr(1).split(/\s+/);
var fn = eval(args[0]);
if (typeof fn == 'function' && typeof fn.prototype.action == 'function') {
var command = new fn();
command.action(args);
} else {
alert('"'+args[0]+'" could not be evaluated to a valid command.');
}
return true;
If those constructors are global variables, you also can access them as a property of the window object:
var fn = window[ args[0] ];
I read this question about the "comma operator" in expressions (,) and the MDN docs about it, but I can't think of a scenario where it is useful.
So, when is the comma operator useful?
The following is probably not very useful as you don't write it yourself, but a minifier can shrink code using the comma operator. For example:
if(x){foo();return bar()}else{return 1}
would become:
return x?(foo(),bar()):1
The ? : operator can be used now, since the comma operator (to a certain extent) allows for two statements to be written as one statement.
This is useful in that it allows for some neat compression (39 -> 24 bytes here).
I'd like to stress the fact that the comma in var a, b is not the comma operator because it doesn't exist within an expression. The comma has a special meaning in var statements. a, b in an expression would be referring to the two variables and evaluate to b, which is not the case for var a, b.
The comma operator allows you to put multiple expressions in a place where one expression is expected. The resulting value of multiple expressions separate by a comma will be the value of the last comma separated expression.
I don't personally use it very often because there aren't that many situations where more than one expression is expected and there isn't a less confusing way to write the code than using the comma operator. One interesting possibility is at the end of a for loop when you want more than one variable to be incremented:
// j is initialized to some other value
// as the for loop executes both i and j are incremented
// because the comma operator allows two statements to be put in place of one
for (var i = 0; i < items.len; i++, j++) {
// loop code here that operates on items[i]
// and sometimes uses j to access a different array
}
Here you see that i++, j++ can be put in a place where one expression is allowed. In this particular case, the multiple expressions are used for side affects so it does not matter that the compound expressions takes on the value of the last one, but there are other cases where that might actually matter.
The Comma Operator is frequently useful when writing functional code in Javascript.
Consider this code I wrote for a SPA a while back which had something like the following
const actions = _.chain(options)
.pairs() // 1
.filter(selectActions) // 2
.map(createActionPromise) // 3
.reduce((state, pair) => (state[pair[0]] = pair[1], state), {}) // 4
.value();
This was a fairly complex, but real-world scenario. Bear with me while I explain what is happening, and in the process make the case for the Comma Operator.
This uses Underscore's chaining to
Take apart all of the options passed to this function using pairs
which will turn { a: 1, b: 2} into [['a', 1], ['b', 2]]
This array of property pairs is filtered by which ones are deemed to be 'actions' in the system.
Then the second index in the array is replaced with a function that returns a promise representing that action (using map)
Finally the call to reduce will merge each "property array" (['a', 1]) back into a final object.
The end result is a transformed version of the options argument, which contains only the appropriate keys and whose values are consumable by the calling function.
Looking at just
.reduce((state, pair) => (state[pair[0]] = pair[1], state), {})
You can see the reduce function starts with an empty state object, state, and for each pair representing a key and value, the function returns the same state object after adding a property to the object corresponding to the key/value pair. Because of ECMAScript 2015's arrow function syntax, the function body is an expression, and as a result, the Comma Operator allows a concise and useful "iteratee" function.
Personally I have come across numerous cases while writing Javascript in a more functional style with ECMAScript 2015 + Arrow Functions. Having said that, before encountering arrow functions (such as at the time of the writing of the question), I'd never used the comma operator in any deliberate way.
Another use for the comma operator is to hide results you don't care about in the repl or console, purely as a convenience.
For example, if you evaluate myVariable = aWholeLotOfText in the repl or console, it will print all the data you just assigned. This might be pages and pages, and if you'd prefer not to see it, you can instead evaluate myVariable = aWholeLotOfText, 'done', and the repl/console will just print 'done'.
Oriel correctly points out† that customized toString() or get() functions might even make this useful.
Comma operator is not specific to JavaScript, it is available in other languages like C and C++. As a binary operator this is useful when the first operand, which is generally an expression, has desired side effect required by second operand. One example from wikipedia:
i = a += 2, a + b;
Obviously you can write two different lines of codes, but using comma is another option and sometimes more readable.
I'd disagree with Flanagan, and say, that comma is really useful and allows to write more readable and elegant code, especially when you know what you're doing:
Here's the greatly detailed article on comma usage:
Several examples from out from there for the proof of demonstration:
function renderCurve() {
for(var a = 1, b = 10; a*b; a++, b--) {
console.log(new Array(a*b).join('*'));
}
}
A fibonacci generator:
for (
var i=2, r=[0,1];
i<15;
r.push(r[i-1] + r[i-2]), i++
);
// 0,1,1,2,3,5,8,13,21,34,55,89,144,233,377
Find first parent element, analogue of jQuery .parent() function:
function firstAncestor(el, tagName) {
while(el = el.parentNode, el && (el.tagName != tagName.toUpperCase()));
return el;
}
//element in http://ecma262-5.com/ELS5_HTML.htm
var a = $('Section_15.1.1.2');
firstAncestor(a, 'div'); //<div class="page">
I haven't found practical use of it other than that but here is one scenario in which James Padolsey nicely uses this technique for IE detection in a while loop:
var ie = (function(){
var undef,
v = 3,
div = document.createElement('div'),
all = div.getElementsByTagName('i');
while ( // <-- notice no while body here
div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->',
all[0]
);
return v > 4 ? v : undef;
}());
These two lines must to execute :
div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->',
all[0]
And inside comma operator, both are evaluated though one could have made them separate statements somehow.
There is something "odd" that can be done in JavaScript calling a function indirectly by using the comma operator.
There is a long description here:
Indirect function call in JavaScript
By using this syntax:
(function() {
"use strict";
var global = (function () { return this || (1,eval)("this"); })();
console.log('Global === window should be true: ', global === window);
var not_global = (function () { return this })();
console.log('not_global === window should be false: ', not_global === window);
}());
You can get access to the global variable because eval works differently when called directly vs called indirectly.
I've found the comma operator most useful when writing helpers like this.
const stopPropagation = event => (event.stopPropagation(), event);
const preventDefault = event => (event.preventDefault(), event);
const both = compose(stopPropagation, preventDefault);
You could replace the comma with either an || or &&, but then you'd need to know what the function returns.
More important than that, the comma separator communicates intent -- the code doesn't care what the left-operand evaluates to, whereas the alternatives may have another reason for being there. This in turn makes it easier to understand and refactor. If the function return type ever changes, the code above would not be affected.
Naturally you can achieve the same thing in other ways, but not as succinctly. If || and && found a place in common usage, so too can the comma operator.
One typical case I end up using it is during optional argument parsing. I think it makes it both more readable and more concise so that the argument parsing doesn't dominate the function body.
/**
* #param {string} [str]
* #param {object} [obj]
* #param {Date} [date]
*/
function f(str, obj, date) {
// handle optional arguments
if (typeof str !== "string") date = obj, obj = str, str = "default";
if (obj instanceof Date) date = obj, obj = {};
if (!(date instanceof Date)) date = new Date();
// ...
}
Let's say you have an array:
arr = [];
When you push onto that array, you are rarely interested in push's return value, namely the new length of the array, but rather the array itself:
arr.push('foo') // ['foo'] seems more interesting than 1
Using the comma operator, we can push onto the array, specify the array as the last operand to comma, and then use the result -- the array itself -- for a subsequent array method call, a sort of chaining:
(arr.push('bar'), arr.push('baz'), arr).sort(); // [ 'bar', 'baz', 'foo' ]
It saves you from using return in nested conditionals and it's very handy especially with the ternary operator. Such as;
function insert(v){
return this.node > v ? this.left.size < this.right.size ? ( this.left.insert(v)
, this
)
: ( this.left.insert(this.node)
, this.node = this.right.popmin()
, this.insert(v)
, this
)
: this.left.size < this.right.size ? ( this.right.insert(this.node)
, this.node = this.left.popmax()
, this.insert(v)
, this
)
: ( this.right.insert(v)
, this
)
}
I just came across this today looking at the proposals for pipeline operator proposal and partial application...
(https://github.com/tc39/proposal-pipeline-operator
(https://github.com/tc39/proposal-partial-application#hack-style-pipelines)
Also, Hack-style pipelines are already feasible without introducing new syntax today:
let $; // Hack-style topic variable
let result = (
$= books,
$= filter($, _ => _.title = "..."),
$= map($, _ => _.author),
$);
The use of comma expressions here can kind of fake the pipeline operator that isn't in the language yet.
Eliminating the space between $= simulates the feeling of a proper pipe token, |>. Note that the "topic" variable, $, can be anything here and that it's just shorthand for repeatedly overwriting the variable. So something more akin to ...
// blocking inside an IIFE
let result = (() => {
let $;
$ = books;
$ = filter($, _ => _.title = "..."),
$ = map($, _ => _.author),
return $;
})()
The "comma" version successfully cuts out some of the noise, getting you closer to what the proposal would be:
let result = books
|> filter($, _ => _.title = "..."
|> map($, _ => _.author)
Here's another example using it to compose functions:
const double = (x) => 2 * x;
const add = (x, y) => x + y;
const boundScore = (min, max, score) => Math.max(min, Math.min(max, score));
const calculateScore = ($) => (
$= double($),
$= add($, 20),
$= boundScore(0, 100, $),
(console.log($), $)
)
const score = calculateScore(28)
The comma operator (,) evaluates each of its operands (from left to right) and returns the value of the last operand. This lets you create a compound expression in which multiple expressions are evaluated, with the compound expression's final value being the value of the rightmost of its member expressions. This is commonly used to provide multiple parameters to a for loop.
let x = 1;
x = (x++, x);
console.log(x);
// expected output: 2
x = (2, 3);
console.log(x);
// expected output: 3
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator
Another area where comma operator can be used is Code Obfuscation.
Let's say a developper writes some code like this:
var foo = 'bar';
Now, she decides to obfuscate the code. The tool used may changed the code like this:
var Z0b=(45,87)>(195,3)?'bar':(54,65)>(1,0)?'':'baz';// Z0b == 'bar'
Demo: http://jsfiddle.net/uvDuE/