Hello World
Spiga

JavaScript:假如default不是switch的最后一项

2011-05-24 11:33 by 老赵, 7322 visits

话说大家对于switch语句应该再熟悉不过了,各种类C语言都不例外,JavaScript自然也是如此。switch的逻辑很简单,根据switch内容的值执行对应的case项,否则执行default项即可。但是不同的语言在具体一些细节上面的处理却是不同的。例如在JavaScript里,每个case项都可以没有break,于是语句便会顺延到下个case或是default里面去——但某些语言设计者认为这种特性容易造成代码理解上的偏差,因此比如在C#里便要求每个非空的case都要有个break。那么再来一个细节问题:如果default之后还有case,那么会出现什么样的情况?如果default里没有break呢?

switch (a) {
    case 0:
        console.log("0");
    default:
        console.log("default");
    case 1:
        console.log("1");
}

就好比这段代码,当a等于0、1或2的时候,将会输出什么样的内容呢?先猜猜,别急着往下看。

当a等于0时,则会输出:

0
default
1

当a等于1时,则会输出:

1

当a等于2时,则会输出:

default
1

好吧,尽管这样的代码比较罕见,但执行结果也并没有什么“特殊”的。switch的规则依旧可以用一句话说清:如果匹配到某个case,则从该case处开始执行,否则就从default处开始执行,一直向下,直到出现break语句为止。至于default的位置是否在最后,对于执行的策略可谓完全没有影响。

当然,我实在没想到为什么有人会写这样的代码,所以假如有人对这点感觉恍惚我也觉得没太大关系。不过既然我要写Jscex,则还是必须对此类代码的行为有所了解。尽管语言的使用者可以选择合适的子集,但语言的开发者(编译器、解释器等等)却必须遵循完整的规范,这是Jscex这类项目需要应对的麻烦。

既然Jscex号称支持“全部JavaScript语言特性”,自然对switch的支持也在包括在内。switch的麻烦之处在于它的每个分支不像if语句那样完全相互独立,而是会不断“穿透”下去直至遇上break。因此Jscex在处理switch的时候也使用了一些技巧。例如下面这段代码:

switch (a) {
    case 0:
        $await(helloWorld());
    default:
        console.log("default");
    case 1:
        console.log("1");
}

Jscex会将每个case及default中的语句“补齐”,以“确保”每项里都有完整的语句以及最后的break:

switch (a) {
    case 0:
        $await(helloWorld());
        console.log("default");
        console.log("1");
        break;
    default:
        console.log("default");
        console.log("1");
        break;
    case 1:
        console.log("1");
        break;
}

然后再将其编译为:

switch (a) {
    case 0:
        return $$_builder_$$_0.Bind(helloWorld(), function () {
            console.log("default");
            console.log("1");
            return $$_builder_$$_0.Normal();
        });
    default:
        console.log("default");
        console.log("1");
        return $$_builder_$$_0.Normal();
    case 1:
        console.log("1");
        return $$_builder_$$_0.Normal();
    }
})

自然,如果switch里没有包含bind操作(例如$await语句),则整个switch语句都会得以保留,这也是Jscex编译结果的优化策略之一。

Creative Commons License

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名赵劼(包含链接),具体操作方式可参考此处。如您有任何疑问或者授权方面的协商,请给我留言

Add your comment

