语法和运算符

语句和表达式

语句:是完整表达某个意思的一组词,由一个或多个'短语'(表达式)组成,他们之间由运算符连接 表达式: 相当于英语中的短语,有些短语不完整,不能表达意思;有些短语相对完整,能够表达某些意思

css 复制代码
var a = 3 * 6
var b = a
b

这里,3*6是一个表达式(结果为18),第二行的a也是一个表达式,第三行的b也是。表达式的a和b的结果都是18

同时,这三行代码都是包含表达式的语句。var a = 36和var b = a称为'声明语句',因为他们声明了变量。而a = 36和b = a(不带var),叫做'赋值表达式'

第三行只有一个表达式b,同时也是一个语句(虽然没有太大意义),这叫做'表达式语句'

1.语句的结果值

很多人不知道,语句都有一个结果值(underfined也算)。

获得结果值最直接的方法是在浏览器开发者工具控制台中输入语句,默认情况下控制台会显示所执行的最后一条语句的结果值

以赋值表达式b = a为例,其结果值是赋给b的值(18),但规范定义var的结果值是underfined,如果在控制台上输入var a = 42会得到结果值underfined而并非42

如果用开发控制台调试过代码,应该会看到很多语句的返回值都是underfined,只是可能并没有探究过其中的原因,其实控制台中显示的就是最后一条语句的结果值。但是我们在代码中获取这个结果值是比较复杂的,首先得弄清楚为什么要获得语句的结果值。

先来看看其他语句的结果值。比如代码块{}的结果值是其最后一个语句/表达式的结果,例如:

csharp 复制代码
var b
if(true){
    b = 4+38
}

在控制台上输入以上的代码,应该会输出42(注意,这和var b = 4 + 38不一样哦),即返回最后一个语句/表达式的结果值。

但是以下代码是不能执行的

css 复制代码
var a,b
a = if(true){
    b = 4+38
}

因为语法不允许我们获得语句结果值并将其赋值给另一个变量,那应该怎么获取语句的结果值呢?可以用eval()

less 复制代码
var a,b
a = eval("if(true){b = 4+38}")
a //42

众所周知,eval()能不用就不用,实际上,ES7规范有一项'do表达式'提案,可以让我们获取语句的结果值

css 复制代码
var a,b
a = do{
    if(true){
        b = 4 + 38
    }
}
a

上例中,do{}表达式执行一个代码块,并返回其中最后一个语句的结果值,然后赋值给变量a。他的目的是将语句当做表达式来处理,而不用将语句封装成函数再用return返回47

2.上下文规则

在JavaScript语法规则中,有时候同样的语法在不同情况下会有不同的解释,这些语法孤立起来很难理解

1. 大括号
  • 对象常量
css 复制代码
var a = {foo:bar()}

{...}被赋值给了a,因为a是一个对象常量

  • 标签
css 复制代码
{
    foo:bar()
}

这并不是一个被孤立的对象常量,没有发生赋值,实际上{...}只是一个普通的代码块,在语法上是完全合法的,尤其与let在一起时非常有用

2. 代码块

这里有一个坑被经常提到

less 复制代码
[] + {} //"[object Object]"
{} + [] //0

在第一行代码中{}出现在+运算符表达式中,因此可以看成一个空数组和空对象的相加,需要通过ToPrimitive抽象操作(不清楚什么是ToPrimitive抽象操作可以看看这个5k字讲透强制类型转换 - 掘金 (juejin.cn))将其转化为字符串,而\[\]转化为'',{}则转化为"object Object",相加就是结果

在第二行代码中,{}被当成一个代码块(不执行任何操作)因此实际上就相当于+\[\],自然就被转化成0了

3. 对象解构

ES6开始,{...}可以被用于'解构赋值'

css 复制代码
function getData(){
    return {
        a:42,
        b:'foo'
    }
}

var {a,b} = getData()
a //42
b //'foo'

另外一种解构可能比较少人知道,{...}可以用作函数命名参数的对象解构

css 复制代码
function foo({a,b,c}){
    //不再需要这样
    //var a = obj.a,b = obj.b,c = obj.c
    console.log(a,b,c);
}

foo({
    a:42,
    c:1,
    b:333
})
4.else if

实际上JavaScript是没有else if的,只是if和else值包含单条语句的时候可以忽略代码块{...}

scss 复制代码
if(a) doSomething(a)

//实际的else if
if(a){
}else{
    if(b){}
}

运算优先级

1. &&和||和三元运算符

==和===的优先级比&&高,&&的优先级比||的优先级更高,因此代码的执行顺序不总是从左到右。而||的优先级比三元运算符高

bash 复制代码
true || false && false //true,先执行false && false
2.关联

&&的优先级比大于||大于三元运算符,那么当多个相同优先级的运算符同时出现时,该怎么处理呢

