- 错误处理
Function对象
重载(overload)
作用域和作用域链
闭包(closure)错误处理: 程序出错的情况下保证程序不中断退出的机制
如何错误处理:
try{
可能出错的代码
}catch(err){
处理错误的代码
}finally{
无论是否出错,都要执行的代码
}
var d=prompt("输入小数位数");
var n=123.456;
try{
console.log(n.toFixed(d));
}catch(err){
alert("小数位数无效");
//alert(err.message);
}finally{//释放资源
d=null;
n=null;
}
其中: err: 发生错误时的错误对象
err.message: 封装了错误的信息
err.name:错误的名字
Error: 错误对象,在错误发生时自动创建的,封装错误信息的对象。
6种: SyntaxError: 语法错误
ReferenceError: 引用错误,找不到对象
RangeError: 范围错误,参数超范围
TypeError: 类型使用错误, 错误的使用了对象的属性或方法。
URIError: URI错误
EvalError: 错误的使用了Eval
强调:finally中一般编写释放资源的代码
如果不需要释放资源,finally部分可省略
关于性能:
问题: 放在try中的代码,即使不出错,执行效率也会降低
解决: 大多数try catch都可用if else代替
为什么: 1. if else执行效率高;
var d=prompt("输入小数位数");
var n=123.456;
if(!isNaN(d)&&0<=d&&d<=20){
console.log(n.toFixed(d));
}else{
alert("小数位数无效");
}
//释放资源
d=null;
n=null;
不创建错误对象,节约内存空间,但,要求程序员的错误处理经验丰富
何时使用try catch:
1.如果可提前预料到错误的原因,就用if else
2.如果无法提前预料到错误的原因,就用try catch
try中仅包裹可能出错的代码
比如: 凡是用eval,都要错误处理关于try catch中的return:
/*鄙视题*/
1. finally中有return:如果出错,返回4
如果没出错,返回3
finally中的return替换之前的return
2. finally中没有return: 如果出错,返回3,n:4
如果没出错,返回2,n:3
finally中的代码也永远执行,
但在确定return的值之后才执行
finally执行后,才return
finally的代码不会影响return的结果*/
var n=1;
function fun(){
try{
//n++;
m++;//报错
return n;
}catch(err){
n++;
return n;
}finally{
n++;
//return n;
}
}
alert(fun());//2
alert(n);//3
主动抛出错误:
何时抛出: 想提示别人错误的使用了自己定义的方法时
如何抛出自定义:
函数定义者抛出异常: throw new Error("错误信息")
函数调用者,需要用try catch接住抛出的异常,并处理
//程序员甲,函数定义者
function round(num,d){//函数定义者抛出自定义错误
if(isNaN(num)||isNaN(d)){
throw new Error("num 和 d 必须是数字");
//抛出自定义错误,等效于错误
//后续代码不再执行!
}
return Math.round(num*Math.pow(10,d))/Math.pow(10,d);
}
//程序员乙,函数调用者
var num=prompt("输入数字");
var d=prompt("输入小数位数");
try{//函数调用者接住错误,并显示错误信息
alert(round(num,d));
}catch(err){
//alert(err.message);
alert("必须输入数字!");
}
<script>
function myFunction(){
try{
var x=document.getElementById("demo").value;
if(x=="") throw "empty";
if(isNaN(x)) throw "not a number";
if(x>10) throw "too high";
if(x<5) throw "too low";
}catch(err){
var y=document.getElementById("mess");
y.innerHTML="Error: " + err + ".";}
}
</script>
<h1>My First JavaScript</h1>
<p>Please input a number between 5 and 10:</p>
<input id="demo" type="text">
<button type="button" onclick="myFunction()">Test Input</button>
<p id="mess"></p>
Function:专门封装函数定义的对象
重载(overload):相同函数名,不同参数列表的多个函数,在调用时,可根据传入参数的不同,自动挑选对应的函数执行。
为什么: 节省调用使用API的负担
强调: 没有代码重用!
何时使用: 同一件事,不同参数,决定不同的业务流程js语法不支持重载: js中两个同名的函数,后定义的会覆盖先定义的。仅有一个生效。
解决:函数中的arguments对象
arguments对象:在函数作用域内,接收所有参数值的类数组对象——长得像数组的对象
内容: 所有传入的参数值
存储: 像数组一样存储:下标访问每个参数值: arguments[i]
也有length属性,记录参数个数
参数 vs arguments:
参数:
1. 提示调用者,如何传入数据
2. 在函数内使用时,见文知意
以参数为主
arguments:
1. 在模拟重载时
2. 在不确定参数个数时
特殊情况才用arguments在普通模式中: 参数中和arguments中使用同一个值
一方修改,另一方同时变化——不合理
解决: 严格模式: 在函数内,顶部加:”use strict”;
参数变量的值和arguments无关!
类数组对象 vs []:
相同: 1. 都可用[i]访问每个元素
2. 都有length属性记录元素个数
3. 都可用for循环遍历,尽量不要用for in遍历
不同点: 1. 类型不同:
[] 是 Array类型
类数组对象 是 Object类型
2. 类数组对象无法使用数组API
function checkout(){
//arguments:[0:450].length:1
//arguments:[0:"1010 1010 1010 1010",1:"123456"].length:2
//如果传入一个参数,就现金结账
if(arguments.length==1){
console.log("现金结账");
console.log("计算找零");
}else if(arguments.length==2){
//否则,如果传入2个参数,就刷卡结账
console.log("刷卡");
console.log("输密码");
console.log("签字");
}
}
checkout(450);
checkout("1010 1010 1010 1010","123456");
function calc(){
//如果传入两个参数,就返回两值的和
if(arguments.length==2){
return arguments[0]+arguments[1];
}else if(arguments.length==1){
//否则 如果传入一个参数,就返回传入值的平方
return arguments[0]*arguments[0];
}
}
console.log(calc(12,35));//47
console.log(calc(13));//169
- Function
创建: 3种:
- 声明:
function 函数名(参数列表){函数体;return 返回值}
比如: function compare(a,b){return a-b;}
只有声明方式创建的函数,才能被声明提前 - 直接量:
var 函数名=function(参数列表){函数体;return 返回值}
函数其实就是一个对象,函数名就是引用函数对象的变量
还说明js中的函数,也可作为参数传递给方法或其他变量
强调: 不能被声明提前 - 实例化对象:
var 函数名=new Function(“a”,”b”,”return a-b”);
- 声明:
*匿名函数:
定义时没有其他变量引用的函数
特点: 定义后立刻调用,调用后立刻释放
为什么: 调用后立刻释放,节约内存
何时使用:
1. 自调: 定义完。立刻调用!
如何自调:
var 返回值=(function(参数列表){
函数体;
return 返回值
})(参数值列表)
何时使用: 如果完成一项特定任务的代码段,仅执行一次,就要用匿名函数封装!
强调: (参数值列表)可写在括号内
var r=(function(a,b){return a+b;}(12,35));
console.log(r);
2. 回调: 将函数交给其他方法去调用
比如: arr.sort(function(a,b){return a-b});
何时使用:如果其它函数需要一个函数作为参数时,而执行完希望立刻释放参数时,就用匿名函数作为参数。
作用域和作用域链:
作用域: 一个变量的可用范围
变量的存储位置
js:2种:
全局作用域window: 全局变量, 可反复使用,随处可访问
问题: 容易被污染
比如: 给从未声明过的变量赋值时,都会在全局创建或修改同名变量。
为什么: 凡是无主的变量或函数都是window的
比如: m=12; //假设m未声明
相当于window.m=12
函数作用域: 局部变量
存储在活动对象中,随函数调用而创建,调用后自动释放。
问题: 不可重用!
作用域链: 从当前函数的作用域AO开始,逐级继承,直到window,形成的链 式关系。
在函数中查找变量,按照从AO到window的顺序查找
如果AO有,就不用window的
如果AO没有,才去window找
vs Java作用域:
块级作用域:
比如一个分支或循环结构,也是一级作用域
Java中:
for(var i=1,sum=0;i<=100;sum++,i++);
输出sum,报错!
if(){var n}
try{var m}catch(err){...}
一旦结构执行完,n和m都不能用
js中:没有块级作用域
以上三种情况的变量都是在window中创建,
可重复使用。
闭包:
保护一个可重用的局部变量的词法结构
为什么: 全局变量: 可重用,但易被污染
局部变量: 不可重用,
何时使用: 即可重用,又不会被污染
如何创建闭包:
1. 外层函数封装受保护的变量以及专门操作变量的内层函数
2. 外层函数将操作变量的内层函数返回到外部
3. 调用外层函数,获得内层函数的对象。
鄙视时:
1. 找受保护的变量
2. 同一次外层函数调用返回的内层函数,始终使用同一个受保护的局部变量
3. 每调用一次外层函数,就会多创建一个受保护的变量的副本。
函数的中的变量预处理以及执行处理:
(1)关于函数内的变量情况一
document.write(b);//全局预处理结果为undefined 声明提前,值在原地
var b=3
document.write(b);//全局预处理值为3
function f(){//定义一个函数来改变b的值
var b=10;//因为已在前声明b的值为3,所以该处的赋值不对b产生影响所以b任然等于3
}
document.write(b);//在调用函数前值为3
f();
document.write(b);//因为已在前声明b的值为3,所以该处的赋值不对b产生影响所以b任然为旧值等于3
(2)关于函数内的变量情况二
document.write(b);//全局预处理结果为undefined 因为声明提前,值在原地
var b=3
document.write(b);//全局预处理值为3
function f(){//定义一个函数来改变b的值
b=10;//此处为给b重新赋值,所以在调用该函数后,b的值为新值,也就是10
}
document.write(b);//在调用函数前值为3
f();
document.write(b);//b=10 因为在调用该函数后,b的值为新值,也就是10
(3)关于调用函数的全局预处理及执行处理
1.声明
function 函数名(参数列表){函数体;return 返回值}
比如: function compare(a,b){return a-b;}
只有声明方式创建的函数,才能被声明提前
document.write(f);//值为4 以function 函数名(){}方式的函数在全局预处理中可以直接调用(也就是在函数前也可调用),因为 document.write(f)的结果为函数f的字符串内容function f(a) {return a+1;}//
function f(a){return a+1;}
f(9);
document.write(f(9));//值为10 正常调用函数f后的新值。
2.直接量:
var 函数名=function(参数列表){函数体;return 返回值}
函数其实就是一个对象,函数名就是引用函数对象的变量
还说明js中的函数,也可作为参数传递给方法或其他变量
强调: 不能被声明提前
document.write("<br/>")
//document.write(g(3));//会报错,因为此处是在函数g之前调用,
// 所以以var 函数名=function(参数){}方式的函数在全局
// 预处理中值为undefined,所以调用结果会报错。
var g=function(b){
return b+2
}
document.write(g(3));//5 正常调用函数
var a=1,b=2;
function add(a,b){
var a=5;
var b=10;
c=a+b;
return c;
}
alert(add(a,b));//15
alert(c)//15 因为c未在函数内(局部)声明则为全局的变量
alert(a)//1
alert(b)//2
var a=1,b=2;
function add(a,b){
c=a+b;
var a=5;
var b=10;
return c;
}
alert(add(a,b));//3
alert(c)//3 因为c未在函数内(局部)声明则为全局的变量
alert(a)//1
alert(b)//2