了解JS语法——语句和表达式

435 阅读5分钟

我们今天讨论下js的语法规则,尽管我们都能熟练使用js进行编程,但我想深入理解的话,其实还是会有我们所不知道的知识点,下面让我们一起探索js语法。这章节关联的知识点关联性比较强,需要重头到位仔细咀嚼~

语句和表达式

在JS中,“语句” 就跟现实生活中的 “句子” 一样,表示一句话的说完,同时具有完整的意义。而 “表达式” 就相当于 “句子” 中的短语、名词,“运算符”相当于标点符号和连接词。

JS中表达式可以返回一个结果值,我们来看个例子:

var a = 4 * 5;
var b = a;
b;

这里,第一行的4 * 5是一个表达式(结果是20),第二行的 a 也是表达式,第三行的 b 也是。a 和 b 的结果值都是 20。

上面的例子中,三行都是一个包含表达式的 “语句”。var a = 4 * 5var b = a 是 声明表达式 。声明了变量,同时可以对其进行赋值。a = 4 * 5b = a 是 赋值表达式 (没有包含 var)。第三行的b只有一个表达式,同时它也是一个 “语句”,我们称为 “表达式语句”。

语句的都有一个结果值

平时在日常开发中,都会用到浏览器的控制台。不知道你有没有发现,我们在控制台调试的时候,总会发现一段语句执行后,语句或者表达式的下方都会出现 undefined 或者其他值。

1660382409168.png

其实,语句都有一个结果值。而上面的截图中,显示的就是语句的结果值。那么如果是 if else 这样的代码块,是否有返回值呢?他的返回值是什么?

1660383222431.png

看截图我们发现,代码块也是有返回结果值的,并且总是最后一个语句的结果值。

但是语句的结果值我们是不能获取的,原因当然是JS的语法规定我们不能这样做。

// 会报错
var a, b;
a = if (true) {
    b = 1 + 1;
}

表达式的副作用

副作用:在主要作用以外附带产生的作用。

通常的表达式中都不会有什么副作用。例如下面的例子:

var a = 5;
var b = a + 3;

表达式 a + 3 本身没有什么副作用,它的结果值为 8 ,然后通过赋值表达式赋值给了 b 。

常见的产生副作用的表达式是函数调用:

var a = 1;
function foo() {
    a = a + 3;
}
foo(); // 结果值是 undefined;副作用是改变了 a 的值。

为什么说副作用的,因为你是单纯的调用函数,但实际函数内部对外部的 a 做了新的赋值,所以在调用函数的同时,顺带产生了对 a 进行赋值的副作用。当然,如果你的函数没有引用外部的值,也不会产生副作用。

我们再看看其他表达式的副作用

递增/递减运算符

var a = 42;
var b = a++; // 42;

a // 43;
b // 42;

上面的例子中,语句var b = a++;中,递增表达式a++,产生了副作用,先返回了结果值,结果值被赋给变量b,然后将a的值加1.

但是前置/后置的递增/递减,为什么导致值的不同呢?这是因为 ++ 运算符产生的副作用的时间不同。

++在前面时,如 ++a,它的副作用(将a递增)产生在表达式返回结果值之前,而 a++ 的副作用则产生在之后。所以就要有了语句var b = a++;中,a的结果值42赋值给了b之后,a进行了递增。

再如delete运算符,它通常以表达式语句出现(作用,删除对象中的属性和数组中的单元)。

var obj = {
    a: 1
}
obj.a // 1
delete obj.a // true
obj.a // undefined

如果删除成功,delete返回true,否则返回false。其副作用是属性被从对象中删除(或者单元从对象中删除)。

上下文规则

上下文在JavaScript中是一个比较抽象的概念,就拿我们高中时的阅读理解为例子,一整篇文章比作一个全局上下文,里面包含了文章的各种信息(人物,背景,语境等)。一个段落也是一个局部上下文,里面也包含着它持有的信息。

JavaScript中,有时相同的语法在不同的情况下也会有不同的意思。下面我们来了解一下我们最常见的花括号。

1.花括号{}

  • 对象常量:我们知道定义一个对象常量,可以这样写 var obj = {};其中 {}被作为一个对象赋值给了 obj
  • 标签语句:这个语法特性由于平时开发过程中基本没见过,同时它的代码十分晦涩难懂,也不建议使用。有兴趣的话可以自己研究。

2.代码块

{}可以作为一个代码块。就好比函数后面的花括号一样,只不过不携带函数名。

{
 var a = 1;
 console.log(a); // 1
}

我们看下这个例子:

[] + {}; // "[object Object]"
{} + []; // 0

第一行之所以是"[object Object]"是因为中间发生了类型转换,[]被转化成了空字符串""。然后空字符串+ {}就有了后面的结果。

第二行:前面的{}其实是个空代码块,并且里面没有执行任何操作。代码结尾不需要分号,所以不存在语法错误。随后的 + [],是一个显示数字类型转换,把 [] 转成了数字0。

3.解构赋值

ES6开始支持的对象解构。

function getData() {
    return {
        a: 42,
        b: "foo"
    }
}
var { a, b } = getData();
console.log(a, b); // 42 "foo"

4.else if 和可选代码块

很多人误以为JS中有 else if,其实在JS中是不存在这个短语的。它其实是有由可选代码块这一特性演变过来的。

我们知道,如果 if 和 else 后面只跟一条语句的话,是可以把{}忽略的。

var a = 'foo';
if(a === 'foo') return 11;
if(a){
} else if(b) {
} else {
}

// 实际上等于
if(a){
} else {
    if(b) {
    } else {
    }
}

由于if(b){} else {}这个判断,其实本身就是一条判断语句,因为前面我们说过,if 和 else 后面只跟一条语句的话,是可以把{}忽略的。所以我们选择性的忽略了 else 后面的 {}。这样就有了 else if