Javascript中的类型和语法(六) --语法

(一)语句和表达式

语句相当于句子,表达式相当于短语,运算符则相当于标点符号和连接词。

var a = 3 * 6;//语句  3*6是表达式
var b = a;//语句 a是表达式
b;//既是语句又是表达式

1.1)语句的结果值

获得结果值最直接的方法是在浏览器开发控制台中输入语句,默认情况下控制台会显示所
执行的最后一条语句的结果值。

/**规范定义 var 的结果值是undefined。**/
var a = 42;//语句结果值 undefined

/**在控制台 /REPL 中输入以上代码应该会显示 42,即最后一个语句 / 表达式 b = 4 + 38 的结果值。**/
var b;
if (true) {
	b = 4 + 38;
}

/**语句结果值不能直接赋值给变量,但是可以通过do 表达式(ES7)或者eval函数(已不推荐使用)**/
var a, b;
a = do {
	//语句结果值不能直接赋值给变量,象a=if(true){...}这样是不行的
	if (true) {
		b = 4 + 38; 
	}
};
//a=eval("if (true) {b = 4 + 38; }");这样也可以,但不要这么使用
a; // 42

1.2)表达式的副作用

表达式会对变量的值进行改变,既是副作用(有可能产生意想不到的结果).

function foo() {
  a = a + 1;
}
var a = 1;
foo(); // 副作用:a的值被改变为2

var a = 42, b;
b = ( a++, a );//多个独立表达式串联,相当于b=a++; b=a;
a; // 43
b; // 43

1.3)上下文规则

标签:

/**在continue中的应用**/
// 标签为foo的循环
foo: for (var i=0; i<4; i++) {
	for (var j=0; j<4; j++) {
		// 如果j和i相等,继续外层循环
		if (j == i) {
			// 跳转到foo的下一个循环
			continue foo;
		}
		// 跳过奇数结果
		if ((j * i) % 2 == 1) {
			// 继续内层循环(没有标签的)
			continue; 
		}
		console.log( i, j );
	}
}
// 1 0
// 2 0
// 2 1
// 3 0
// 3 2
/**在break中的应用,可以跳出多层循环**/
// 标签为foo的循环
foo: for (var i=0; i<4; i++) {
	for (var j=0; j<4; j++) {
		if ((i * j) >= 3) {
			console.log( "stopping!", i, j );
			break foo; 
		}
		console.log( i, j );
	}
}
// 0 0
// ...省略循环结果
// 1 2
// stopping! 1 3

标签也能用于非循环代码块,但只有 break 才可以。

// 标签为bar的代码块,比较少见也不建议使用
function foo() {
	bar: {
		console.log( "Hello" );
		break bar;
		console.log( "never runs" );
	}
	console.log( "World" );
}
foo();
// Hello
// World

一个特殊的涉及强制转换的例子:

[] + {}; // "[object Object]" []转换成"",{}的toString为"[object Object]"
{} + []; // 0 这个{}被当成空的代码块,[]->""->0

对象解构:
ES6 开始,{ … } 也可用于“解构赋值”,特别是对象的解构。

function getData() {
	// ..
	return {
		a: 42,
		b: "foo" 
	};
}
var { a, b } = getData();
console.log( a, b ); // 42 "foo"
//{ a , b } = .. 就是 ES6 中的解构赋值,相当于下面的代码:
var res = getData();
var a = res.a;
var b = res.b;

//精简对象作为形参
function foo({ a, b, c }) {
	// 不再需要这样:
	// var a = obj.a, b = obj.b, c = obj.c
	console.log( a, b, c );
}

(二)运算符优先级

js中运算符优先级统计:

优先级运算类型关联性运算符
1圆括号( … )
2成员访问从左到右.
需计算的成员访问从左到右… [ … ]
new (带参数列表)new… ( … )
函数调用从左到右… ( … )
3new (无参数列表)从右到左new…
4后置递增、后置递减(运算符在后)… ++、… - -
5逻辑非、按位非从右到左!.. 、~…
一元加法、一元减法从右到左+…、-…
前置递增、前置递减从右到左++…、- -…
typeof、void、delete、await从右到左typeof…、void…、delete…、await…
6从右到左…**…
7乘法、除法、取模从左到右…*…、…/…、…%…
8加法、减法从左到右…+…、…-…
9按位左移、按位右移、无符号右移从左到右…<<…、…>>…、…>>>…
10小于、小等于、大于、大等于从左到右…<…、…<=…、…>…、…>=…
in、instanceof从左到右…in…、…instanceof…
11等号、不等、全等号,不全等从左到右==、!=、===、!==
12按位与从左到右… & …
13按位异或从左到右… ^ …
14按位或从左到右… | …
15逻辑与从左到右… && …
16逻辑或从左到右… || …
17条件运算符从右到左… ? … : …
18赋值从右到左…=…, +=, -=, *=, /=, %=, <<=, >>=, >>>=, &=, |=, ^=
19yield、yield*从右到左yield…、yield*…
20展开运算符
21逗号从右到左,
var a = 42, b;
b = ( a++, a );
a; // 43
b; // 43
//如果去掉 ( ) 会出现什么情况?
var a = 42, b;
b = a++, a;
a; // 43
b; // 42
//原因是 , 运算符的优先级比 = 低。所以 b = a++, a 其实可以理解为 (b = a++), a。

2.1)短路

