[Effective JavaScript 笔记] 第6条:了解分号插入的局限

时间:2021-08-19 23:17:09

分号可以省略

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允许逗号分隔表达式,常用于声明多个变量,逗号分隔符用于赋值时,操作符总会返回最后一个表达式的值。

[Effective JavaScript 笔记] 第6条:了解分号插入的局限

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

+号:

[Effective JavaScript 笔记] 第6条:了解分号插入的局限

10是立即运行的结果

NaN是+undefined的结果

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

[Effective JavaScript 笔记] 第6条:了解分号插入的局限

/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++

){

//…

}

上面会出现语法错误。

[Effective JavaScript 笔记] 第6条:了解分号插入的局限

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

function infiniteLoop(){while(true)}

[Effective JavaScript 笔记] 第6条:了解分号插入的局限

必须写成

function infiniteLoop(){while(true);}

[Effective JavaScript 笔记] 第6条:了解分号插入的局限

提示

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

[Effective JavaScript 笔记] 第6条:了解分号插入的局限

[Effective JavaScript 笔记] 第6条:了解分号插入的局限的更多相关文章

  1. &lbrack;Effective JavaScript 笔记&rsqb; 第4条:原始类型优于封闭对象

    js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //&q ...

  2. &lbrack;Effective JavaScript 笔记&rsqb; 第5条:避免对混合类型使用&equals;&equals;运算符

    “1.0e0”=={valueOf:function(){return true;}} 是值是多少? 这两个完全不同的值使用==运算符是相等的.为什么呢?请看<[Effective JavaSc ...

  3. &lbrack;Effective JavaScript 笔记&rsqb;第27条:使用闭包而不是字符串来封装代码

    函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式 ...

  4. &lbrack;Effective JavaScript 笔记&rsqb;第28条:不要信赖函数对象的toString方法

    js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+ ...

  5. &lbrack;Effective JavaScript 笔记&rsqb;第68条:使用promise模式清洁异步逻辑

    构建异步API的一种流行的替代方式是使用promise(有时也被称为deferred或future)模式.已经在本章讨论过的异步API使用回调函数作为参数. downloadAsync('file.t ...

  6. &lbrack;Effective JavaScript 笔记&rsqb;第46条:使用数组而不要使用字典来存储有序集合

    对象属性无序性 js对象是一个无序属性集合. var obj={}; obj.a=10; obj.b=30; 属性a和属性b并没有谁前谁后之说.for...in循环,先输出哪个属性都有可能.获取和设置 ...

  7. &lbrack;Effective JavaScript 笔记&rsqb;第45条:使用hasOwnProperty方法以避免原型污染

    之前的43条,44条讨论了属性的枚举,但都没有彻底地解决属性查找中原型污染的问题.看下面关于字典的一些操作 'zhangsan' in dict; dict.zhangsan; dict.zhangs ...

  8. &lbrack;Effective JavaScript 笔记&rsqb;第67条:绝不要同步地调用异步的回调函数

    设想有downloadAsync函数的一种变种,它持有一个缓存(实现为一个Dict)来避免多次下载同一个文件.在文件已经被缓存的情况下,立即调用回调函数是最优选择. var cache=new Dic ...

  9. &lbrack;Effective JavaScript 笔记&rsqb;第66条:使用计数器来执行并行操作

    第63条建议使用工具函数downloadAllAsync接收一个URL数组并下载所有文件,结果返回一个存储了文件内容的数组,每个URL对应一个字符串.downloadAllAsync并不只有清理嵌套回 ...

随机推荐

  1. 天津*应急系统之GIS一张图(arcgis api for flex)讲解(六)地图搜索模块

    config.xml文件的配置如下: <widget label="地图搜索" icon="assets/images/emergency_resource_ove ...

  2. java class的兼容问题

    前不久在工作中,遇到了几次编译class引起的NoSuchMethodError,经过分析与测试验证,也算是搞清楚了中间的来龙去脉,现在把一些结论性的东西(附带一些过程性的分析)分享出来. 在使用ja ...

  3. HW5&period;31

    import java.util.Scanner; public class Solution { public static void main(String[] args) { Scanner i ...

  4. Qt动画与Qt坐标小记

    Qt动画 转载自: <http://jingyan.baidu.com/article/154b46315757b628ca8f4116.html> 和  <http://blog. ...

  5. 快速理解Parquet的DL和RL

    关于Parquet的详细介绍,请参考: 新一代列式存储格式Parquet,此文中对Parquet做了详细的介绍,这里不做重复介绍,但其中关于Definition Level(DL)和Repeated ...

  6. 【渗透笔记】拿下某小H网的全过程

    自从班上A片小王子的7个T资源被封了以后,本小白为造福全班同学,尝试拿下个小H网,先用webrobot搜某些只有小H网才会出现的关键词 本以为直接导出放御剑里跑就行了,然并软.于是用awvs扫了一下, ...

  7. 工作笔记-table问题汇总(vue单文件组件)

    1.vue: computed里定义的数据,在其他地方不能再重新赋值,会报错: Computed property "xxxxxx" was assigned to but it ...

  8. M2阶段团队个人贡献分

    团队个人贡献分: 徐钧鸿:53 张艺:48 黄可嵩:51 徐方宇:47 刘浩然:52 钟毅恒:49 杨伊:50

  9. android之lint警告This Handler class should be static or leaks might occur

    更新到adt2.0的开发者们可能会在handler上发现这么一条警告:This Handler class should be static or leaks might occur . 首先在ADT ...

  10. Scala 知识点掌握1

    Scala知识点巩固 1.Scala简介 Scala是一门面向对象和面向函数的编程语言,是一门静态编程语言,如 Java Scala(变量类型在编译阶段确定):源码文件需要基于 JVM 运行的. 动态 ...