标签: 语法,语句,表达式,you don't know javascript
语句和表达式
- “语句”和“表达式”在JavaScript中不同的两个概念。
var a = 3 * 6;
var b = a;
b;
表达式:
- 第一行 3 * 6
- 第二行 a
- 第三行 b
语句:
这三行代码叫做包含表达式的语句。
- 第三行中只有一个表达式b,通常叫做“表达式语句”
语句的结果值
- 语句都有一个结果值(statement completion value, undefined也算)。
- 可以在浏览器控制台中输入语句,总是返回的是最后一条语句的结果值。
> var a = 30
< undefined
> a = 40
< 40
- 声明语句的返回值是undefined。
- 赋值语句返回的是被赋值变量赋值后的值。
{..}代码块的结果值
- 代码块的结果值是最后一条语句的结果。
> {
var b = 10;
b = 20
}
< 20
- 语句的结果值不能通过变量赋值的方式取到
var result = {
var b = 10;
b = 20
}
// SyntaxError
- 可以用eval(..)获得语句的结果值,但是JavaScript编程从来都不推荐使用eval()。
var result = eval(`{
var b = 10;
b = 20
}`)
result // 20
表达式的副作用
- 中文翻译版将
effect
译为副作用
,有点难以理解。 - 个人理解为,表达式有返回值是表达式的一个重要特性,但是大部分表达式还会有
运算
作用,他会对程序中的其他部分(变量)产生影响。简单理解为有运算符的表达式就会有effect
。
var a = 30;
a++; // 30
a; // 31
++a; // 32
a; // 32
-
副作用
产生的时间不同,++a
在副作用(a自增)产生之后返回值,而a++
在副作用产生之前返回值。 -
delete
表达式也有返回值,删除不存在或可配置的属性,返回true,否则返回false或者报错。
赋值运算符
- 研究赋值运算符的副作用和返回值在连等语句中有重要意义。
var a,b,c
a = b = c = 30;
// c = 30的副作用是将30复制给c,返回值是表达式c = 30的结果值,同样原理左依次执行。
- 利用表达式的返回值和副作用将语句合并。
function foo (a) {
return a > 20
}
var num = 30
if (num > 10) {
var result = foo(num)
if (result) {
console.log(result)
}
}
上面的两个if
可以合并成
var result
if (num > 10 && (result = foo(num))) {
console.log(result)
}
大括号
- JavaScript中同样的语法在不同情况下会有不同的解释,其中大括号就是典型。
对象字面量
- 字面量形式定义变量
var a = {
foo: 1
}
代码块
- 以下代码使用的{}定义代码块并产生了es6中的块级作用域
{
let a = 10;
a
}
对象解构
- 这部分之前学习过,不再重复。
let {a, b} = {a: 10, b: 20}
else if问题
-
if
和else
后面的语句只有一句时可以省略{}
。
if (a) console.log(a)
-
else if
不是JavaScript的语法规则,是开发者发明的,很好用,所以极为常见。 -
else if
表示else
后跟了一个省略{}
的if
代码块。 -
因不推荐将
if
或else
后面的{}
省略,所以else if
在这个层面上讲是不规范的写法。
if (a) {
} else if (b) {
}
// 规范写法
if (a) {
} else {
if (b) {
}
}
大括号的“歧义”
- 这个“歧义”是指开发者难以判断大括号代表的是什么(对象?代码块?),而不是指引擎解析上的歧义。
function foo () {
}
{
foo: foo()
}
上面代码像是创建了一个对象但是没有变量接收,但是实际上是{}
代表了一个代码块,而foo:
在正常代码中写法显得奇怪但是没有报错,是因为这里有个我们不常用的label
代码标签。
- 上面代码块中的逻辑为“定义了一个名为
foo
的语句foo()
”
代码标签在循环中的作用
-
break
continue
跳出嵌套循环
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
if (i === j) {
break;
}
console.log(i,j)
}
}
// 1 0
foo: for (var i = 0; i < 2; i++) {
for (var j = 0; j < 2; j++) {
if (i === j) {
break foo; // 表示跳出标签为foo的循环,即外层循环
}
console.log(i,j)
}
}
// 没有打印任何i,j
分号问题
自动分号
- Automatic Semicolon Insertion,ASI,自动分号插入。
- JavaScript解析器中发现代码行可能因为缺少分号而导致错误,就会在代码行末尾与换行符之间没有其他内容的情况下(空格和注释除外)自动补上分号。
纠错机制
- ASI是一种纠错机制,现在有些时候被用于语法简洁。
- 在之前学习ES6时,有听到一种说法是“es6中建议省略不必要的分号”,于是在学习es6之后,都习惯性的不加分号了。
- 在了解了这个规范之后,对于加不加分号有了新的认识:这不是一个代码美观问题,而是一个语法规则问题。
异议
json语法在控制台直接输入的报错解释
书中的说法:
以下代码在控制台直接输入会报错,因为{..}
被解析成代码块,foo
被解析成标签,而标签不能带有引号
> {
"a": 20
}
实际的结果:
chrome中会解析成创建了一个对象和{a:20}
相同。
以下两种{}
的运行结果
报错:
> ;{
"a": 20
}
不报错:
> ;{
a: 20
}
< 20
强制类型转换的问题
[] + {} // "[object Object]"
{} + [] // 0
{} + []
中{}被视为代码块,空代码块被强制类型转换后的值怎么确定的不太明确。
但可以测试空代码块的返回值是undefined
> eval("{}")
< undefined
> ;{}
< undefined
往期精彩回顾
- Ant Design 组件 —— Form表单(一)
- Ant Design 组件 —— Form表单(二)
- CMS管理后台入门指南 (Ant Design Pro v2.0)
- 实现点击下载文件的几种方法
- 在https中引入http资源所导致的问题
叶茂 广州芦苇科技web前端工程师
网友评论