JavaScript 异常处理和预编译

JavaScript 异常处理

try+catch+finally+throw
try 语句测试代码块的错误。
catch 语句处理错误。
throw 语句创建自定义错误。
finally 语句在 try 和 catch 语句之后,无论是否有触发异常,该语句都会执行。

语法

try/catch/finally语句
try/catch/finally 是 JavaScript 异常处理语句。语法格式如下:

try{
    //调试代码块
}
catch(e) {
    //捕获异常,并进行异常处理的代码块
}
finally{
    //后期清理代码块
}

ps: 在正常情况下,JavaScript 按顺序执行 try 子句中的代码,如果没有异常发生,将会忽略 catch 子句,跳转到 finally 子句中继续执行。
如果在 try 子句运行时发生错误,或者使用 throw 语句主动抛出异常,则执行 catch 子句中的代码,同时传入一个参数,引用 Error 对象。
例:
在下面的例子中,在 try 块的代码中写了一个错字。
catch 块会捕捉到 try 块中的错误,并执行代码来处理它。

var txt=""; 
function message() 
{ 
    try { 
        adddlert("Welcome guest!"); 
    } catch(err) { 
        txt="本页有一个错误。\n\n"; 
        txt+="错误描述:" + err.message + "\n\n"; 
        txt+="点击确定继续。\n\n"; 
        alert(txt); 
    } 
}

finally 语句
finally 语句不论之前的 try 和 catch 中是否产生异常都会执行该代码块。
不管 try 语句是否完全执行,finally 语句最后都必须要执行,即使使用了跳转语句跳出了异常处理结构,也必须在跳出之前先执行 finally 子句。
例:

/*在函数体内设计一个异常处理结构,为每个子句添加一个 return 语句。
调用函数后,实际返回的是“finally”,而不是“try”,
因为 finally 子句必须最后执行,把 finally 子句去掉,
函数才会返回“try”。*/
function test() {
    try {
        return "try";
    }catch {
        return "catch";
    }finally {
        return "finally";
    }
}
console.log(test());  //返回“finally”

ps:
try/catch/finally 语句允许嵌套使用,嵌套的层数不限,同时形成一条词法作用域链。在 try 中发生异常时,JavaScript 会停止程序的正常运行,并跳转到层级最近的 catch 子句(异常处理器)。
如果没有找到异常处理器,则会沿着作用域链,检查上一级的 catch 子句,以此类推,直到找到一个异常处理器。如果在程序中没有找到任何异常处理器,将会显示错误。
例:

/*下面代码就是一个多层嵌套的异常结构,在处理一系列的异常时,内层的 
catch 子句通过将异常抛出,
就可以将异常抛给外层的 catch 子句来处理。*/

try {  //外层异常处理结构
    try {  //内层异常处理结构
        test();  //错误调用
    }
    catch(error) {
        if (error.name == "ReferenceError") console.log("错误参考");   //如果是异常引用,则提示这样的信息
        else throw error;  //否则再次抛出一个异常,并把错误信息向上传递
        }
}
catch (error) {  //获取内层异常处理结构中抛出的异常
    console.log("内层 try/catch 不能够处理这个错误");
}

Throw 语句
throw 语句允许我们创建自定义错误。
创建或抛出异常(exception)。
如果把 throw 与 try 和 catch 一起使用,那么您能够控制程序流,并生成自定义的错误消息。
语法
throw exception
异常可以是 JavaScript 字符串、数字、逻辑值或对象。
例:
本例检测输入变量的值。如果值是错误的,会抛出一个异常(错误)。catch 会捕捉到这个错误,并显示一段自定义的错误消息:

function myFunction() {
    var message, x;
    message = document.getElementById("message");
    message.innerHTML = "";
    x = document.getElementById("demo").value;
    try { 
        if(x == "")  throw "值为空";
        if(isNaN(x)) throw "不是数字";
        x = Number(x);
        if(x < 5)    throw "太小";
        if(x > 10)   throw "太大";
    }
    catch(err) {
        message.innerHTML = "错误: " + err;
    }
}

注意 如果 getElementById 函数出错,上面的例子也会抛出一个错误。

错误类型

ECMA-262 规范了 7 种错误类型,具体说明如下。其中 Error 是基类,其他 6 种错误类型是子类,都继承 Error 基类。Error 类型的主要用途是自定义错误对象。
Error:普通异常。与 throw 语句和 try/catch 语句一起使用,属性 name 可以读写异常类型,message 属性可以读写详细错误信息。
SyntaxError:出现语法错误时抛出。
RangeError:数字超出合法范围时抛出、
ReferenceError:读取不存在的变量时抛出。
TypeError:值得类型发生错误时抛出。
EvalError:不正确的使用 eval() 方法时抛出。
URIError:URI 编码和解码错误时抛出。