对 && 和 || 来说,如果从左边的操作数能够得出结果,就可以忽略右边的操作数。我们将
这种现象称为“短路”(即执行最短路径)。
&&左边为假值,则不会执行右边的操作数;
||左边为真值,则不会执行右边的操作数;

2.2)关联

一般说来,运算符的关联不是从左到右就是从右到左,这取决于组合是从左开始还是从右开始。
请注意:关联和执行顺序(js的执行顺序是从左向右的)不是一回事。

var a = 42;
var b = "foo";
var c = false;
var d = a && b || c ? c || b ? a : c && b : a;
//?:是右关联,因此先找到(c || b ? a : c && b)
//等价于((a && b) || c) ? ((c || b) ? a : (c && b)) : a
d; // 42

建议:如果运算符优先级 / 关联规则能够令代码更为简洁,就使用运算符优先级 / 关联规则,例如if(a&&b&&c)而不是if((a&&b)&&c);
而如果 ( ) 有助于提高代码可读性,就使用 ( ),例如((a && b) || c) ? ((c || b) ? a : (c && b)) : a,便于理解。

(三)自动分号插入

有时 JavaScript 会自动为代码行补上缺失的分号,即自动分号插入(Automatic Semicolon Insertion,ASI)。

var a = 42, b 
c;//独立表达式,若第一行为var a = 42, b, ASI则不会自动补上分号

//ASI 在某些情况下很有用,比如:
var a = 42;
do {
 // ..
} while (a) // <-- 这里应该有;ASI会自动补上
a;

建议在所有需要的地方加上分号,将对 ASI 的依赖降到最低。

(四)语法错误

function foo(a,b,a) { } // 没问题
function bar(a,b,a) { "use strict"; } // 错误!
(function(){
	"use strict";
	var a = { 
		b: 42,
		b: 43
	}; // 错误!
})();
{
	a = 2; // ReferenceError!
	let a; 
}
{
	typeof a; // undefined
	typeof b; // ReferenceError! TDZ(Temporal Dead Zone,暂时性死区)
	let b;
}

(五)函数参数

只需遵守一个原则,将命名参数和 arguments 数组混用也不会出错:即不要同时访问命名参数和其对应的 arguments 数组单元。

/**虽然参数 a 和 b 都有默认值,但是函数不带参数时,arguments 数组为空。
相反,如果向函数传递 undefined 值,则 arguments 数组中会出现一个值为 undefined 的单元,而不是默认值。**/
function foo( a = 42, b = a + 1 ) {
	console.log(
		arguments.length, a, b,
		arguments[0], arguments[1]
	);
}
foo(); // 0 42 43 undefined undefined
foo( 10 ); // 1 10 11 10 undefined
foo( 10, undefined ); // 2 10 11 10 undefined
foo( 10, null ); // 2 10 null 10 null

//即使将命名参数和 arguments 数组混用也不会出错:
//只需遵守一个原则,即不要同时访问命名参数和其对应的 arguments 数组单元。
function foo(a) {
 console.log( a + arguments[1] ); // aarguments[0],访问arguments[1]安全!
}
foo( 10, 32 ); // 42

(六)try…finally

try 可以和 catch 或者 finally配对使用,并且必要时两者可同时出现。

  • finally先执行,在执行try中的return
function foo() {
	try {
		return 42;
	} 
	finally {
		console.log( "Hello" );
	}
	console.log( "never runs" );
}
console.log( foo() );
// Hello
// 42
  • 如果 finally 中抛出异常,函数就会在此处终止。如果此前 try 中 已经有 return 设置了返回值,则该值会被丢弃。
  • 如果 try中抛出异常,finally仍会执行,执行完再抛出异常。
function foo() {
	try {
		return 42;
	} 
	finally {
		throw "Oops!";
	}
	console.log( "never runs" );
}
console.log( foo() );
// Uncaught Exception: Oops!

function foo() {
	try {
		throw 42; 
	}
	finally {
		console.log( "Hello" );
	}
	console.log( "never runs" );
}
console.log( foo() );
// Hello
// Uncaught Exception: 42
  • continue 在每次循环之后,会在 i++ 执行之前执行 console.log(i),所以结果是 0…9 而非1…10。
for (var i=0; i<10; i++) {
	try {
		continue; 
	}
	finally {
		console.log( i );
	}
}
// 0 ... 9

(七)switch

  • switch()内的参数值是要和case后的值严格相等的,否则不会进入条件内。
  • 若不加break;代码会一直往下执行。
  • default并非必须的,放在非最后一行,会出现其他结果。
//a 和 case 表达式的匹配算法与 ===相同。通常 case 语句中的 switch都是简单值,所以这并没有问题。
switch (a) {
	case 2:
		// 执行一些代码
		break;
	case 42:
		// 执行另外一些代码
		break;
	default:
		// 执行缺省代码
}
//复杂的switch
var a = "42";
switch (true) {
	case a == 10:
		console.log( "10 or '10'" );
		break;
	case a == 42;
		console.log( "42 or '42'" );
		break;
	default:
		// 永远执行不到这里
}// 42 or '42'

//break和default
//default 是可选的,并非必不可少(虽然惯例如此)。break 相关规则对 default 仍然适用:
var a = 10;
switch (a) {
	case 1:
	case 2:
		// 永远执行不到这里
	default:
		console.log( "default" );
	case 3:
		console.log( "3" );
		break;
	case 4:
		console.log( "4" );
}
// default
// 3
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Funnee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值