原文地址:http://www.2ality.com/2012/09/expressions-vs-statements.html

Update 2012-09-21: New in Sect. 4: using void for IIFEs, concatenating IIFEs.

This blog post looks at a syntactic distinction that is unfortunately quite important in JavaScript: the difference between expressions and statements.

Statements and expressions

JavaScript distinguishes expressions and statements. An expression produces a value and can be written wherever a value is expected, for example as an argument in a function call. Each of the following lines contains an expression:

  1. myvar
  2. 3 + x
  3. myfunc("a", "b")

Roughly, a statement performs an action. Loops and if statements are examples of statements. A program is basically a sequence of statements (we’re ignoring declarations here). Wherever JavaScript expects a statement, you can also write an expression. Such a statement is called an expression statement. The reverse does not hold: you cannot write a statement where JavaScript expects an expression. For example, an if statement cannot become the argument of a function.

Similar kinds of statements and expressions

The difference between statements and expressions becomes clearer if we look at members of the two syntactic categories that are similar.

If statement versus conditional operator

The following is an example of an if statement:

  1. var x;
  2. if (y >= 0) {
  3. x = y;
  4. } else {
  5. x = -y;
  6. }

Expressions have an analog, the conditional operator. The above statements are equivalent to the following statement.

  1. var x = (y >= 0 ? y : -y);

The code between the equals sign and the semicolon is an expression. The parentheses are not necessary, but I find the conditional operator easier to read if I put it in parens.

Semicolon versus comma operator

In JavaScript, one uses the semicolon to chain statements:

  1. foo(); bar()

For expressions, there is the lesser-known comma operator:

  1. foo(), bar()

That operator evaluates both expressions and returns the result of the second one. Examples:

  1. > "a", "b"
  2. 'b'
  3.  
  4. > var x = ("a", "b");
  5. > x
  6. 'b'
  7.  
  8. > console.log(("a", "b"));
  9. b

Expressions that look like statements

Some expressions look like statements. We’ll examine why that is a problem at the end of this section.

Object literal versus block

The following is an object literal, an expression that produces an object.

  1. {
  2. foo: bar(3, 5)
  3. }

However, it is also a perfectly legal statement, with these components:

  • A block: a sequence of statements in curly braces.
  • A label: you can prefix any statement with a label. Here the label is foo.
  • A statement: the expression statement bar(3, 5).

{} being either a block or an object literal is responsible for the following WAT:

  1. > [] + {}
  2. "[object Object]"
  3.  
  4. > {} + []
  5. 0

Given that the plus operator is commutative, shouldn’t these two (expression) statements return the same result? No, because the second statement is equivalent to a code block followed by +[]:

  1. > +[]
  2. 0

For details on this and other WAT phenomena, consult [1].

JavaScript has stand-alone blocks? It might surprise you that JavaScript has blocks that can exist on their own (as opposed to being part of a loop or an if statement). The following code illustrates one use case for such blocks: You can give them a label and break from them.

  1. function test(printTwo) {
  2. printing: {
  3. console.log("One");
  4. if (!printTwo) break printing;
  5. console.log("Two");
  6. }
  7. console.log("Three");
  8. }

Interaction:

  1. > test(false)
  2. One
  3. Three
  4.  
  5. > test(true)
  6. One
  7. Two
  8. Three

Function expression versus function declaration

The code below is a function expression:

  1. function () { }

You can also give a function expression a name and turn it into a named function expression:

  1. function foo() { }

The function name (foo, above) only exists inside the function and can, for example, be used for self-recursion:

  1. > var fac = function me(x) { return x <= 1 ? 1 : x * me(x-1) }
  2. > fac(10)
  3. 3628800
  4. > console.log(me)
  5. ReferenceError: me is not defined

A named function expression is indistinguishable from a function declaration (which is, roughly, a statement). But their effects are different: A function expression produces a value (the function). A function declaration leads to an action – the creation of a variable whose value is the function. Furthermore, only a function expression can be immediately invoked, but not a function declaration.

