JS表达式:关于JavaScript表达式、语句和表达式语句你需要知道的一切
读完本文后,你应该能够详细描述下图中的代码是如何工作的以及它为什么能工作。
Javascript 中有两大语法类别:
- 声明
- 表达式
区分表达式和语句至关重要,因为表达式可以像语句一样工作,这也是为什么我们有表达式语句的原因。但另一方面,语句不能像表达式一样工作。
表达方式
表达式产生值
表达式是 JavaScript 代码片段,它只会返回一个值。表达式的长度可以任意长,但始终只会返回一个值。
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
以上都是表达式,可以出现在任何 JavaScript 需要值的地方。因此console.log,下面的参数会解析为一个单独的值,并将其记录到控制台。
console.log(true && 2 * 9) // 18
表达式不一定会改变状态
例如,
const assignedVariable = 2; //this is a statement, assignedVariable is state
assignedVariable + 4 // expression
assignedVariable * 10 // expression
assignedVariable - 10 // expression
console.log(assignedVariable) // 2
尽管上面的代码片段中包含了各种表达式,但 assignedVariable 的值仍然是 2。那么,为什么necessarily本节标题中会出现“2”呢?这是因为函数调用本身就是表达式,而函数可以包含改变状态的语句。因此,`if`foo()本身就是一个表达式,它要么返回 undefined,要么返回其他值。但如果foo写成这样,它就不是 `if` 了。
const foo = foo () => {
assignedVariable = 14
}
那么,即使它的调用是一个表达式,它也导致了状态的改变。因此,重写 foo 函数和语句的更好方法是:
const foo = foo () => {
return 14 //explicit return for readability
}
assignedVariable = foo()
或者更好
const foo = foo (n) => {
return n//explicit return for readability
}
assignedVariable = foo(14)
这样一来,你的代码更易读、更易组合,表达式和语句之间也有了清晰的区分和分离。这是函数式和声明式 JavaScript 的基本原则。
声明
语句是函数式编程的一大难题。基本上,语句执行操作,它们做事情。
在 JavaScript 中,语句永远不能用于需要传入值的地方。因此,它们不能用作函数参数、赋值语句的右侧元素、运算符的操作数、返回值……
foo(if () {return 2}) //js engine mind = blown
这些都是 JavaScript 语句:
- 如果
- 如果-否则
- 尽管
- 做
- 为了
- 转变
- for-in
- (已弃用)
- 调试器
- 变量声明
如果您在浏览器的控制台中输入以下代码片段并按回车键,就会出现问题。
if (true) {9+9}
你会发现它确实返回了值,18但即便如此,你也不能把它当作表达式使用,或者用在 JavaScript 需要值的地方。这很奇怪,因为你通常会认为语句不应该返回任何值,毕竟如果不能使用返回值,那它就毫无用处。这就是 JavaScript,真是奇怪。
函数声明、函数表达式和命名函数表达式
函数声明是一个语句
function foo (func) {
return func.name
}
函数表达式是一种表达式,也就是我们常说的匿名函数。
console.log(foo(function () {} )) // ""
命名函数表达式是一种表达式,类似于匿名函数,但它有一个名称。
console.log(foo(function myName () {} )) // "myName"
函数作为表达式和函数作为声明之间的区别归根结底在于理解这一点:
当你在 JavaScript 需要接收值的地方声明一个函数时,它会尝试将其视为一个值;如果无法将其视为值,则会抛出错误。
而当在脚本、模块的全局级别或代码块的顶层(即不需要接收值的地方)声明函数时,它就只是一个函数声明。
例如:
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
}
将表达式转换为语句:表达式语句
使用 JavaScript 做任何事都简单明了吗?
2+2; //expression statement
foo(); //expression statement
您可以通过在行尾添加分号或启用自动分号插入功能,将表达式转换为表达式语句。2+2表达式本身是一个表达式,但整行代码是一个语句。
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
分号与逗号运算符
使用分号,您可以将多个语句放在同一行。
const a; function foo () {}; const b = 2
逗号运算符允许您链接多个表达式,并仅返回最后一个表达式。
console.log( (1+2,3,4) ) //4
console.log( (2, 9/3, function () {}) ) // function (){}
console.log( (3, true ? 2+2 : 1+1) ) // 4
附注:告诉 Javascript 引擎期望一个值的一种方法是通过括号 (),如果没有括号,每个表达式都会被视为 console.log 的参数。
function foo () {return 1, 2, 3, 4}
foo() //4
所有表达式将从左到右依次求值,并返回最后一个表达式的结果。
立即调用函数表达式 (IIFE)
匿名函数可以是一个表达式,如果我们在 JavaScript 需要值的地方使用它,这意味着如果我们能告诉 JavaScript 需要用括号括起来的值,我们就可以把匿名函数作为该值传递。
function () {}
因此,虽然上面的代码片段无效,但下面的代码片段有效。
(function () {}) // this returns function () {}
如果将匿名函数放在括号内会立即返回该匿名函数本身,这意味着我们可以直接调用它,像这样:
(function () {
//do something
})()
所以,这些都是有可能的。
(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
对象字面量与代码块语句
附注:这是有效的 Javascript 代码。
r: 2+2 // valid
foo()
const foo = () => {}
以上是一系列全局作用域内的语句,它们将被解析为有效的 JavaScript 代码并执行。这些r语句被称为标签,它们主要用于打破循环。例如:
loop: {
for (const i = 0; i < 2; i++) {
for (const n = 0; n <2; n++) {
break loop //breaks outer loop and stops entire loop
}
}
}
您可以在任何表达式或表达式语句前添加标签,但请注意,这样做并不会创建变量 lab:
lab: function a () {}
console.log(lab) //ReferenceError: lab is not defined
花括号 {} 允许你将表达式语句和附加语句组合在一起。因此,你可以这样写:
{var a = "b"; func(); 2+2} // 4
如果你把上面的代码粘贴到浏览器的控制台中,它会返回 4,然后你执行 ` console.log(a)getString()` 语句,你会得到一个字符串b。你可以把它称为代码块语句,它与你可能熟悉的对象字面量不同。
console.log({a: 'b'}) // {a: 'b'}
console.log({var a = "b", func(), 2+2}) // SyntaxError
const obj = {var a = "b", func(), 2+2} // SyntaxError
你不能将代码块语句用作值或表达式,因为 `console.log` 是一个函数,它不能接受语句作为参数。不过,它可以接受对象字面量。
希望你理解了我上面解释的所有内容,因为下面的代码片段可能会让你感到困惑。
{} + 1 //1
{2} + 2 // 2
{2+2} + 3 // 3
{2+2} -3 // -3
你可能会预期它会抛出语法错误,或者分别返回 1、4 或 7。记住,语句不应该返回任何值,因为它们不能用作值。因此,JavaScript 不会抛出错误,而是尝试将运算符的操作数转换+为数字或字符串;如果转换失败,才会抛出异常。所以,无论代码块语句返回什么值,都会被隐式强制转换为0用作操作数。
呼,如果你一直读到这里,你真是个MVP。关于表达式、语句和表达式语句,你可能只需要知道这些就够了。
文章来源:https://dev.to/promisetochi/javascript-in-deep-all-you-need-to-know-about-expressions-statements-and-express-statements-5k2