DEV Community

Cover image for All you need to know about Javascript's Expressions, Statements and Expression Statements
Promise Tochi
Promise Tochi

Posted on

JS Expressions All you need to know about Javascript's Expressions, Statements and Expression Statements

By the end of this article, you should be able to describe in detail how the code in the image below works and why it works.

Example code Object statement expression screentshot

There are two major syntactic categories in Javascript:

  1. Statements
  2. Expressions

It is important to make this distinction because expressions can act like statements, which is why we also have Expression statements. Though, on other the hand, statements cannot act like expressions.

EXPRESSIONS

Expressions produce value

Expressions are Javascript code snippets that result in a single value. Expressions can be as long as you want them to be, but they would always result in a single value.



2 + 2 * 3 / 2

(Math.random() * (100 - 20)) + 20

functionCall()

window.history ? useHistory() : noHistoryFallback()

1+1, 2+2, 3+3

declaredVariable

true && functionCall()

true && declaredVariable


Enter fullscreen mode Exit fullscreen mode

All of the above are expressions, and can appear anywhere Javascript expects a value. So that the argument to console.log below, resolves to a single value, that is logged to the console.



console.log(true && 2 * 9) // 18


Enter fullscreen mode Exit fullscreen mode

Expressions don’t necessarily change state

For example,



const assignedVariable = 2; //this is a statement, assignedVariable is state

assignedVariable + 4 // expression

assignedVariable * 10 // expression

assignedVariable - 10 // expression

console.log(assignedVariable) // 2


Enter fullscreen mode Exit fullscreen mode

Despite all the expressions in the snippet above, assignedVariable’s value is still 2. So why the necessarily in the heading for this section, it’s because function calls are expressions but a function can contain statements that change state. So foo() in itself is an expression, that either returns undefined or some other value, but if foo was written as



const foo = foo () => {
  assignedVariable = 14
}


Enter fullscreen mode Exit fullscreen mode

then, even though its call is an expression, its call has also resulted in a state change. So a better way to rewrite the foo function and statement would be:



const foo = foo () => {
  return 14 //explicit return for readability
}
assignedVariable = foo()


Enter fullscreen mode Exit fullscreen mode

or even better



const foo = foo (n) => {
  return n//explicit return for readability
}
assignedVariable = foo(14)


Enter fullscreen mode Exit fullscreen mode

This way your code is more readable, composable, and there is a clear distinction and separation between expression and statements. This a fundamental of functional and declarative Javascript.

STATEMENTS

Statements are the headache of functional programming 😄. Basically, statements perform actions, they do things.

In javascript, statements can never be used where a value is expected. So they cannot be used as function arguments, right-hand side of assignments, operators operand, return values…



foo(if () {return 2}) //js engine mind = blown


Enter fullscreen mode Exit fullscreen mode

These are all javascript statements:

  1. if
  2. if-else
  3. while
  4. do-while
  5. for
  6. switch
  7. for-in
  8. with (deprecated)
  9. debugger
  10. variable declaration

If you type the snippet below in your browser’s console and hit enter



if (true) {9+9}


Enter fullscreen mode Exit fullscreen mode

you will see that it returns 18 but despite that you cannot use it as an expression or where Javascript expects a value. It is weird because you’d expect statements not to return anything, since the return value is pretty much useless if you cannot use it. That’s Javascript for you, weird.

Function declarations, Function expressions and Named Function expressions

A function declaration is a statement



function foo (func) {
  return func.name
}


Enter fullscreen mode Exit fullscreen mode

A function expression is an expression, what you call an anonymous function



console.log(foo(function () {} )) // ""


Enter fullscreen mode Exit fullscreen mode

A named function expression is an expression, like an anonymous function, but it has a name



console.log(foo(function myName () {} )) // "myName"


Enter fullscreen mode Exit fullscreen mode

The distinction between function as an expression and function as a declaration boils down to understanding this:
whenever you declare a function where Javascript is expecting a value, it will attempt to treat it as a value, if it can’t use it as a value, an error will be thrown.
Whereas declaring a function at the global level of a script, module, or top level of a block statement (that is, where it is not expecting a value), will result in a function declaration.

Examples:



if () {
  function foo () {} // top level of block, declaration
}

function foo () {} //global level, declaration

function foo () {
  function bar() {} //top level of block, declaration
}

function foo () {
  return function bar () {} // named function expression
}

foo(function () {}) // anonymous function expression

function foo () {
  return function bar () {
    function baz () {} // top level of block, declaration
  }
}

function () {} // SyntaxError: function statement requires a name

if (true){
  function () {} //SyntaxError: function statement requires a name
}


Enter fullscreen mode Exit fullscreen mode

Converting Expressions to Statements: Expression Statements

Is anything ever simple and straightforward with Javascript 😃



2+2; //expression statement
foo(); //expression statement


Enter fullscreen mode Exit fullscreen mode