像a && b && c这样的组合就涉及到组合,意味着a && b或者b && c会先被执行

从技术角度来说,&&运算符是左关联,因此会被处理成(a && b) && c。但是需要注意的是,即便是右关联被处理成a && (b && c),并不是指从右往左执行,而是从右往左组合,也就是不会先执行(b && c),无论怎么组合,严格的执行顺序都是a,b,c

css 复制代码
a ? b : c ? d : e

对于三元运算符来说,他是右关联的(即先执行右边的),即上述代码会被处理成

less 复制代码
a ? b : (c ? d : e)

同样是右关联的还有=运算符

css 复制代码
var a,b,c
a = b = c = 42

他首先会执行c = 42,然后是b = ,然后是a = ,因为是右关联,所以他实际上是a=(b=(c=42))

less 复制代码
var a = 42
var b = 'foo'
var c = false

var d = a && b || c ? c || b ? a : c && b : a //42

根据优先级以及关联,这里会被处理成

less 复制代码
((a && b) || c) ? ((c || b) ? a :( c && b)) : a 
3. try..finally

try..catch..finaly的作用我们非常熟悉,finally中的代码总是会在try之后执行,如果有catch的话则在catch之后执行,也可以将finally中的代码当成一个回调函数,即无论出现什么情况都会被调用。

javascript 复制代码
function foo(){
    try {
        return 42
    }
    finally {
        console.log('hello');
    }
    console.log('never runs');
}

console.log(foo());
//hello
//42

这里首先执行foo()函数,先执行try中的return 42,但此时foo()还未执行完,因此不会立刻输出42。接着执行finally,输出hello,此时再输出42。即在try中无论是return还是throw抛出一个错误或者是continue,最后finally的代码还是会执行

如果finally中抛出了异常(无论是有意还是无意),函数就会在此处终止,如果此前在try中已经有了return返回值,则该值也会被丢弃

在finally中return会覆盖try和catch中return的返回值。

4.switch

switch可以看成if...else if..else的简化关系

arduino 复制代码
switch(a){
    case 2:
        //执行一些操作
        break;
    case 22:
        //执行一些操作
        break;
    default:
        //执行缺省代码
}

这里的a和case表达式逐一进行比较,如果匹配就执行case中的代码,直到break结束代码,但是这里还是有一些不太为人知的陷阱:

1.a和case表达式的匹配算法与===相同,即不会有隐式数据类型转换

2.有时可能需要强制类型转换的话需要进行一些处理

arduino 复制代码
var a = '42'

switch(true){
    case a == 10:
        console.log("10 or '10'");
        break;
    case a == 42:
        console.log("42 or '42'");
        break;
    default:
        //执行缺省代码
}

case中的表达式的结果值会和true进行比较,因为a == 42的结果为true,所以条件成立。但如果返回的结果并不是严格意义上的true,那么条件不会成立

csharp 复制代码
var a = 'hello world'
var b = 10

switch(true){
    case (a || b == 10):
        //永远不会走到这里
        break;
    default:
        //执行缺省代码
}

(a || b == 10)由于==的优先级比较高,因此这里的组合为(a || (b == 10)),返回值为'hello world',这不是严格意义上的true,而是真值,可以通过强制类型转换!!(a || (b == 10))来返回true

javascript 复制代码
var b = 10

switch(b){
    case 1:
    case 3:
    default :
        console.log('default');
    case 11:
        console.log(11);
    case 'a':
        console.log('a');
        break
    case 6 :
        console.log(6);
}
//default
//11
//'a'

这段代码是这样执行的,首先遍历并找到所有匹配的case,如果没有匹配则执行default中的代码,因为其中没有break,所以继续执行已经遍历过的11和'a'

完结撒花❀

相关推荐
清秋11 分钟前
全网最全 ECMAScript 攻略( 更新至 ES2026)
前端·javascript·编程语言
Cao_Ron28 分钟前
用 SVG image symbol 画 ECharts graph 圆形节点
前端
hxy060132 分钟前
React-Call让你的React组件像异步函数一样调用
前端·react.js
Hilaku33 分钟前
为什么 AI 写前端时,总是优先选择 React,而不是 Vue?
前端·javascript·程序员
京东云开发者35 分钟前
GPT-5.6、ChatGPT Work 与 Codex 更新食用指南
前端
Cao_Ron1 小时前
用 ECharts graph 画出卡片式矩形节点
前端
skiyee1 小时前
换个底座的 Wot Starter 居然让 AI 更懂项目!
前端·ai编程
anyup1 小时前
这一套绝了!AI 大模型写故事,星云 SDK 驱动 3D 数字人实时演绎
前端·人工智能·aigc
耀耀切克闹灬1 小时前
短链跳转
前端
孟陬1 小时前
高质量全方位的 AI 工具 sse-stuntman — Mock SSE 接口
前端·node.js·openai