39 条回复

  1. jeffwong
    218.247.215.*
    链接

    jeffwong 2011-05-24 12:42:34

    看后的浅薄感想:

    1. switch的这种写法确实令人恍惚,但是我又知道了一个“技巧”。
    2. “尽管语言的使用者可以选择合适的子集,但语言的开发者(编译器、解释器等等)却必须遵循完整的规范,这是Jscex这类项目需要应对的麻烦。” 老赵你不觉得有时候这种遵循规范的做法看上去有一点“将错就错”的感觉吗?
  2. 老赵
    admin
    链接

    老赵 2011-05-24 13:11:07

    @jeffwong

    没办法,这是规范。要我设计JavaScript,我不会这么设计,但既然我是实现JavaScript,那只能实现这些……

  3. stevey
    116.226.28.*
    链接

    stevey 2011-05-24 13:17:38

    switch (a) {
        case 0:
            console.log("0");
        default:
            console.log("default");
        case 1:
            console.log("1");
    }
    

    老赵有人会这样写代码吗?阅读性不强啊,让阅读的人感觉有歧义。如果没有写break,就直接自动帮他加个 break。

    switch (a) {
        case 0:
            console.log("0"); break;
        default:
            console.log("default"); break;
        case 1:
            console.log("1"); break;
    }
    

    我想不知道这个特点的人,他的本意估计是忘记写break了,如果这个规范没必要,何必去遵循呢?

    个人之见,不对之处还请批评指正!

  4. 老赵
    admin
    链接

    老赵 2011-05-24 13:31:43

    @stevey

    Jscex只是个编译器,不能改变代码的行为。如果Jscex真希望杜绝别人写出没有break的switch,也应该“引发错误”而不是“自动加上break”,否则就变成改变代码的执行行为了。至于本意什么的,实在不好说,我唯一该做的就是保证语义。

  5. xanpeng
    58.101.89.*
    链接

    xanpeng 2011-05-25 00:54:00

    经验证,gcc 也是如此

  6. 老赵
    admin
    链接

    老赵 2011-05-25 10:52:44

    @xanpeng

    “如此”是指?

  7. fishcatcher
    124.14.51.*
    链接

    fishcatcher 2011-05-25 21:33:28

    原来如此啊!

  8. 陈澄
    61.171.199.*
    链接

    陈澄 2011-05-25 21:49:10

    尽管语言的使用者可以选择合适的子集,但语言的开发者(编译器、解释器等等)却必须遵循完整的规范,这是Jscex这类项目需要应对的麻烦。

    那Jscex会不会将开发者的糟糕代码优化成使用js good parts呢?

    因此比如在C#里便要求每个case都要有个break

    非空的case吧?空的case可以没有break。

  9. 老赵
    admin
    链接

    老赵 2011-05-26 00:59:22

    @陈澄: 那Jscex会不会将开发者的糟糕代码优化成使用js good parts呢?

    Jscex不是代码优化器,是编译器,某种程度上说,跟一个JavaScript执行引擎做的是差不多的事情。

  10. 管宇
    1.202.91.*
    链接

    管宇 2011-05-27 08:24:31

    switch (a) {
        case 0:        
            break;
        case 1:        
            break;
        default:        
            break;
    }
    

    已经习惯了这种写法。

  11. amenamena
    114.251.186.*
    链接

    amenamena 2011-05-27 15:31:51

  12. 小牛
    222.73.22.*
    链接

    小牛 2011-05-27 18:33:14

    看到你的主页用到了Gavatar,请问你是使用md5.js来生成Gavatar需所要的序列吗?

    如果是,在客户端用js怎么做的?

    谢谢!

  13. 老赵
    admin
    链接

    老赵 2011-05-27 19:56:10

    @小牛

    很显然是服务器端生成的么……

  14. 老赵
    admin
    链接

    老赵 2011-05-27 19:59:28

    @amenamena

    很显然这位熊大哥不怎么了解.NET及其用法么……

  15. zzfff
    222.53.147.*
    链接

    zzfff 2011-05-27 21:13:51

    看了熊大哥的tricks,我骂人又有了新招:您是TW出来的么?您们全家都是TW出来的!

    妈痹啊妈痹,80后要狂,至少要像老赵这样有货才行嘛!

  16. zzfff
    222.53.147.*
    链接

    zzfff 2011-05-27 21:32:18

    线程啊,为什么.net中instance methods不是线程安全的、而static methods是线程安全的...这些老熊不懂我倒能宽容,“List不能赋值给IList,IList不能赋值给ICollection…”这句话让我极度百思不得其解:

    public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable
    

    天啊!

  17. zzfff
    222.53.147.*
    链接

    zzfff 2011-05-27 22:50:52

    说个稍微靠谱的。TPL Dataflow——actor/agent-oriented、message passing、pipelining式的concurrency,没见老赵研究呢?

  18. 小牛
    124.14.52.*
    链接

    小牛 2011-05-28 15:25:13

    有点激动了啊!

  19. zzfff
    222.53.139.*
    链接

    zzfff 2011-05-28 15:57:11

    宗教战争之帝国反击战

    老赵你不是常说.NET社区太温顺底气不足吗?来,咱俩联手,你讲道理,我谩骂,干死那群侮辱本社区的傻逼装逼犯们!

    兔年兔月兔日,霸气升腾!信窗子教,得$,得老婆!!

  20. 链接

    cnn 2011-05-29 02:57:18

    老赵你好。凌晨2点半,没睡着。经常浏览你的博客,很不错,学到很多。

    最近在找工作,不知你有空能否写个文章谈谈工作面试的问题,以及你有什么经验和建议。

    本人顺便在此发一下面试的牢骚。本人工作也许多年了,主要专注于.net开发,每次面试前都会复习一下一些基本概念。最近面了个公司,说实话,感觉很不爽。主要有以下几个问题。 面试官不知所云,说的问题都不知怎么回答。 比如,本人简历中写道,熟悉.net调试。结果,被问到一堆调试快捷键,并且f11和f10的区别。本人当场就觉得不爽,感觉有侮辱人的意思,问的问题毫无水平,但还是老老实实回答。而且,vs工具,有不同的快捷键环境,我只是按照我用的C#环境所说。

    还有算法题目,随随便便说个算法名字,然后就问我算法复杂度,说实话,本人大学期间学过算法,但是工作中真的很少用到,因为很多算法都有现成的。因此,确实不太了解,但是这并不代表我不懂算法。期间的一个面试官,问我一个数组,全是0和1,如何排序。本人当场觉得这种问题太无意义,全是0,1排序干嘛,数一下有多少个0,移到前面不就ok。但是本人还是说了一个二分排序,因为我依稀记得这个排序方式貌似算法复杂度最低。但仅仅是猜测,说实话,这种题目我手边没有一本算法书,我是回答不出来的。因为陌生了很久。还有很傻的问题,比如asp.net mvc开发的时候,如何对url重写,本人也很无奈,mvc的开发,本来就对url有着很高的可读性,重写的话就是配置路由,我没搞懂这个重写是什么意思,只好说没做过。面试官就一个鄙视的眼神。 还有再面试时候,经常会有些细节问题。说实话我都知道,都曾经了解过学习过,但忘性大,很多东西很久不用就会忘记,比如virtual函数,被重写的时候,可以写new,override,但是重写的时候什么都不写,会有什么情况。本人遇到这种问题,也会经常无奈,说实话,很少碰到这种情况,我相信答案要么是报错,要么是默认给他一个new或者override,但不亲自测试一下。实在不确定是什么。我不知道老赵你是不是对这种面试问题能过做到对答如流。 我相信,即使你回答不出,也不会觉得你没水平。但是作为我这种普通程序员,面试的时候,回答不出,就会给面试官一个没水平,基础功不扎实的印象。本人甚是郁闷。

    总觉得面试时候,明明一肚子的知识,但是就是没法倾倒出来。所以暗自伤神。

    不知老赵最近有没有参加被面试的经历,面试的时候对面试官的问题是否都是对答如流,如果老赵也觉得有些问题有印象,但是要翻翻书才能确定,那本人也就坦然了。呵呵。

    以上这些抱怨,本人随口说说。 老赵若是有空的话,写些面试经吧。多谢。睡觉了。

  21. 冤孽
    91.140.120.*
    链接

    冤孽 2011-05-29 04:19:03

    编译器前端对switch/case不是应该用jmp table吗?

    另:totworks及其员工是个十足的笑料源

  22. 老赵
    admin
    链接

    老赵 2011-05-30 11:41:45

    @冤孽

    那是编译至Native Code的编译器。

  23. 老赵
    admin
    链接

    老赵 2011-05-30 11:47:17

    @zzfff: 说个稍微靠谱的。TPL Dataflow——actor/agent-oriented、message passing、pipelining式的concurrency,没见老赵研究呢?

    Message Passing之前都讨论过很多了……最近又懒了……

  24. 老赵
    admin
    链接

    老赵 2011-05-30 13:39:56

    @cnn

    如果是我,如果答不出来,就会说我为什么答不出来,然后谈点相关的。比如URL Routing里的“URL重写”,就说“不明白什么意思,不过我知道如果在ASP.NET是怎么实现重写的,URL Routing是怎么使用重写来实现的”。

  25. cnn
    180.172.78.*
    链接

    cnn 2011-05-31 11:34:26

    那老赵能写个文章谈谈面试经吗

  26. 燕窝
    121.207.162.*
    链接

    燕窝 2011-05-31 16:24:42

    规范的事情··没的讲哦··

  27. libinqq
    221.4.170.*
    链接

    libinqq 2011-06-06 17:33:01

    老赵什么时候更新博客啊,大家等着看你完善呢。

  28. ??
    221.217.40.*
    链接

    ?? 2011-06-07 09:06:14

    @老赵

    看了文章又看回复,没明白你到底自动加了break还是没有,其次:

    但既然我是实现JavaScript,那只能实现这些

    并且把这个东西称为编译器,这太唬人了。

    你既没有实现前端(根据以往帖子没弄错的话)、也没有实现后端,虽然广义上来说确实可以叫编译器,但其实质不过是个改写器,这么叫也许初学者很佩服,但是会让懂点行的人B4的。(哪怕更容易的自写XML解析器+背后的某个根据数据做一些复杂点的代码生成或者执行操作的东东,都比你这个更配叫做编译器/解释器)

    我觉得XX rewriter或者XX引擎XX框架是个不错的选择,否则那些精简器全把自己叫做编译器好了,虽然目的和你这个不同。

  29. 老赵
    admin
    链接

    老赵 2011-06-08 14:42:38

    @??

    Google Closure Compiler都叫Compiler,为什么我不能叫编译器?Closure Compiler做的还是等价变换,我可是完全改写的,哈哈。

  30. libinqq
    125.95.75.*
    链接

    libinqq 2011-06-09 11:42:48

    建议老赵出一本书吧,就把几年的javascript 的心得都写上,包括你的异步编程,如果出了价格在50元左右的,我肯定买,估计受博客园知名度来讲, 只要书写的好,销量不成问题,把你的心得表达出去。

  31. zzc
    180.168.111.*
    链接

    zzc 2011-06-21 15:46:03

    至今没看到国内哪位大大出一本不错的JS教程.

    实在搞不懂 default后面还会出现case,这不就是程序写错了吗?老赵再把错误的程序编译正确的意义是什么呢?支持错误的程序吗?

  32. 链接

    Ivony 2011-06-22 11:17:49

    switch的本意就是一大堆if goto,在C、C++、C#、JavaScript...都是这样,只是C#考虑比较周到,怕有些程序员忘了写break,对于两个跳转标记之间存在代码,且不是以break或goto语句结尾的情况报个编译错误。

  33. 老赵
    admin
    链接

    老赵 2011-06-22 11:56:49

    @zzc

    default后面有case不是错误程序,是合法的。

  34. xiami
    116.236.188.*
    链接

    xiami 2011-08-03 16:22:08

    我该称呼什么好呢,赵老师博客很棒,还有我感觉在遇到困难的时候您是很主动去展示自己的,这点估计是cnn缺乏的,呵呵,咱要把不懂乱问的人引导到他听着很玄乎的“领域”嘛。。。

  35. itsuki
    124.134.245.*
    链接

    itsuki 2011-08-11 20:13:39

    还有这么种写法

    switch (a) {
        case "a":
        case "b":
        case "c":
              ...
        break;
    }
    

    相当于if里的3个或

  36. 老赵
    admin
    链接

    老赵 2011-08-11 23:20:19

    @itsuki

    这个再正常不过了……

  37. XiNGRZ
    113.105.12.*
    链接

    XiNGRZ 2011-12-28 14:23:43

    我想起了曾经看过一种更坑爹的switch用法:

    $i = 1;
    switch (true)
    {
        case ($i < 0):
            echo 'i < 0';
            break;
        case ($i > 0):
            echo 'i > 0';
            break;
        default:
            echo 'i = 0';
            break;
    }
    

    这个是PHP里的,JS能否这样就不知道了,呵呵

  38. wjsl
    123.127.237.*
    链接

    wjsl 2012-01-12 16:56:39

    看了老赵的一些文章觉得质量还可以,但这篇文章介绍的有点小儿科了。

    MS的《jscript手册》上的说明讲得很明白。

    使用 default 子句来提供一个语句,该语句只在没有任何一个标签值与 expression 相匹配时才被执行。它可以出现在 switch 代码块内的任何地方。

    可以指定零或多个 label 块。如果没有 label 和 expression 的值匹配,并且没有提供 default 情况,则不执行任何语句。

    通过 switch 语句执行流程如下:

    1. 求 expression 的值并依次序查看 label,直到找到一个匹配。
    2. 如果 label 的值等于 expression 的值,则执行它相应的 statementlist。
    3. 继续执行,直到遇到一个 break 语句,或者 switch 语句结束。这意味着如果没有使用一个 break 语句,则多个 label 块被执行。
    4. 如果没有 label 等于 expression 的值,则跳转到 default 情况。 如果没有 default 情况,则跳转到最后一步。
    5. 继续执行紧接 switch 代码块末尾的语句。
  39. zero
    202.133.226.*
    链接

    zero 2012-02-20 14:09:43

    我觉从编译器设计角度而言这个情况是最简单的,也是让程序员可以拥有最大自由的做法。考虑到switch case出现的年代,插入跳转指令这种做法会被认为是低效的,也对程序编写者带来的太大的限制。

发表回复

登录 / 登录并记住我 ,登陆后便可删除或修改已发表的评论 (请注意保留评论内容)

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

评论内容(大于5个字符):

  1. Your Name yyyy-MM-dd HH:mm:ss

使用Live Messenger联系我