类型转换与错误处理

错误处理机制

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的使用








你可能感兴趣的:(类型转换与错误处理)