《你不知道的JavaScript》阅读笔记(5)
语句和表达式
语句:相当于句子,表达式:组成句子的短语,逗号等号则为中间的连接符
在JS中语句其实也是有返回值的
var a=42
//undefined
如果你在浏览器控制台中写过这个语句就会看到这个返回值,JS规定了var的返回值为undefined,
代码块的结果值是最后一个表达式的结果值,但是我们不能用这个返回值来赋值,虽然可以用万恶的eval,但并不推荐
if(true){
b=3+4
}
//7
表达式的副作用
递增运算符++,递减运算符- -都是会产生副作用的
delete用来删除一个对象中的属性,如果操作成功则返回true,同时对象的属性就会被删除,这也是delete方法的副作用
很多表达式都具有副作用,只不过你平时并不关心
上下文规则
在JS中使用大括号单独执行一个代码块其实是合法的语句,只是我们不常见,如下代码其实是完全合法的
{
foo:bar() //假设bar已经在之前声明过了
}
引申下话题,你可能会觉得foo:bar()这句可能看着很奇怪,其实这涉及到JS的标签,foo其实就是bar()的标签了,只不过可能你在c++中会遇到goto语法你就可以goto foo来执行bar(),好在JS中并没有这个语法,但是JS中可以通过标签来实现部分功能,如下你可以通过标签的方式直接跳到外层for循环
foo:for(var i=0;i<4;i++){
for(var j=0;i<4;j++){
if(i===j){
continue foo//跳到外层
}
}
}
标签还有更强大的功能,比如在内层循环直接结束外层循环,不使用标签则需要一定代码量
foo:for(var i=0;i<4;i++){
for(var j=0;i<4;j++){
console.log(i,j)
if(i*j>3){
break foo//跳到外层
}
}
}
// 0 0
// 0 1
// 0 2
// 0 3
// 1 0
// 1 1
// 1 2
// 1 3
运算符优先级
当有多个运算符的时候我们就需要考虑运算符的优先级了
var a=42,b;
b=(a++,a);
a//43
b//43
上面代码先执行a++,然后再把a赋值给b,所以a、b都为43
在看下面代码,如果我们去掉了括号,这时候++的优先级是比等号低,所以b为a加1之前的值
var a=42,b;
b=a++,a;
a//42
b//43
在比如,当等号和&&在一起的时候,&&的优先级会高于等号,如下,一定要用括号把右边部分括起来,否则会出错
if(a&&(b=/s+/.test('111'))){
....
}
平时我们用到的&& 、||其实也有优先级,&&的优先级会比||高,可以看下面的例子
true&&false||false //false
true||false&&false //true
&&和||具有短路特性,这在我们平时也是非常常见的,比如一个对象未赋值,但是你访问了它的属性,它就会报错,否则如果我们使用&&则可以通过短路特性来规避到报错,如下
a&&a.val
tips:&&>||>三目运算符
如果多个相同的优先级的运算符同时出现,JS一般执行顺序不是从左往右就是从右往左,
&&和||一般就是左关联,而三目运算则是右关联
a?b:c?e:f
//它的执行顺序为 a?b:(c?e:f),左关联其实并不会影响结果,但是右关联是会的
等于号也是右关联的
var a,b,c;
a=b=c=43
//实际执行顺序为a=(b=(c=43))
JS的解释器具有自动分号插入功能(ASI),他会在换行处的末尾如果除了注释的没有其他代码的话会自动插入分号,这其实是JS的一个语法纠错机制,因为如果你缺少必要的分号就会进行报错,当然我们平时还是多写写分号为好,因为有时候ASI会改变你语句的含义
JS对于编译时产生的问题称之为早起错误,比如
a=,; //错误的语法
var a=/+foo/ //错误的正则
42=a;
es6引入的let可以创建块级作用域,它不能进行变量提升,在当前块内使用这个变量会报错,且声明之前的部分叫做暂时性死区
{
//暂时性死区
let a=3;
}
暂时性死区不光在块内会产生,在函数的参数中也会产生,如下
function foo(a=3,b=a+b+3){
....
}
第二个参数在等号右边的b就在暂时性死区中,无法访问到b;
es6中函数参数默认值会导致arguments数组和参数出现偏差
function foo(a=1,b=2){
console.log(arguments[0])
}
foo(); //undefined
foo(3) //3
try...finally:如果在try的时候返回一个值,并执行这个函数,执行的函数和finally哪一个先执行呢答案是finally先执行
function foo(){
try{
return 2;
}finally{
console.log('finally')
}
}
console.log(foo())
// finally
//2
如果是在finally中return一个值,则会覆盖掉try中的值