Using object literals and function expressions as statements

We have seen that some expressions are indistinguishable from statements. That means that the same code works differently depending on whether it appears in an expression context or a statement context. Normally the two contexts are clearly separated. However, with expression statements, there is an overlap: There, you have expressions that appear in a statement context. In order to prevent ambiguity, the JavaScript grammar forbids expression statements to start with a curly brace or with the keywordfunction:

  1. ExpressionStatement :
  2. [lookahead {"{", "function"}] Expression ;

So what do you do if you want to write an expression statement that starts with either of those two tokens? You can put it in parentheses, which does not change its result, but ensures that it appears in an expression-only context. Let’s look at two examples: evaland immediately invoked function expressions.

eval

eval parses its argument in statement context. If you want eval to return an object, you have to put parentheses around an object literal.

  1. > eval("{ foo: 123 }")
  2. 123
  3. > eval("({ foo: 123 })")
  4. { foo: 123 }

Immediately invoked function expressions (IIFEs)

The following code is an immediately invoked function expression.

  1. > (function () { return "abc" }())
  2. 'abc'

If you omit the parentheses, you get a syntax error (function declarations can’t be anonymous):

  1. > function () { return "abc" }()
  2. SyntaxError: function statement requires a name

If you add a name, you also get a syntax error (function declarations can’t be immediately invoked):

  1. > function foo() { return "abc" }()
  2. SyntaxError: syntax error

Another way of guaranteeing that an expression is parsed in expression context is a unary operator such as + or !. But, in contrast to parentheses, these operators change the result of the expression. Which is OK, if you don’t need it:

  1. > +function () { console.log("hello") }()
  2. hello
  3. NaN

NaN is the result of applying + to undefined, the result of calling the function. Brandon Benvie mentions another unary operator that you can use: void [2]:

  1. > void function () { console.log("hello") }()
  2. hello
  3. undefined

Concatenating IIFEs

When you concatenate IIFEs, you must be careful not to forget semicolons:

  1. (function () {}())
  2. (function () {}())
  3. // TypeError: undefined is not a function

This code produces an error, because JavaScript thinks that the second line is an attempt to call the result of the first line as a function. The fix is to add a semicolon:

  1. (function () {}());
  2. (function () {}())
  3. // OK

With operators that are only unary (plus is both unary and binary), you can omit the semicolon, because automatic semicolon insertion kicks in.

  1. void function () {}()
  2. void function () {}()
  3. // OK

JavaScript inserts a semicolon after the first line, because void is not a valid way of continuing this statement [3].

Related posts

  1. What is {} + {} in JavaScript?
  2. The void operator in JavaScript
  3. Automatic semicolon insertion in JavaScript

【转】Expressions versus statements in JavaScript的更多相关文章

  1. Expressions versus statements in JavaScript

    Statements and expressions An expression produces a value and can be written wherever a value is exp ...

  2. JavaScript简易教程(转)

    原文:http://www.cnblogs.com/yanhaijing/p/3685304.html 这是我所知道的最完整最简洁的JavaScript基础教程. 这篇文章带你尽快走进JavaScri ...

  3. 每个JavaScript工程师都应懂的33个概念

    摘要: 基础很重要啊! 原文:33 concepts every JavaScript developer should know 译文:每个 JavaScript 工程师都应懂的33个概念 作者:s ...

  4. JavaScript简易教程

    这是我所知道的最完整最简洁的JavaScript基础教程. 这篇文章带你尽快走进JavaScript的世界——前提是你有一些编程经验的话.本文试图描述这门语言的最小子集.我给这个子集起名叫做“Java ...

  5. 每个 JavaScript 工程师都应懂的33个概念

    简介 这个项目是为了帮助开发者掌握 JavaScript 概念而创立的.它不是必备,但在未来学习(JavaScript)中,可以作为一篇指南. 本篇文章是参照 @leonardomso 创立,英文版项 ...

  6. JavaScript 中表达式和语句的区别

    1.语句和表达式 JavaScript中的表达式和语句是有区别的.一个表达式会产生一个值,它可以放在任何需要一个值的地方,比如,作为一个函数调用的参数.下面的每行代码都是一个表达式: myvar3 + ...

  7. 对JavaScript优化及规范的一些感想

    变量...... 1.一个变量只存一种类型的数据,2.尽量减少对隐式转换的依赖,这样可增强程序的可读性,日后修改程序时不至于混乱,3.使用匈牙利命名法,4.使用局部变量时记得加 var 进行声明,不然 ...

  8. (译)详解javascript立即执行函数表达式(IIFE)

    写在前面 这是一篇译文,原文:Immediately-Invoked Function Expression (IIFE) 原文是一篇很经典的讲解IIFE的文章,很适合收藏.本文虽然是译文,但是直译的 ...

  9. [转]Javascript中的自执行函数表达式

    [转]Javascript中的自执行函数表达式 本文转载自:http://www.ghugo.com/javascript-auto-run-function/ 以下是正文: Posted on 20 ...