JavaScript 预编译

JavaScript运行的步骤

1、语法分析
2、预编译
3、解释执行

在执行代码前,还有两个步骤
语法分析 就是引擎检查你的代码有没有什么低级的语法错误
预编译 简单理解就是在内存中开辟一些空间,存放一些变量与函数
解释执行 执行代码了

JavaScript预编译(变量提升和函数提升)

JS并不会完全按照代码顺序进行解析执行,而是在解析之前进行一次“预编译”。在此过程中,会把:
(1)定义式的函数优先执行
(2)所有var变量定义,默认值为undefined、
函数声明和变量赋值
function fun() {} / 函数声明 这种形式的写法是函数声明,也即是声明一个函数,这种写法,脚本在执行之前会做预编译处理
var fun = function() {} // 变量赋值
这种写法就属于是变量的赋值了,函数在js中也是一种数据,匿名函数作为变量赋值给定义的变量。这种形式的写法,在编译阶段也会做处理,但是只会给变量fun分配一个内存空间,初始值为undefined
具体值的初始化是在程序执行阶段。
ps:
函数声明整体提前,变量 声明提前
函数中的预编译 发生在,函数执行前的那一刻

作用域 全局window(Global Object) 局部作用 (AO Activation Object)

例:

function fun1(){
			//c a 暗示成了全局变量
			a = 123;
			var b = c = 10;
		}

局部的预编译

例:

function demo(a){
			console.log(d);
			console.log(a);//1
			var c = 123;
			console.log(c);//123
			function d(){}
			var f = function(){}
		}

demo(1);

在代码执行前

1、创建AO(activation object/执行期上下文)对象
	AO = {}
2、找形参和变量声明,将变量和形参作为AO的属性名,值为undefined
	AO = {
		a : undefined,
		c : undefined,
		f : undefined
	}
3、将实参值和形参统一
	AO = {
		a : 1,
		c : undefined,
		f : undefined
	}
4、在函数中找函数声明,值赋予函数体
	AO = {
		a : 1,
		c : undefined,
		f : undefined,
		d : function d(){}
	}

执行时

AO = {
		a : 1,
		c : 123,
		f : function(){},
		d : function d(){}
	}
1 test AO {
}
2 test AO{
	a : undefined,
	b : undefined,
}
3 test AO{
	a : 10,
	b : undefined,
}
4 test AO{
	a : function a(){},
	b : undefined,
	d : function d(){}
}

全局的预编译

生成了一个GO对象 其他的三步和函数中是一致
全局的预编译,发生在全局执行前 , 打开浏览器window对象就生成了

console.log(a);//function... 
	a();
	var  a  = 11;
	function a (){
		c = 10;//如果执行了,归属于GO
	}
	console.log(window.a);
	console.log(a);
console.log(test);/* 输出
			ƒ test(test){
			console.log(test);
			var test = 234;
			console.log(test);
			function test(){}
			console.log(num++);//10
		}*/
		function test(test){
			console.log(test);//ƒ test(){}
			var test = 234;
			console.log(test);//234
			function test(){}
			console.log(num++);//10
		}
		
		var num = 10;
		test(1);
		var test = 123;
		console.log(test);//123
		console.log(num);//11
GO{
	test : undefined --> function --> 123,
	num : undefined -- > 10 --> 11 //在test方法里console.log(num++);//10修改了num的值
}


testAO{
	test : undefined --> 1 --> 234
}

注意
预编译阶段发生变量声明和函数声明,没有初始化行为(赋值),匿名函数不参与预编译
只有在解释执行阶段才会进行变量初始化

总结

预编译(函数执行前)

  1. 创建AO对象(Active Object)
  2. 查找函数形参及函数内变量声明,形参名及变量名作为AO对象的属性,值为undefined
  3. 实参形参相统一,实参值赋给形参
  4. 查找函数声明,函数名作为AO对象的属性,值为函数引用

预编译(脚本代码块script执行前)

  1. 查找全局变量声明(包括隐式全局变量声明,省略var声明),变量名作全局对象的属性,值为undefined
  2. 查找函数声明,函数名作为全局对象的属性,值为函数引用
发布了24 篇原创文章 · 获赞 0 · 访问量 753

猜你喜欢

转载自blog.csdn.net/weixin_45846263/article/details/103642645