mirror of
https://github.com/DefectingCat/DefectingCat.github.io
synced 2025-07-16 01:01:38 +00:00
update test 更新文章 1. 测试新主题 挖坑 挖坑 update config 更新文章 1. 简易FaaS平台 更新文章 1. 修复错误 更新文章:Promise信任问题 update theme 1. 合并js post: update notedly fix: update faas feature: change theme * fix: comment * feature: pgp * fix: delete local file post: update darkmode update: update dependencies fix: navbar in post incorrect height * pre code adapt to dark mode update new post useCallback update dependencies new post tiny router * add static files update vue tiny router 添加备案 * 更新依赖 add post Add ignore file
233 lines
7.0 KiB
Markdown
233 lines
7.0 KiB
Markdown
---
|
||
title: JavaScript-语法
|
||
date: 2021-07-12 15:20:03
|
||
tags: JavaScript
|
||
categories: 笔记
|
||
url: javascript-syntax
|
||
---
|
||
|
||
在 JavaScript 中,有很多常见的语法仍然有很多地方容易产生困惑、造成误解。
|
||
|
||
## 语句与表达式
|
||
|
||
语句(statement)和表达式(expression)常常被混为一谈,但他们二者之间有细微的差别。语句相当于一句话,而表达式更类似于一条短语。
|
||
|
||
JavaScript 中表达式可以返回一个结果值:
|
||
|
||
```js
|
||
let a = 3 * 6;
|
||
const b = a;
|
||
b;
|
||
```
|
||
|
||
上述带有`let`与`const`的语句被称为“声明语句”(declaration statement),而不带声明关键词的`a = 3 * 6;`则是“赋值表达式”。
|
||
|
||
乍一看可能还是不能太过于区分他们之间的区别,但是有些地方会让我们强制区分他们。在现代 SPA 框架中,模板语法中通常只允许插入 JavaScript 表达式,而不允许插入语句。例如在常见的 Vue 和 React 中:
|
||
|
||
```vue
|
||
<template>
|
||
<span>{{ 3 * 6 }}</span>
|
||
<span>{{ a }}</span>
|
||
<span>{{ let a = 3 * 6 }}</span> // 错误
|
||
</template>
|
||
```
|
||
|
||
### 语句的结果值
|
||
|
||
基本上所有的语句都有一个结果值(undefined 也算)。
|
||
|
||
如果经常和控制台打交道,应该会发现很多时候控制台都会给我们返回一个 undefined。其实这就是语句所返回的值,只不过大部分情况下都是 undefined。
|
||
|
||
代码块中的返回值是最后一个语句/表达式的结果:
|
||
|
||
```js
|
||
let a ;
|
||
// undefined
|
||
if (true) {
|
||
a = 3 + 1;
|
||
}
|
||
// 4
|
||
```
|
||
|
||
目前代码块语句返回的值无法被拿到:
|
||
|
||
```js
|
||
b = if (true) {
|
||
a = 3 + 1;
|
||
}
|
||
// VM268:1 Uncaught SyntaxError: Unexpected token 'if'
|
||
```
|
||
|
||
当然也有变通的方法 evil:
|
||
|
||
```js
|
||
b = eval("if (true) {a = 3 + 1;}");
|
||
b; // 4
|
||
```
|
||
|
||
除了万恶的 evil,ES7 还有一项“do 表达式”提案,目前环境还没实现:
|
||
|
||
```js
|
||
b = do {
|
||
if (true) {
|
||
a = 3 + 1;
|
||
}
|
||
};
|
||
b; // 4
|
||
```
|
||
|
||
### 表达式的副作用
|
||
|
||
副作用通常是指执行了语句之后,除了返回值或赋值,还有其他变量等被修改。
|
||
|
||
最常见的副作用的表达式是函数调用:
|
||
|
||
```js
|
||
const foo = () => {
|
||
a = a + 1;
|
||
};
|
||
|
||
let a = 1;
|
||
foo(); // 结果:undefined,副作用:a 的值被修改
|
||
```
|
||
|
||
### 上下文规则
|
||
|
||
在 JavaScript 语法规则中,有时候同样的语法在不同的情况下会有不同的解释。这些语法规则孤立起来会很难理解。
|
||
|
||
#### 大括号
|
||
|
||
大括号的作用有很多,随着 JavaScript 的演进可能会出现更多类似的情况。
|
||
|
||
**对象常量**
|
||
|
||
大括号可以使用表达式的方式定义对象:
|
||
|
||
```js
|
||
const a = {
|
||
foo: bar()
|
||
}
|
||
```
|
||
|
||
大括号内的内容被赋值给变量 a,因而它是一个对象常量。
|
||
|
||
**标签**
|
||
|
||
将上述声明语句去掉,这时的大括号可不是一个孤立的对象常量。因为这里的`{...}`被看作了一个普通的代码块。这里的`foo: bar()`被解释为标签语句。
|
||
|
||
```js
|
||
{
|
||
foo: bar()
|
||
}
|
||
```
|
||
|
||
标签语句配合 continue 和 break 语句可以实现类似于 goto 的方式进行跳转。但 goto 是一种极为糟糕的编码方式,所以 JavaScript 并不支持真正的 goto 语句。
|
||
|
||
#### 代码块
|
||
|
||
有一个强制类型转换的坑经常被提到:
|
||
|
||
```js
|
||
[] + {} // "[object Object]"
|
||
{} + [] // 0
|
||
```
|
||
|
||
看上去简直就是 JavaScript 在和我们开玩笑。其实不然,上述情况并不是非常难以理解。
|
||
|
||
第一行是普通的强制类型转换,二者都会被强制转换字符串,而`[]`被转换为了`''`,`{}`被转换为了`"[object Object]"`,所以他们相加的最后结果就是`"[object Object]"`。
|
||
|
||
第二行看上去有点莫名其妙,换个位置结果就变了。这里其实`{}`并没有参与运算,而是被当作了一段独立的空代码块。真正参与运算的是`+[]`,最终被转换为数字 0。
|
||
|
||
#### 对象解构
|
||
|
||
从 ES6 开始`{...}`也可用于解构赋值。
|
||
|
||
```js
|
||
const foo = () => {
|
||
return {
|
||
name: 'xfy',
|
||
age: 18,
|
||
};
|
||
};
|
||
|
||
const { name, age } = foo();
|
||
console.log(name, age);
|
||
```
|
||
|
||
同理,函数形参也可以利用解构赋值。
|
||
|
||
#### else if 和可选代码块
|
||
|
||
其实`else if`并不存在于 JavaScript 语法中,只是多个`if else`只包含单条语句的时候可以省略代码块的`{...}`。
|
||
|
||
实际上`else if`是这样的:
|
||
|
||
```js
|
||
if (a) {
|
||
console.log(a);
|
||
} else {
|
||
if (b) {
|
||
console.log(b);
|
||
} else {
|
||
console.log(c);
|
||
}
|
||
}
|
||
```
|
||
|
||
## 运算符优先级
|
||
|
||
多数情况下,JavaScript 的数学运算符与真正的数学一样,按照真正的数学的顺序进行运算。但 JavaScript 的语句并不是数学公式,而且语句中通常会充斥着各种各样的运算符。他们也是有各自的运算优先级的。
|
||
|
||
### 短路
|
||
|
||
and 与 or 操作拥有“短路”(short circuiting)的特性,别急,我们还没走错片场。在 JavaScript 中,他们分别是`&&`和`||`运算符。
|
||
|
||
短路的意思是,当左边的操作数能够得出结果时,就可以忽略右边的操作数。
|
||
|
||
对于`&&`来说,左边的操作数为假值时,则就没有必要再判断右边的值。同理,对于`||`来说,左边的操作数为真值时,也没有必要再判断右边的操作数。
|
||
|
||
### 关联
|
||
|
||
JavaScript 的代码是从上往下执行的,而单条语句是从左往右执行的。但在特定的运算符下,某些语句需要先求得右边的值。
|
||
|
||
例如:
|
||
|
||
```js
|
||
true ? false : true ? true : true;
|
||
```
|
||
|
||
这段代码乍一看很令人头疼,但它确实说明运算符的关联最容易理解的例子。在这里,第二个三目运算符的结果会影响第一个三目运算符,所以这里要执行右关联,也就是需要先求得第二个三目运算符的结果。
|
||
|
||
> 虽然是右关联,但它的运算顺序还是从左到右。`a ? b : c;`还是会先执行 a 然后 b c。
|
||
|
||
## 自动分号
|
||
|
||
有时 JavaScript 会自动为代码补上分号,即分号自动插入机制(Automatic Semicolon Insertion,ASI)。
|
||
|
||
ASI只会在换行符处起作用,而不会在代码中间插入分号。
|
||
|
||
常见的情况是在 return 后自动加上,这样 return 后换行的代码就会不生效。return 语句可以跨越多行,但是其后必须右换行符以外的代码。
|
||
|
||
```js
|
||
const foo = () => {
|
||
return (
|
||
1 + 2
|
||
);
|
||
};
|
||
```
|
||
|
||
### 纠错机制
|
||
|
||
是否应该完全依赖 ASI 来编码,这是 JavaScript 社区中最具争议性的话题之一。
|
||
|
||
支持的一方认为 ASI 大有裨益,能省略掉那些不必要的`;`,让代码更简洁。此外 ASI 让许多`;`变得可有可无,因此只要代码没问题,有没有`;`都一样。
|
||
|
||
反方则认为 ASI 机制问题太多,对于缺乏经验的初学者尤其如此,因为自动插入`;`会无意改变代码的逻辑。有些人还认为依赖 linter 这些工具找出错误,而不是依赖 JavaScript 引擎。
|
||
|
||
事实上,现在多数`;`可以通过 ESlint 这样的 linter 工具来在格式化代码是插入。这种方式相比较用引擎来找出错误好很多,而且它不会在运行时改变代码原有的意思。
|
||
|
||
## 小结
|
||
|
||
JavaScript 语法规则中的许多细节需要我们多花时间和精力来了解。从长远来看,这有助于更深入地掌握这门语言。
|
||
|