{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# JavaScript syntax" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Single-line expressions\n", "\n", "* Multiple variable declaration and assignment\n", "* `for` loops with only an initialization block\n", "* `while` loops with no incrementor/decrementor statement\n", "* Conditional statements using the ternary operator\n", "* Evaluation using the comma operator and `||` and `&&` operators\n", "* Evaluate and return\n", "* ES6 arrow functions\n", "\n", "References: [1](http://stackoverflow.com/questions/9579546/comma-operator-where-it-can-really-be-useful), [2](https://javascriptweblog.wordpress.com/2011/04/04/the-javascript-comma-operator/)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Variable declaration and assignment" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is not the comma **operator**, but the use of a **comma separator** in variable assignment statements:" ] }, { "cell_type": "code", "execution_count": 83, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 2 undefined undefined\n" ] }, { "data": { "text/plain": [ "undefined" ] }, "execution_count": 83, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(function() {\n", " var a = 1, b = 2, c, d;\n", " console.log(a, b, c, d); // => 1 2 undefined undefined\n", "})();" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Multiple declaration and assignment using one expression:" ] }, { "cell_type": "code", "execution_count": 85, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 1 undefined\n" ] }, { "data": { "text/plain": [ "undefined" ] }, "execution_count": 85, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(function() {\n", " var a = b = 1, c;\n", " console.log(a, b, c); // => 1 1 undefined\n", "})();" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Declare, evaluate and assign using the comma operator" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2\n", "2 2\n", "3 3\n", "4 5\n" ] }, { "data": { "text/plain": [ "undefined" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "var a = (1, 2); // Only final value is assigned\n", "console.log(a); // => 2\n", "\n", "var a = 1;\n", "var b = (a++, 2); // All expressions are evaluated, final value is assigned\n", "console.log(a, b); // => 2 2\n", "\n", "b = (a++, 3);\n", "console.log(a, b); // => 3 3\n", "\n", "b = (a++, a + 1); // Increment a by 1, then assign a + 1 to b\n", "console.log(a, b); // => 4 5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Embedding a debugging expression:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 2\n", "2 3\n" ] }, { "data": { "text/plain": [ "undefined" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "var a = 1, b = 2;\n", "b = (console.log(a, b), a++, a + 1); // => 1 2\n", "console.log(a, b); // => 2 3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Resetting an iterator:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2\n", "4\n" ] }, { "data": { "text/plain": [ "undefined" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "var arr = [1, 2, 3, 4];\n", "for (var i = 0, len = arr.length; i < len; i++) console.log(arr[i++, i]);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### The `||` and `&&` operators" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Like the comma operator, the `||` and `&&` operators also return only the final value assigned. But `||` only evaluates the second expression if the first is falsey, while `&&` only evaluates the second expression if the first is truthy: " ] }, { "cell_type": "code", "execution_count": 111, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2 2 3\n", "1 2 1\n", "1 2 3\n", "1 2 3\n", "1 2 false\n" ] }, { "data": { "text/plain": [ "undefined" ] }, "execution_count": 111, "metadata": {}, "output_type": "execute_result" } ], "source": [ "var a = 1, b = 2;\n", "\n", "/* `a += 1` is evaluated but its value is discarded;\n", " `c` is assigned the value of `b + 1` */\n", "var c = (a += 1, b + 1);\n", "console.log(a, b, c); // => 2 2 3\n", "\n", "var a = 1, b = 2;\n", "\n", "/* `a` is evaluated as truthy and evaluation stops;\n", " `c` is assigned the value of `a` */\n", "var c = (a || b + 1);\n", "console.log(a, b, c); // => 1 2 1\n", "\n", "var a = 1, b = 2;\n", "\n", "/* `a` is evaluated as falsey and evaluation continues;\n", " `c` is assigned the value of `b + 1` */\n", "var c = (a === 0 || b + 1);\n", "console.log(a, b, c); // => 1 2 3\n", "\n", "var a = 1, b = 2;\n", "\n", "/* `a` is evaluated as truthy and evaluation continues;\n", " `c` is assigned the value of `b + 1` */\n", "var c = (a === 1 && b + 1);\n", "console.log(a, b, c); // => 1 2 3\n", "\n", "var a = 1, b = 2;\n", "\n", "/* `a` is evaluated as falsey and evaluation stops;\n", " `c` is assigned the value 'false' */\n", "var c = (a === 0 && b + 1);\n", "console.log(a, b, c); // => 1 2 false" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `while` and `for` loops" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`i--` alone can be used as a `while` loop iterator expression, without needing a separate incrementor or decrementor statement, because **as long as the incrementor value is not modified anywhere else in the loop**, the expression will not produce an infinite loop but will terminate at (and including) zero:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3\n", "2\n", "1\n", "0\n" ] }, { "data": { "text/plain": [ "undefined" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "var i = 4; while (i--) console.log(i);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using the comma operator to chain **expressions** (which might otherwise be written as separate **statements**) on one line:" ] }, { "cell_type": "code", "execution_count": 122, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3\n", "2\n", "1\n", "[ 3, 2, 1 ]\n", "2\n", "1\n", "0\n", "[ 2, 1, 0 ]\n" ] }, { "data": { "text/plain": [ "undefined" ] }, "execution_count": 122, "metadata": {}, "output_type": "execute_result" } ], "source": [ "var arr = [];\n", "for (var i = 3; i > 0; i--) console.log(i), arr.push(i);\n", "console.log(arr);\n", "\n", "arr = [];\n", "var i = 3; while (i--) console.log(i), arr.push(i);\n", "console.log(arr);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Parentheses are not needed in the preceding code, but they help make the syntax clearer:" ] }, { "cell_type": "code", "execution_count": 123, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3\n", "2\n", "1\n", "[ 3, 2, 1 ]\n", "2\n", "1\n", "0\n", "[ 2, 1, 0 ]\n" ] }, { "data": { "text/plain": [ "undefined" ] }, "execution_count": 123, "metadata": {}, "output_type": "execute_result" } ], "source": [ "var arr = [];\n", "\n", "// parens enclosing expressions joined by comma operator\n", "for (var i = 3; i > 0; i--) (console.log(i), arr.push(i));\n", "\n", "console.log(arr);\n", "\n", "arr = [];\n", "\n", "// parens enclosing expressions joined by comma operator\n", "var i = 3; while (i--) (console.log(i), arr.push(i));\n", "\n", "console.log(arr);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using the comma operator to increment more than one iterator value in the initialization block of a loop:" ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 1\n", "1 2\n", "2 3\n" ] }, { "data": { "text/plain": [ "undefined" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "for (var i = 0, j = 1; i < 3; i++, j++) {\n", " console.log(i, j);\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using the comma operator to express more than one condition in a loop:" ] }, { "cell_type": "code", "execution_count": 68, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "5\n", "4\n" ] }, { "data": { "text/plain": [ "4" ] }, "execution_count": 68, "metadata": {}, "output_type": "execute_result" } ], "source": [ "var n = 5, x = 1;\n", "while (n > x, n !== 3) console.log(n), n--;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Another example, with no loop body statements. This is a good way to embed debugging expressions." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "4\n", "3\n", "2\n", "1\n", "0\n" ] }, { "data": { "text/plain": [ "undefined" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "var i = 4;\n", "while (console.log(i), i--);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using the comma operator to include more than one iteration statement in a loop:" ] }, { "cell_type": "code", "execution_count": 117, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]\n" ] }, { "data": { "text/plain": [ "undefined" ] }, "execution_count": 117, "metadata": {}, "output_type": "execute_result" } ], "source": [ "var arr = [];\n", "for (var i = 0; i < 10; arr.push(i), i++); // Notice, no block following loop\n", "console.log(arr); // => [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Grouping parentheses are not required in the preceding code, but they help make the syntax clearer:" ] }, { "cell_type": "code", "execution_count": 119, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]\n" ] }, { "data": { "text/plain": [ "undefined" ] }, "execution_count": 119, "metadata": {}, "output_type": "execute_result" } ], "source": [ "var arr = [];\n", "for (var i = 0; i < 10; (arr.push(i), i++));\n", "console.log(arr); // => [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Conditional statements" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "true\n" ] }, { "data": { "text/plain": [ "undefined" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "function foo() {\n", " if (1 === 1) return true;\n", "}\n", "\n", "console.log(foo()); // => true" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### The ternary operator" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "equal\n", "equal\n", "equal\n" ] }, { "data": { "text/plain": [ "undefined" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "console.log((1 === 1) ? 'equal' : 'unequal'); // => 'equal'\n", "\n", "var foo = (1 === 1) ? 'equal' : 'unequal';\n", "console.log(foo);// => 'equal'\n", "\n", "function foo() {return (1 === 1) ? 'equal' : 'unequal'};\n", "console.log(foo); // => 'equal'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using the comma operator (here we need parentheses to group the expressions):" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "equal\n", "[ 'equal' ]\n" ] }, { "data": { "text/plain": [ "undefined" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "var arr = [];\n", "console.log((1 === 1) ? (arr.push('equal'), 'equal') : 'unequal');\n", "console.log(arr);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Example of concision using the comma operator in a ternary expression (from [The JavaScript Comma Operator](https://javascriptweblog.wordpress.com/2011/04/04/the-javascript-comma-operator/)):" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "// player loses\n", "lives ? (lives--, go()) : (gameOver(), exit());" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Syntax contrasts" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "// `if...else`, clearly demarcated blocks\n", "function evenOrOdd(int) {\n", " if (int % 2 === 0) {\n", " return 'Even';\n", " } else {\n", " return 'Odd';\n", " }\n", "}\n", "\n", "// Single-line `if` statement, implicit `else`\n", "function evenOrOdd(int) {\n", " if (int % 2 === 0) return 'Even';\n", " return 'Odd';\n", "}\n", "\n", "// Ternary conditional operator\n", "function evenOrOdd(int) {\n", " return (int % 2 === 0) ? 'Even' : 'Odd';\n", "}\n", "\n", "// Using falsiness of zero\n", "function evenOrOdd(int) {\n", " return (int % 2) ? 'Even' : 'Odd';\n", "}\n", "\n", "// ES6 arrow function\n", "var evenOrOdd = int => (int % 2) ? 'Even' : 'Odd';" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Multiple-line statements using ternary operator" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Foo is me\n" ] }, { "data": { "text/plain": [ "undefined" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "function foo() {\n", " var foo = ('foo' === 'foo')\n", " ? 'I am foo, I am really really foo'\n", " : 'I am not foo';\n", " return (foo === 'I am foo, I am really really foo')\n", " ? 'Foo is me'\n", " : 'No foo';\n", "}\n", "\n", "console.log(foo()); // => 'Foo is me'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Return statements" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Returning a Boolean value" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "These two return statements are equivalent:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "true true\n" ] }, { "data": { "text/plain": [ "undefined" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "function foo() {return (1 === 1) ? true : false;}\n", "function bar() {return (1 === 1);}\n", "\n", "console.log(foo(), bar()); // => true true" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Returning a second value if the first is undefined:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "foo\n" ] }, { "data": { "text/plain": [ "undefined" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(function() {\n", " function foo() {\n", " var bar;\n", " return bar || 'foo';\n", " }\n", " console.log(foo());\n", "})();" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Evaluate and return, using comma operator" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All expressions in the return statement are evaluated, but only the final expression is returned:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[]\n", "foo\n", "[ 'foo' ]\n" ] }, { "data": { "text/plain": [ "undefined" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "var arr = [];\n", "\n", "function foo() {\n", " var str = 'foo';\n", " return (arr.push(str), str);\n", "}\n", "\n", "console.log(arr); // []\n", "console.log(foo()); // 'foo' <- return value\n", "console.log(arr); // ['foo'] <- result of evaluating `arr.push(str)`" ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3\n" ] }, { "data": { "text/plain": [ "undefined" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "function foo(n) {\n", " return n + 1;\n", "}\n", "\n", "function bar(n) {\n", " return n++, foo(n);\n", "}\n", "\n", "console.log(bar(1)); // => 3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Q: When you return only a variable assignment expression, you are producing a side effect *and* returning the evaluation of that expression?" ] }, { "cell_type": "code", "execution_count": 61, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1\n", "1\n" ] }, { "data": { "text/plain": [ "undefined" ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "var a = 0;\n", "\n", "function foo() {return a = 1;}\n", "\n", "console.log(foo());\n", "console.log(a);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Returning more than one value" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To return more than one value, return an array or object:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[ 1, 2 ]\n" ] }, { "data": { "text/plain": [ "undefined" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "function foo() {return [1, 2]};\n", "console.log(foo());" ] } ], "metadata": { "kernelspec": { "display_name": "Javascript (Node.js)", "language": "javascript", "name": "javascript" }, "language_info": { "file_extension": "js", "mimetype": "application/javascript", "name": "javascript", "version": "0.12.4" } }, "nbformat": 4, "nbformat_minor": 0 }