You can convert expressions to expression statement, just by adding a semi-colon to the end of the line or allowing automatic semi-colon insertion to do the work. 2+2 itself is an expression but the complete line is a statement.



2+2 // on its own is an opposition

foo(2+2) //so you can use it anywhere a value is expected

true ? 2+2 : 1 + 1

function foo () {return 2+2}


2+2; //expression statement
foo(2+2;) //syntaxError


Enter fullscreen mode Exit fullscreen mode

Semi-colon vs Comma operator

With semi-colon, you can keep multiple statements on the same line



const a; function foo () {}; const b = 2


Enter fullscreen mode Exit fullscreen mode

The comma operator allows you to chain multiple expression, returning only the last expression



console.log( (1+2,3,4) ) //4

console.log( (2, 9/3, function () {}) ) // function (){}

console.log( (3, true ? 2+2 : 1+1) ) // 4


Enter fullscreen mode Exit fullscreen mode

Sidenote: one way to tell the Javascript engine to expect a value is via parentheses, (), without the parentheses, each expression will be treated as an argument to console.log.



function foo () {return 1, 2, 3, 4}
foo() //4


Enter fullscreen mode Exit fullscreen mode

All the expressions will be evaluated from left to right, and the last one will be returned.

IIFEs (Immediately Invoked Function Expressions)

An anonymous function can be an expression, if we use it where Javascript is expecting a value, that means we if we can tell Javascript to expect a value with parentheses, we can pass an anonymous function as that value.



function () {}


Enter fullscreen mode Exit fullscreen mode

So while the snippet above is invalid, the snippet below is valid



(function () {}) // this returns function () {}


Enter fullscreen mode Exit fullscreen mode

If putting a anonymous function inside a parentheses immediately returns the same anonymous function, that means we can call it straight away, like this:



(function () {
  //do something
})()


Enter fullscreen mode Exit fullscreen mode

So, these are possible



(function () {
  console.log("immediately invoke anonymous function call")
})() // "immediately invoke anonymous function call"

(function () {
  return 3
})() // 3

console.log((function () {
  return 3
})()) // 3

//you can also pass an argument to it
(function (a) {
  return a
})("I'm an argument") // I'm an argument


Enter fullscreen mode Exit fullscreen mode

Object literals vs Block Statements

Sidenote: this is valid Javascript



r: 2+2 // valid

foo()

const foo = () => {}


Enter fullscreen mode Exit fullscreen mode

The above are sequence of statements in the global scope that will be parsed as valid Javascript and executed. The r is what you’ll call a label, and they are mostly useful in breaking loops. Example:



loop: {
  for (const i = 0; i < 2; i++) {
    for (const n = 0; n <2; n++) {
      break loop //breaks outer loop and stops entire loop
    }
  }
}


Enter fullscreen mode Exit fullscreen mode

You can prepend a label to any expression or expression statement, note that you not are creating a variable lab by doing this:



lab: function a () {}
console.log(lab) //ReferenceError: lab is not defined


Enter fullscreen mode Exit fullscreen mode

Curly braces, {}, allow you to group expression statements and statements. So you can write,



{var a = "b"; func(); 2+2} // 4


Enter fullscreen mode Exit fullscreen mode

If you paste the above in your browsers console, it will return 4 and when you do console.log(a), you will get string b. You can call that a block statement, which is different from the object literal you might be used to.



console.log({a: 'b'}) // {a: 'b'}

console.log({var a = "b", func(), 2+2}) // SyntaxError

const obj = {var a = "b", func(), 2+2} // SyntaxError


Enter fullscreen mode Exit fullscreen mode

You cannot use a block statement as a value or expression, because console.log is a function, it cannot accept a statement as an argument. It can accept an object literal though.Â
I hope you understood all I explained above, cause the next snippet below might throw you off.



{} + 1 //1

{2} + 2 // 2

{2+2} + 3 // 3

{2+2} -3 // -3



Enter fullscreen mode Exit fullscreen mode

You might expect it to throw either a syntax error or to return 1, 4, 7 respectively. Remember statements aren’t supposed to return anything because they can’t be used as values. So Javascript rather throwing an error, attempts to convert the operands of the + operator to a number or string, if it can’t then it throws. So whatever is returned by the block statement, is implicitly coerced to 0 used as the operand.

Whew, if you read all the way, you are the real MVP. That probably is all you need to know about Expressions, Statements, and Expression Statements.

Top comments (15)

Collapse
 
sebastiansimon profile image
Sebastian Simon • Edited

Expressions produce a value

There are a lot of misconceptions about what an expression really is. An expression is not actually required to produce a value. Either that, or, at the very least, it’s debatable.

For example:

Is new Array(-1) an expression? Sure looks like it. It throws a RangeError.

JSON.parse("x") is an expression, right? It throws a SyntaxError.

These errors can be caught with trycatch.

