zoukankan      html  css  js  c++  java
  • [Effective JavaScript 笔记] 第6条:了解分号插入的局限

    分号可以省略

    js可以在语句结束不强制加分号。(建议还是添加,不添加分号往往会出现不易发现的BUG

    function Point(x,y){

                this.x=x||0;

                this.y=y||0;

    }

    Point.prototype.isOrigin=function(){

              return this.x===0 && this.y===0

    }

    上面代码可以运行,是由于js可以自动插入分号,它是一种程序解析技术。能推断出某些上下文中省略的分号,然后有效地自动地将分号“插入”到程序中。

    ECMAScript标准细心地制定了分号插入机制,可以方便移植。

    分号插入的陷阱

    不能避免学习其规则,受分号插入的影响,js语法有一些额外的限制。

    分号插入的原则

    第一条原则

    分号仅在}标记之前、一个或多个换行之后和程序输入的结尾被插入。

    讲人话就是,你只能在一行、一个代码块和一段程序结束的地方省略分号。

    function square(x){

        var n=+x

        return n*n

    }

    function area(r){r=+r;return Math.PI*r*r}

    function add1(x){return x+1}

    不合法的

    function area(x){r=+r  return  Math.PI*r*r}

    第二条原则

    分号仅在随后的输入标记不能解析时插入。

    人话:分号插入是一种错误校正规则。

    a=b

    (f());

    能解析为一条单独的语句,等价于:

    a=b(f());

    没有分号插入,因为b(f())是一个合法的语句

    a=b

    f();

    会解析为两条独立的语句

    a=b f();

    解析有误。

    这条说明你省略分号的时候一定要小心。

    有问题的字符

    5个明确有问题的字符需要密切注意:(、[、+、/。

    每个字符都能作为一个表达式运算符或一条语句的前缀,依赖于上下文。

    小心以表达式结束的语句,如赋值语句。下一行以上面这5个字符的其中之一开始,不插入分号。

    出问题最好的是以(或[开头的语句。

    a=b

    [“r”,”g”,”b”].forEach(function(key){

            bakcground[key]=foreground[key]/2;

    });

    这句话会被等价于

    a=b[“r”,”g”,”b”].forEach(function(key){

    bakcground[key]=foreground[key]/2;

    });

    注意:中括号表达式有点怪,js允许逗号分隔表达式,常用于声明多个变量,逗号分隔符用于赋值时,操作符总会返回最后一个表达式的值。

    image

    +、-、/字符出现在语句开始并不常见,但也有这种情况。

    +号:

    image

    10是立即运行的结果

    NaN是+undefined的结果

    /号:出现在语句的开始实际不是一个入口标记,而是正则表达式

    image

    /Error/i.test(str)&&fail();

    (&&是一种短路操作,即如果第一个操作数能够决定结果,那么就不会再对第二个操作数求值。)

    如果/Error/i.test(str)结果为真,才会执行fail()操作。

    但如果情况如下

    a=b

    /Error/i.test(str)&&fail();

    会被解析为

    a=b/Error/i.test(str)&&fail();

    /被当成除法运算符了。

    想省略分号,可以在语句后跟一个声明,以保证语句不会被错误解析。

    a=b

    var x

    (f())

    重构时可能被人改为

    var x

    a=b

    (f())

    虽然上面把var语句提前,这两段代码应该是等价的。但事实上,b后面跟着的一个括号,会被错误地解析为

    var x;

    a=b(f());

    结果,你还是要小心省略分号的地方,检查接下来的一行开始的标记是否会禁止自动插入分号。

    或者在(、[、+、/字符的开始前置加一个额外的分号语句的方法。

    a=b

    var x

    ;(f())

    现在把var提前也不会出错了。

    var x

    a=b

    ;(f())

    另外一个常见的情况是,省略分号可能导致脚本连接问题。每个文件可能由大量的函数表达式组成。

    file1.js

    (function(){

         //…..

    })()

    file2.js

    (function(){

       //…..

    })()

    当合并压缩时,结果被视为

    (function(){

    //….

    })()(function(){

    //….

    })();

    省略分号时,不仅要当心当前文件的下一个标记,而且要当心脚本合并时可能出现在语句之后的任一标记。

    可以防御性在加一个前缀的分号以保护脚本免受合并的影响。

    file1.js

    ;(function(){

    //…..

    })()

    file2.js

    ;(function(){

    //…..

    })()

    合并后

    ;(function(){

    //….

    })();(function(){

    //….

    })();

    脚本正常运行。

    那么不省略任何分号,是不是就没有问题了呢?也不尽然,如

    在打

    return {};

    时打成

    return

    {};

    没有返回一个对象,因为上面这句被解析成

    return ;

    {}

    ;

    3条单独的语句。

    这就是所谓的js语法限制产生式,它不允许在两个字符之间出现换行。如果存在换行,可能会被自动插入分号。

    如上面的return 语句。在return关键字和其可选参数之间一定不能出现换行.

    其它的限制产生式:

    • throw语句
    • 带有显式标签的break或continue语句
    • 后置自增或自减运算符

    最后一条是为了消除以下情况

    a

    ++

    b

    会被解析为

    a;++b;

    第三条规则

    分号不会作为分隔符在for循环空语句的头部被自动插入。

    人话:你必须在for循环的头部显式地包含分号。

    for(var i=0,len=20

         i<len

         i++

    ){

       //…

    }

    上面会出现语法错误。

    image

    空循环的while同样也需要显式的分号。否则也会报错

    function infiniteLoop(){while(true)}

    image

    必须写成

    function infiniteLoop(){while(true);}

    image

    提示

    1. 仅在“}”标记之前、一行的结束和程序的结束处自动插入分号
    2. 仅在紧接着的标记不能被解析的时候插入分号
    3. 在以(、[、+、-、/字符开头的语句前绝不能省略分号
    4. 当脚本文件进行连接时,在脚本开头加上防御性的分号
    5. 在return、throw、 break 、continue、 ++、 --的参数之前绝不能换行
    6. 分号不能作为for循环的头部或空语句的分隔符而自动插入

    image

  • 相关阅读:
    假期总结三
    假期总结三
    假期总结三
    假期总结三
    Redis 在线管理工具(phpRedisAdmin)介绍 两次git
    Redis 在线管理工具(phpRedisAdmin)介绍 两次git
    Redis 在线管理工具(phpRedisAdmin)介绍 两次git
    Redis 在线管理工具(phpRedisAdmin)介绍 两次git
    进程
    C# exe文件 添加到windows 服务
  • 原文地址:https://www.cnblogs.com/wengxuesong/p/5479160.html
Copyright © 2011-2022 走看看