随机推荐

  1. show status 查看各种状态

    要查看MySQL运行状态,要优化MySQL运行效率都少不了要运行show status查看各种状态,下面是参考官方文档及网上资料整理出来的中文详细解释: 如有问题,欢迎指正 状态名 作用域 详细解释 ...

  2. MySQL语句整理(二)

    数据库操作前的准备 -- 创建数据库 -- create database python_test_1 charset=utf8; -- 使用数据库 -- use python_test_1; -- ...

  3. h5和css3构建响应式网站

    响应式页面组成 灵活图像,媒体:资源尺寸使用百分比定义 流式布局,所有width属性使用百分比设定,水平属性通常使用相对单位(em,百分数,rem等) 媒体查询,根据媒体特征进行设计调整 创建可伸缩图 ...

  4. Spark在实际项目中分配更多资源

    Spark在实际项目中分配更多资源 Spark在实际项目中分配更多资源 性能调优概述 分配更多资源 性能调优问题 解决思路 为什么调节了资源以后,性能可以提升? 性能调优概述 分配更多资源 性能调优的 ...

  5. Spark运行模式_本地伪集群运行模式(单机模拟集群)

    这种运行模式,和Local[N]很像,不同的是,它会在单机启动多个进程来模拟集群下的分布式场景,而不像Local[N]这种多个线程只能在一个进程下委屈求全的共享资源.通常也是用来验证开发出来的应用程序 ...

  6. STM32F407+STemwin学习笔记之STemwin移植补充Touch

    原文地址:http://www.cnblogs.com/NickQ/p/8857213.html 环境:keil5.20  STM32F407ZGT6  LCD(320*240)  STemwin:S ...

  7. Python的scrapy之爬取链家网房价信息并保存到本地

    因为有在北京租房的打算,于是上网浏览了一下链家网站的房价,想将他们爬取下来,并保存到本地. 先看链家网的源码..房价信息 都保存在 ul 下的li 里面 ​ 爬虫结构: ​ 其中封装了一个数据库处理模 ...

  8. C语言学习记录_2019.02.04

    逻辑性变量的定义符:bool,在C语言中只有true和false: 定义方式:bool t = true; 逻辑运算符: !:逻辑非 &&:逻辑与 ||:逻辑或 表达区间的错误形式:4 ...

  9. for循环删除列表中元素遇到的漏删的问题(python)

    问题描述:python中通过for循环来删除列表中的两个相邻的元素,存在漏删的问题 比如说下面的例子,准备删掉2和3,但是结果是2删掉了,3没删掉 是因为把2删掉后3的下标就变成了1,但是原本下标为1 ...

  10. Go 问题集

    删除文件后缀名,出现问题 import "strings" func changePath(file_path string) string { ) } 转换路径 /转换为\\ i ...