What about (() => {await x})? It’s a function expression wrapping another expression with await. All the parts are expressions (can you argue otherwise?). But, because await is not used top-level in an async function, it throws a SyntaxError in any context during parsing (so cannot be caught). Similarly: (function({}){"use strict";}).

This opens the question what kind of semantics are even applicable for the term “expression”. Is an expression a runtime thing? A syntax thing? Is it a thing before parsing? Or after parsing?

The ECMAScript specification, interestingly, doesn’t even define what an “expression” is in natural language. Instead, it provides grammar productions for “Expression”, defining it inductively, starting with the comma operator. It moves the question of what an “Expression” is further and further into less abstract questions, until it reaches concrete terminal symbols. For example an “Expression” is either an “AssignmentExpression” or an “Expression”, followed by a ,, followed by another “AssignmentExpression”. Then, similarly “AssignmentExpression” is defined, and so on.

Whether something is an expression cannot be determined without the full context (as demonstrated in the article with blocks vs. objects) and without building an AST (e.g. with AST explorer) (JS currently has no way of reflecting its own AST). On a mental level it’s an “I know it when I see it” kind of thing.


You will see that it returns 18 but despite that you cannot use it as an expression or where JavaScript expects a value. It is weird because you’d expect statements not to return anything, since the return value is pretty much useless if you cannot use it. That’s JavaScript for you, weird.

That is… not how things work. That statement does not return anything. It’s not a “weird” thing of JavaScript that a value is shown. This is the output of a JavaScript REPL. This output isn’t a return value, it’s the value from the completion record produced by the given code snippet. This completion record can only be accessed by a REPL, and only its value and (indirectly) its state can be accessed with the eval function, but only after its completion (e.g. eval("if(true){4;}") === 4, and if it doesn’t throw its state is “normal”). Also see Why does this dowhile loop repeat the last value after the end?.


So whatever is returned by the block statement, is implicitly coerced to 0 used as the operand.

No. There is no second operand on the left, there is no return value of the block, there is no 0. This is neither a binary + nor a binary -. It’s unary + and unary -.

Collapse
 
dudleycraig profile image
Dudley Craig • Edited

this article looked to be answering some queries i've had, tho got stopped at this ...
const foo = foo () => {
assignedVariable = 14
}
what does that mean? it doesn't compile ... is it an attempt to be a "named expression"? I'd assumed the arrow syntax is only for anonymous expressions?

again here, what's this? ... "foo(function () {} );"?

a bit ambiguous for me.

Collapse
 
paxfeline profile image
David Newberry

The code has one too many "foo"s, it should be:

const foo = () => {
assignedVariable = 14
}

() => { ... } is an anonymous function, which is then assigned to const foo.

The other code:
foo(function () {} );
also uses an anonymous function (using different syntax). This code would mean call the function "foo" and pass an anonymous (and empty) function.

Collapse
 
hayola profile image
Ayoola Moore

Hey - In quote - "A function declaration is a statement," I think a function declaration is an expression rather than a statement. Kindly refer to 3.2 of this article - 2ality.com/2012/09/expressions-vs-...

Also, with reference to your article here.
foo(if () {return 2})

the above code wouldn't work because the "if" within foo is a statement.

Look at this-
console.log(function(){}); // return the function. If function declaration is a statement, that would be invalid. As an expression is expected and I quote- "A value or string that can be used with replaceable parameters in the main message." ref: docs.microsoft.com/en-us/previous-...

Please let me know your thought. I'm just mastering every bit of Javascript. Thanks.

Collapse
 
aydinmuminkorcan profile image
Mümin Korcan Aydın

That was simply the best article about js expressions and statements I ve ever read , thank you

Collapse
 
promisetochi profile image
Promise Tochi

Thank you :)

Collapse
 
guid75 profile image
Guid75

Again, another reason why I deeply hate this language.

Thanks you.

Collapse
 
promisetochi profile image
Promise Tochi

Thanks for reading

Collapse
 
dailyscat profile image
Dailyscat • Edited

Hello tochi, I'm Eungyu Lee, korean Frontend developer. I think your writing is worth a lot. So I'd like to translate this and post it, would you let me?

Collapse
 
promisetochi profile image
Promise Tochi

Hi Eungyu, please go ahead. Can you share the translated with me if possible :)

Collapse
 
dailyscat profile image
Dailyscat

Thank you very much. Thanks to your post, more people are getting knowledge.
constds.tistory.com/123

Thread Thread
 
promisetochi profile image
Promise Tochi

Thank you

Collapse
 
alexandrutomescu profile image
Alexandru Tomescu

Thank you !

Collapse
 
cantfindaname88 profile image
Joe Buza

Fantastic article. I learned quite a bit. Very well written. Thank you.

Collapse
 
bellomuboye profile image
Bell Omuboye

What are the other categories. I have seen severally that these are the major ones but where can I see a full list