JavaScript 学习笔记 之 语法

自动分号

JavaScript中存在着自动补上分号的行为,即自动分号插入(缩写ASI)

ASI只在换行处起作用,而且只有在代码行末尾只有空格或注释的时候才会自动补上分号

		//不会自动补分号
		var a,
		    b

		//会自动补分号
		var a
		b

JavaScript中规定do..while后面必须带分号,而while和for循环则不需要

这时候ASI就非常有用了

此外还需要注意的是break,continue,return,和yield(ES6)等关键字也会在后面补分号

所以换行的时候需要格外考虑是否ASI是否会对代码逻辑产生影响

		function fun1() {
			return
			1 + 1
		}
		fun1() //undefined

		function fun2() {
			return 1 + 1
		}
		fun2() //2

 

错误

JavaScript中的错误除了运行时错误(TypeError,ReferenceError,SyntaxError等)

还有一些编译时错误

这些编译阶段发现的代码错误叫做早期错误

语法错误是早期错误中的一种,不仅局限于语法错误,还包括语法正确但不符合语法规则的错误

比如正则表达式中的语法错误但是JavaScript语法正确也会产生早期错误

 

语法规定赋值对象必须是一个标识符(或者ES6中的结构表达式)

因此以下情况会报错

		var a;
		42 = a;

 

ES5的严格模式还定义了很多早期错误

比如函数参数不能同名

		function foo(a, b, a) {} //没问题

		function foo(a, b, a) { "use strict" } //错误!

 

从语义角度考虑,这些错误在词法上是正确的,但是语法上是错误的,由于没有GrammarError类型,一些浏览器就用SyntaxError来代替

 

暂时性死区(TDZ)

let块作用域

ES6规范定义了一个新概念,叫TDZ

TDZ指的是由于代码中的变量还没有初始化而不能被引用的情况

例如

		{
			a = 2;  //ReferenceError
			let a;
		}

a = 2试图在let a初始化 a 之前使用该变量(作用域在{}中),这里({..}中到let a前的区域)就是a的TDZ

 

注意,typeof的安全机制(具体介绍JavaScript 学习笔记 之 类型) 在TDZ中无用,但对未声明的变量依然有用

		{
			typeof a; //ReferenceError
			typeof b; //undefined
			let a;
		}

 

函数参数

另一个TDZ违规的例子是ES6中的参数默认值

		(function(a = 1, b = b + a) {})(); //Uncaught ReferenceError: b is not defined

这个例子中,b=b+a(=右边的b)刚好在b的TDZ中访问了b,因此触发了错误

而访问a则没问题,此时刚好跨出了a的TDZ

 

在ES6中,如果参数被省略或者值为undefined,则取该参数的默认值,但是参数值为undefined,依然会在arguments数组中

		function fun(a = "a", b = "b") {
			console.log(a, b, arguments);
		}

		fun(1, 2); //1 2 [1,2]
		fun(undefined); //a b [undefined]

向函数传递参数时,arguments数组中对于单元会和对应命名参数建立关联已得到相同的值

		function fun(a) {
			a = "linked";
			console.log(arguments[0]);
		}

		fun("a"); //linked
		fun(undefined); //linked
		fun(); //undefined

但是在严格模式下不存在关联

		function fun(a) {
			"use strict";
			a = "linked";
			console.log(arguments[0]);
		}

		fun("1"); //1
		fun(undefined); //undefined
		fun(); //undefined

 

try..finally

finally中的代码总是在try之后执行,如果有catch的话则在catch之后执行

可以吧finally中的代码看做是一个回调函数,即无论什么情况最后一定会调用

即使try中已经return了,finally中的代码还是会执行,而且如果finally中有return,则会覆盖之前return的值

		function fun() {
			try {
				return "try";
			} finally {
				console.log("finally");
			}
			console.log("never run");
		}
		console.log(fun()); //finally try

这里先执行 return 并将返回值设置为 try 然后执行完finally ,函数才算执行完毕返回"try"值(

try中出现了throw也是一样,先执行完finally后再抛出错误

但是如果finally中抛出异常的话函数就会在此处终止,如果之前try中有返回值,这个返回值会被丢弃

continue和break也是一样,执行完continue在i++之前会先执行finally中的代码块

 

ES6中的yield有些特别

与return不同的是,yield在generator重新开始时结束

		function* fun() {
			try {
				yield "try";
			} finally {
				throw "over";
			}
		}
		var gen = fun();
		console.log(gen.next()); //{value: "try", done: false}
		console.log(gen.next()); //Uncaught over

 

事实上还可以将finally和带标签的break混合使用

		function fun() {
			bar: {
				try {
					return "try";
				} finally {
					break bar;
				}
			}
			return "break";
		}

		console.log(fun()); //break

 

switch

switch可以看做是if..else if..else的简化版本

		switch (a){
			case value:
				break;
			default:
				break;
		}

运行时会用a和value进行一个===比较

因此必要时我们可以进行一些特殊处理来进行==比较(即通过强制类型转换进行相等比较)

		switch(true) {
			case !!(a == value):
				break;
			default:
				break;
		}

 

你可能感兴趣的:(JavaScript,学习笔记)