错误处理机制
1、Error 实例对象
js所有抛出的错误都是Error构造函数的实例,参数表示错误提示,可以从实例的message
属性读到
var err = new Error('出错了');
err.message // "出错了"
- 抛出
Error
实例,执行中断
整个程序就中断在发生错误的地方,不再执行后面代码
- 属性
- message:错误提示信息
- name:错误名称(非标准属性)
- stack:查看错误发生时的堆栈(非标准属性)
function throwit() {
throw new Error('');//抛出
}
function catchit() {
try {
throwit();
} catch(e) {
console.log(e.stack); // print stack trace
}
}
catchit()
// Error
// at throwit (~/examples/throwcatch.js:9:11)
// at catchit (~/examples/throwcatch.js:3:9)
// at repl:1:5
错误堆栈的最内层是throwit
函数,然后是catchit
函数,最后是函数的运行环境
2、自定义错误
function UserError(message) {
this.message = message || '默认信息';
this.name = 'UserError';
}
//自定义错误对象
throw new UserError('出错了!');
3、throw语句
用来手动中断程序执行,抛出一个错误
实际上,throw
可以抛出任何类型的值
-
错误对象
function UserError(message) { this.message = message || '默认信息'; this.name = 'UserError'; } //自定义错误对象 throw new UserError('出错了!');
-
其他类型
// 抛出一个字符串 throw 'Error!'; // Uncaught Error! // 抛出一个数值 throw 42; // Uncaught 42 // 抛出一个布尔值 throw true; // Uncaught true // 抛出一个对象 throw { toString: function () { return 'Error!'; } }; // Uncaught {toString: ƒ}
4、try...catch语句
允许对错误进行处理,选择是否往下执行
try {
throw "出错了";
} catch (e) {
console.log(111);
}
console.log(222);
// 111
// 222
- 不中断
catch
代码块捕获try中抛出的错误之后,程序不会中断(除非return),会继续执行下去
- 可嵌套
可以嵌套使用try...catch
结构
5、finally代码块
在try...catch
最后添加finally
,不管是否出现错误,最后必需运行的语句
-
错误没有捕获,中断
没有
catch
捕获错误,执行finally
后,程序就中断在错误抛出的地方
try {
throw new Error('出错了……');
console.log('此行不会执行');
} finally {
console.log('完成清理工作');
}
// 完成清理工作
// Error: 出错了……
-
try / catch 有return
try中return,延迟到finally执行完再返回
var count = 0; function countUp() { try { return count; } finally { count++; } } countUp() // 0 count // 1
try中throw,catch也有return,在catch中return时,跳到finally执行(如果finally没return,会等finally执行完,再执行catch中return;如果return,则不会执行catch中的return语句
try{}catch{}finally{}中存在return,则该结构后面的语句不执行
function f() { try { console.log(0); throw 'bug'; } catch(e) { console.log(1); return true; // 这句原本会延迟到 finally 代码块结束再执行 console.log(2); // 不会运行 } finally { console.log(3); return false; // 这句会覆盖掉前面那句 return console.log(4); // 不会运行 } console.log(5); // 不会运行 } var result = f(); // 0 // 1 // 3 result // false
-
try / catch 有throw
try中throw,之后语句不执行,跳到catch执行
try中throw,catch也有throw,在catch中throw时,跳到finally执行(如果finally没return,会等finally执行完,再执行catch中throw;如果return,则不会执行catch中的throw语句
function f() { try { throw '出错了!'; } catch(e) { console.log('捕捉到内部错误'); throw e; // 这句原本会等到finally结束再执行 } finally { return false; // 直接返回 } } try { f(); } catch(e) { // 此处不会执行 console.log('caught outer "bogus"'); } // 捕捉到内部错误
类型转换
强制转换
1、Number()
-
简单数据类型
// 数字 -> 数字 Number(324) // 324 /* 字符串 -> 1.全是数字:对应数字 2.空串:0 3.含字母其他:NaN -----> Number将字符串转为数值,比parseInt函数严格,只要有一个字符无法转成数值,整个字符串就会被转为NaN */ parseInt('42 cats') // 42 Number('42 cats') // NaN //布尔 -> true为1,false为0 //null -> 0 //undefined -> NaN
-
对象类型
除了包含单个数值的数组,其他都是NaN
Number({a: 1}) // NaN Number([1, 2, 3]) // NaN Number([5]) // 5
Number
背后的转换规则比较复杂:1、调用对象的
valueOf
方法返回的简单数据类型,直接对返回结果使用Number(),结束
2、调用对象的
toString
方法1中返回的是对象时,调用toString
返回的简单数据类型,直接对返回结果使用Number(),结束
3、报错
2中
toString
方法返回的是对象,就报错
2、String()
-
简单数据类型
- 数值:转为相应的字符串
- 字符串:原来的值
- 布尔值:
"true"
,"false"
- undefined:
"undefined"
- null:
"null"
-
对象类型
- 对象,返回一个类型字符串
- 数组,返回该数组的字符串形式
String({a: 1}) // "[object Object]" String([1, 2, 3]) // "1,2,3"
String
()转换规则与
Number
方法基本相同,只是互换了valueOf
方法和toString
方法的执行顺序String({a: 1}) // "[object Object]" // 等同于 String({a: 1}.toString()) // "[object Object]"
3、Boolean()
-
false五种情况
除了以下五个转换为false,其余为true
undefined
null
-
-0 或 +0
Boolean(0) // false
NaN
' '(空字符串)
-
对象都是true,
false
对应的布尔对象也是Boolean({}) // true Boolean([]) // true Boolean(new Boolean(false)) // true
自动转换
1、布尔值
预期为布尔值的地方(比如if
语句的条件部分),会自动转换为布尔值
将一个表达式转为布尔值
// 写法一
expression ? true : false
// 写法二
!! expression
2、字符串
字符串的加法运算,一个值为字符串,一个非字符串
具体规则是,先将复合类型的值转为原始类型的值,再将原始类型的值转为字符串
'5' + {} // "5[object Object]"
'5' + [] // "5"
'5' + function (){} // "5function (){}"
'5' + undefined // "5undefined"
'5' + null // "5null"
3、数值
- 其他运算符(除了+)
'5' * [] // 0
false / '5' // 0
'abc' - 1 // NaN
null + 1 // 1
undefined + 1 // NaN
-
一元运算符
一元运算符也会把运算子转成数值
+'abc' // NaN
-'abc' // NaN
+true // 1
-false // 0
set数据结构
//
// 1.接收一个数组参数, 将数组去重再保存
// 2.set.clear()清空set 无返回值 返回undefined
// 3.set.add(val)添加某个值,返回set对象本身{1,2,3,4,5}
// 4.set.delete(val)删除某个值,返回是否删除成功
// 5.set.has(val)查找某个值,返回是否包含
// 6.set.size属性 值的个数
// 数组的去重
let arr = [1,2,3,4,5,1,2];
let set = new Set(arr);
arr = [...set];
map数据结构
// map数据结构 :类似于对象
// 1.接收一个二维数组,将数组转化成key-value的形式
// 2.map.clear()清空 map变为{}
// 3.map.get(key)获取key对应的value值
// 4.map.has(key) 返回是否包含
// 5.map.set(key,val)修改或增加key
// 6.map.delete(key) 返回是否删除成功
// 7.map.size属性 值的个数
let arr=[
['a',1],
['a2',2],
['a3',3]
]
let map = new Map(arr);
console.log(map);
// {'a'=>1,'a'=>2,'a'=>3}
数组键名
1、数组的键名可以是整数或字符串?
arr = [1,2,3];
arr[0] //1
arr['0'] //1
arr.0//报错
之所以可以用数值读取,是因为非字符串的键名会被转为字符串
(方括号是运算符,可以接受数值,点结构不能)
2、数组的本质
是个对象
arr = [];
typeof arr;//object
3、length
属性是可写的
可用来清空数组,将length
属性设为0
var arr = [ 'a', 'b', 'c' ];
arr.length = 0;
arr // []
人为设置length
大于当前元素个数,会自动新增,新增位置为空
var a = ['a'];
a.length = 3;
a[1] // undefined
小于,则会自动删除多余元素
注意:使用delete
命令删除数组元素,会形成空位,length
不变
类似数组的对象
一个对象的所有键名都是正整数或零,并有length
属性,这个对象就很像数组,但不是数组。
1、和数组的区别
- 他没有数组特有的方法,且length为静态,不随元素变化而变化
var obj = {
0: 'a',
1: 'b',
2: 'c',
length: 3
};
obj[0] // 'a'
obj[1] // 'b'
obj.length // 3
obj.push('d') // TypeError: obj.push is not a function
2、有哪些例子
以下例子可以:使用[ ]获取元素,用length获取长度,但instanceof Array为false
- 函数的不定参数arguments
- DOM元素集
- 字符串
3、转换为真正数组
有以下几种方法:
-
使用数组方法slice,转换为真正数组
var arr = Array.prototype.slice.call(arguments); arr.forEach((element, index) => { })
-
使用call调用数组方法
Array.prototype.forEach.call(arguments, (element, index) => { //等同于调用for循环 })
但是,真正转为数组,再使用数组方法要比后面的方法快
对象
obj = {
age: 18,
name: "ccc"
}
var a = "sex";
obj[a] = "female";//name: "female"
obj.a = "female";//a: "female"
(方括号是运算符,可以接受变量,点结构不能)
函数新增方法
// 函数新增方法
// 箭头函数
// 1.箭头函数使用()=>返回值,可以直接接收返回值,不用return
// 2.箭头函数没有不定参数arguments,可以使用(...arg)剩余参数接收多个参数,组成一个数组来获取参数
// 3.箭头函数没有this 调用箭头函数的this,指向声明时的作用域的this
数组新增方法
// 数组新增方法
// 1.flat(depth) 参数为递归层数,不改变原数组
let arr = [1,[2,3,[4],5],6];
let arr2 = arr.flat(Infinity);
console.log(arr2);
// 2.flatMap(callback) 只能递归一层,返回新组成的数组
let arr = [['a1',['11','11']],['a2','15']];
let arr2 = arr.flatMap((item, index) => {
console.log(item,index);
// 这里return item相当于flat递归一层,将第一层元素为数组的转为元素
item = item.filter((item,index) => {
return index == 0;
})
return item;
})
console.log(arr2);
// 3.find(callback)返回第一个满足要求的值 都没有就是undefined
let arr = [1,2,3,4,5,1,2];
let val = arr.find((item,index)=>{
if(item>3){
return true;
}
});
// 或者
val = arr.find(item => item > 2);
console.log(val);
// 4.findIndex(callback)
let i = arr.findIndex(item => item > 2);
对象新增的方法
// 对象新增的方法
// 1.简洁表示法
// 1.1属性简洁表示
// 1.2方法简洁表示
// 2.属性名表达式[]
let age = 10;
let name = 'ccc';
let a = 'sex';
let person = {
name,
age,
[a]: 'man',
say(){
console('say:function() => say(){}');
}
}
console.log(person);
// 对象合并
let obj = {a:1,b:2};
let obj2 = {c:3,d:4};
obj2 = Object.assign(obj2, obj);
console.log(obj2);
let obj2 = {
...obj,
c: 3,
d: 4
};
console.log(obj3);
//is()对象比较
// 1.和===大部分相同,例如以下都是true
// 两个值都是undefined
// 两个值都是null
// 两个值都是true或者false
// 两个值都是相同字符串
// 两个值都是同一个对象(地址相同)
// 两个值都是数字
// 2.区别:
// +0 -0
// NaN NaN
console.log(Object.is(+0,-0))//false
console.log(+0 === -0)//true
console.log(NaN === NaN)//false
console.log(Object.is(NaN,NaN))//true
var与let
区别:块级作用域
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 10
-
var
命令声明的,在全局范围内都有效,因此全局只有一个i
,所有数组成员的函数内部的i
,指向的都是同一个i
,每一次循环,i
值改变,最后为10
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 6
-
let
声明的,只在本轮循环有效,每一次循环i
是一个新的变量,JavaScript 引擎内部会记住上一轮循环的值,在此基础上进行计算新的i
for循环作用域
循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
// abc
// abc
// abc
//说明各自单独的作用域
Babel的使用