美文网首页让前端飞Web前端之路web前端 - 从菜鸟到大神
JS学习系列 07 - 标签声明(Label Statement

JS学习系列 07 - 标签声明(Label Statement

作者: liuxuan | 来源:发表于2018-05-30 14:49 被阅读6次

    1. 引言

    假设有这么一道题:

    for (var i = 0; i < 10; i++) {
        console.log(i);
        for (var j = 0; j < 5; j++) {
            console.log(j);
        }
    }
    
    console.log('done');
    

    我想要当 j = 2 的时候就退出所有的for语句,打印最后的 done ,你会怎么做?

    可能有的同学会想到这样:

    
    function foo () {
        for (var i = 0; i < 10; i++) {
            console.log(i);
            for (var j = 0; j < 5; j++) {
                console.log(j);
                if (j === 2) return;
            }
        }
    }
    
    foo();
    
    console.log('done');
    

    这样可以实现,但是又多写了一个函数,那么有没有别的办法呢?

    再看一个例子,你也一定见到过这样的写法:

    // 假设str是你通过ajax接收到的JSON串
    var str = '{"name": "liu", "age": 20}';
    var obj = eval('(' + str + ')');
    
    console.log(obj); 
    

    那么,你有没有想过 eval 里面为什么要加上括号呢?如果不加又是什么情况?(提前剧透,不加括号这里会报错哦)。

    接着往下看,当你读完这篇文章的时候,心中的疑惑会完全解开。

    2. Label Statement

    学过C语言的同学知道,C的语法中有一个语句叫:goto,同时老师也多次强调不让我们使用goto语句,因为会大大影响程序的可读性可维护性

    我们先来看一段C语言的goto代码:

    void main(){
        int a=2, b=3;
        
        if(a>b) {
            goto aa;
        }
        
        printf("hello");
        
        aa: printf("s"); 
        
        return 0;
    }
    

    当 a < b 的时候,这里会打印字符串 "hello",然后结束。
    当 a > b 的时候,由于goto语句的作用,就会跳过 print("hello"),直接跳到 aa 标签声明的代码块中,打印字符 "s",然后结束。

    这就是goto语句的作用,通过标签声明一个代码块,然后在任何地方都可以执行 goto 'labe' 来进行程序跳转。

    显而易见,这样的写法,违背了程序顺序执行的原则,会跳来跳去,最后导致根本无法维护,所以,记住老师的话,不要使用 goto 语句

    那么,看完了C语言中的 goto 语句,和我们的 JavaScript 又有什么关系呢?
    这就引出了今天的主题:Label Statement,它就是 JS 中的 goto 语句。

    3. 用法

    首先明确一个原则,在JavaScript中,语句优先
    也就是说,如果一段代码既能够以语句的方式解析,也能用语法的方式解析,在JS中,会优先按语句来解析。

    { a : 1 }
    

    上面这段代码,在JS中的执行结果是什么呢?
    大家思考2分钟....



    好,2分钟已过,大家有结果了吗?
    千万不要在浏览器的控制台中去写这段代码,虽然结果和你开始想的结果一样,
    但是,它是错误的。

    这是在console控制台中执行的结果:

    label-console图片

    这是在watch中的执行结果:

    clipboard.png

    可以看到两个结果是不一样的。
    console是经过处理的这里不能相信,watch是直接JS的运行环境执行后的结果,是正确的。

    为什么 { a : 1 } 结果会是 1 呢?

    我换一个写法:

    {
        a : 1
    }
    

    相信有的同学已经明白了,在JS中,{}既可以代表代码块,又可以作为Object的语法标志。
    那么我们前面说过,JS是语句优先的,当一段代码既可以按照语句解析,又可以按照语法解析的时候,会优先按语句解析。

    当把{}当做是代码块的时候,里面的 a : 1,是不是很像C语言goto语句的标签声明呢?
    开头我们提出的第一个问题,如果用这种方式来解决,代码如下:

    aa : {
        for (var i = 0; i < 10; i++) {
            console.log(i);
            for (var j = 0; j < 5; j++) {
                console.log(j);
                if (j === 2) break aa;
            }
        }
    }
    
    console.log('done');
    

    aa是标签声明,包裹一个代码块,break 的作用是跳出当前的循环,本来是无法跳出外面那层for循环的,但是 break aa,这里跳出了整个代码块。

    当然,这种写法是完全不提倡的,这里只是用来说明JS中的Label Statement这个特性,大家千万不要这样写代码。

    再来看开头提出的第二个问题:

    // 假设str是你通过ajax接收到的JSON串
    var str = '{"name": "liu", "age": 20}';
    var obj = eval('(' + str + ')');
    
    console.log(obj); 
    

    我们知道,eval(str)会把接收到的字符串在当前上下文中执行,如果不加括号:

    eval('{"name": "liu", "age": 20}}')
    

    这里的执行语句就会变成:

    {
        "name" : "liu", "age" : 20
    }
    

    {}按照语句解析,执行里面的逗号表达式,我们知道逗号表达式要求每一项都必须是表达式,输出最后一项的结果,而这里不满足要求,所以会报错。

    label-watch2

    但是加上括号就变成了这样:

    ({
        "name" : "liu", "age" : 20
    })
    

    小括号可以把里面的内容当做表达式来解析,那么里面的内容就是一个对象了。

    label-watch2

    这也是立即执行函数的原理:

    (function () {
        console.log('IIFE');
    })()
    

    小括号把函数声明变成了函数表达式,后面再跟一个小括号表示调用。

    4. 结束

    这里通过几个例子,引出了 JavaScript 的标签声明语句(Label Statement),从而解释了一些我们常用写法的原理。

    以后万一有人问你为什么 eval() 解析JSON要加括号呢?
    这回知道怎么说了吧。

    相关文章

      网友评论

        本文标题:JS学习系列 07 - 标签声明(Label Statement

        本文链接:https://www.haomeiwen.com/subject/oyotsftx.html