JS学习系列5-数据类型转换、错误处理机制、console对象

515 阅读9分钟

前言

本系列是之前的学习积累,主要针对JS教程入门时进行学习的记录整理。使用到的参考资料有菜鸟教程、阮一峰JS入门教程系列、网道等多个网站。内容主要基于 ECMAScript 5.1 版本,这是学习 JavaScript 语法的基础。

数据类型转换

JavaScript 是一种动态类型语言,变量没有类型限制,可以随时赋予任意值。

各种运算符对数据类型是有要求的。如果运算符发现,运算子的类型与预期不符,就会自动转换类型。

1. 强制转换

1.1 Number()

使用Number函数,可以将任意类型的值转化成数值。

  • 原始类型值
// 数值:转换后还是原来的值
Number(324) // 324

// 字符串:如果可以被解析为数值,则转换为相应的数值
Number('324') // 324

// 字符串:如果不可以被解析为数值,返回 NaN
Number('324abc') // NaN

// 空字符串转为0
Number('') // 0

// 布尔值:true 转成 1,false 转成 0
Number(true) // 1
Number(false) // 0

// undefined:转成 NaN
Number(undefined) // NaN

// null:转成0
Number(null) // 0

Number函数将字符串转为数值,要比parseInt函数严格很多。基本上,只要有一个字符无法转成数值,整个字符串就会被转为NaN。

parseInt和Number函数都会自动过滤一个字符串前导和后缀的空格。

parseInt('\t\v\r12.34\n') // 12
Number('\t\v\r12.34\n') // 12.34
  • 对象 Number方法的参数是对象时,将返回NaN,除非是包含单个数值的数组。

Number背后的转换规则如下:

  1. 调用对象自身的valueOf方法。如果返回原始类型的值,则直接对该值使用Number函数,不再进行后续步骤。

  2. 如果valueOf方法返回的还是对象,则改为调用对象自身的toString方法。如果toString方法返回原始类型的值,则对该值使用Number函数,不再进行后续步骤。

  3. 如果toString方法返回的是对象,就报错

valueOf和toString方法,都是可以自定义的。

Number({
  valueOf: function () {
    return 2;
  }
})
// 2

Number({
  toString: function () {
    return 3;
  }
})
// 3

Number({
  valueOf: function () {
    return 2;
  },
  toString: function () {
    return 3;
  }
})
// 2

1.2 String()

String函数可以将任意类型的值转化成字符串

  • 原始类型值
String(123) // "123"
String('abc') // "abc"
String(true) // "true"
String(undefined) // "undefined"
String(null) // "null
  • 对象 String方法的参数如果是对象,返回一个类型字符串;如果是数组,返回该数组的字符串形式。

String方法背后的转换规则:

  1. 先调用对象自身的toString方法。如果返回原始类型的值,则对该值使用String函数,不再进行以下步骤。
  2. 如果toString方法返回的是对象,再调用原对象的valueOf方法。如果valueOf方法返回原始类型的值,则对该值使用String函数,不再进行以下步骤。
  3. 如果valueOf方法返回的是对象,就报错。
String({
  toString: function () {
    return 3;
  }
})
// "3"

String({
  valueOf: function () {
    return 2;
  }
})
// "[object Object]"

String({
  valueOf: function () {
    return 2;
  },
  toString: function () {
    return 3;
  }
})
// "3"

1.3 Boolean()

Boolean()函数可以将任意类型的值转为布尔值。

除了以下五个值的转换结果为false,其他的值全部为true。

undefined
null
0(包含-0和+0)
NaN
''(空字符串)

Boolean({}) // true
Boolean([]) // true
Boolean(new Boolean(false)) // true

为了保证性能,统一规定,对象的布尔值为true。

2. 自动转换

遇到以下三种情况时,JavaScript 会自动转换数据类型,即转换是自动完成的,用户不可见。

  • 不同类型的数据互相运算。
  • 对非布尔值类型的数据求布尔值
  • 对非数值类型的值使用一元运算符(即+和-)。

自动转换的规则:

预期什么类型的值,就调用该类型的转换函数。比如,某个位置预期为字符串,就调用String函数进行转换。如果该位置即可以是字符串,也可能是数值,那么默认转为数值。

由于自动转换具有不确定性,且不易被发现,建议在预期为布尔值、数值、字符串的地方,全部使用Boolean、Number和String函数进行显式转换。

2.1 自动转换为布尔值

系统内部会自动调用Boolean函数。

因此除了以下五个值,其他都是自动转为true。

undefined
null
+0或-0
NaN
''(空字符串)

2.2 自动转换为字符串

具体规则是,先将复合类型的值转为原始类型的值,再将原始类型的值转为字符串。

字符串的自动转换,主要发生在字符串的加法运算时。当一个值为字符串,另一个值为非字符串,则后者转为字符串。

'5' + 1 // '51'
'5' + true // "5true"
'5' + false // "5false"
'5' + {} // "5[object Object]"
'5' + [] // "5"
'5' + function (){} // "5function (){}"
'5' + undefined // "5undefined"
'5' + null // "5null"

2.3 自动转换为数值

系统内部会自动调用Number函数。

除了加法运算符(+)有可能把运算子转为字符串,其他运算符都会把运算子自动转成数值。

一元运算符也会把运算子转成数值。

错误处理机制

1. Error实例对象

JavaScript 解析或运行时,一旦发生错误,引擎就会抛出一个错误对象。JavaScript 原生提供Error构造函数,所有抛出的错误都是这个构造函数的实例。

var err = new Error('出错了');
err.message // "出错了"
  • 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

2. 原生错误类型

Error实例对象是最一般的错误类型,在它的基础上,JavaScript 还定义了其他6种错误对象。也就是说,存在Error的6个派生对象。

2.1 SyntaxError 对象

解析代码时发生的语法错误。

// 变量名错误
var 1a;
// Uncaught SyntaxError: Invalid or unexpected token

// 缺少括号
console.log 'hello');
// Uncaught SyntaxError: Unexpected string

2.2 ReferenceError 对象

引用一个不存在的变量时发生的错误。

另一种触发场景是,将一个值分配给无法分配的对象,比如对函数的运行结果赋值。

// 使用一个不存在的变量
unknownVariable
// Uncaught ReferenceError: unknownVariable is not defined

// 等号左侧不是变量
console.log() = 1
// Uncaught ReferenceError: Invalid left-hand side in assignment

2.3 RangeError 对象

一个值超出有效范围时发生的错误.

主要有几种情况,一是数组长度为负数,二是Number对象的方法参数超出范围,以及函数堆栈超过最大值。

2.4 TypeError 对象

变量或参数不是预期类型时发生的错误.

比如,对字符串、布尔值、数值等原始类型的值使用new命令,就会抛出这种错误,因为new命令的参数应该是一个构造函数。

调用对象不存在的方法,也会抛出TypeError错误。

2.5 URIError 对象

URI 相关函数的参数不正确时抛出的错误,主要涉及encodeURI()、decodeURI()、encodeURIComponent()、decodeURIComponent()、escape()和unescape()这六个函数。

2.6 EvalError 对象

eval函数没有被正确执行时,会抛出EvalError错误。

开发者可以使用它们,手动生成错误对象的实例。这些构造函数都接受一个参数,代表错误提示信息(message)。

var err1 = new Error('出错了!');
var err2 = new RangeError('出错了,变量超出有效范围!');
var err3 = new TypeError('出错了,变量类型无效!');

err1.message // "出错了!"
err2.message // "出错了,变量超出有效范围!"
err3.message // "出错了,变量类型无效!"

3. 自定义错误

function UserError(message) {
  this.message = message || '默认信息';
  this.name = 'UserError';
}

UserError.prototype = new Error();//继承Error对象
UserError.prototype.constructor = UserError;

new UserError('这是自定义的错误!');

4. throw语句

throw语句的作用是手动中断程序执行,抛出一个错误。

throw也可以抛出自定义错误

function UserError(message) {
  this.message = message || '默认信息';
  this.name = 'UserError';
}

throw new UserError('出错了!');
// Uncaught UserError {message: "出错了!", name: "UserError"}

throw可以抛出任何类型的值。也就是说,它的参数可以是任何值。

5.try...catch结构

JavaScript 提供了try...catch结构,允许对错误进行处理,选择是否往下执行。

catch代码块捕获错误之后,程序不会中断,会按照正常流程继续执行下去。

try {
  throw "出错了";
} catch (e) {
  console.log(111);
}
console.log(222);
// 111
// 222

try...catch结构允许在最后添加一个finally代码块,表示不管是否出现错误,都必需在最后运行的语句。

return语句的执行是排在finally代码之前,只是等finally代码执行完毕后才返回

var count = 0;
function countUp() {
  try {
    return count;
  } finally {
    count++;
  }
}

countUp()
// 0
count
// 1

经典使用场景

openFile();

try {
  writeFile(Data);
} catch(e) {
  handleError(e);
} finally {
  closeFile();
}

catch代码块之中,触发转入finally代码块的标志,不仅有return语句,还有throw语句

function f() {
  try {
    throw '出错了!';
  } catch(e) {
    console.log('捕捉到内部错误');
    throw e; // 这句原本会等到finally结束再执行
  } finally {
    return false; // 直接返回
  }
}

try {
  f();
} catch(e) {
  // 此处不会执行
  console.log('caught outer "bogus"');
}

//  捕捉到内部错误

console对象

console对象是 JavaScript 的原生对象,它有点像 Unix 系统的标准输出stdout和标准错误stderr,可以输出各种信息到控制台,并且还提供了很多有用的辅助方法。

console的常见用途有两个。

调试程序,显示网页代码运行时的错误信息。 提供了一个命令行接口,用来与网页代码互动。

1. console对象的静态方法

1.1 console.log()

console.log方法用于在控制台输出信息。它可以接受一个或多个参数,将它们连接起来输出。

console.log方法会自动在每次输出的结尾,添加换行符

console.log(1);
console.log(2);
console.log(3);
// 1
// 2
// 3

console.log(' %s + %s = %s', 1, 1, 2)
//  1 + 1 = 2
console.log({foo: 'bar'})
// Object {foo: "bar"}
console.log(Date)
// function Date() { [native code] }

支持以下占位符,不同类型的数据必须使用对应的占位符。

%s 字符串
%d 整数
%i 整数
%f 浮点数
%o 对象的链接
%c CSS 格式字符串

console对象的所有方法,都可以被覆盖。因此,可以按照自己的需要,定义console.log方法。

使用自定义的console.log方法,可以在显示结果添加当前时间:
['log', 'info', 'warn', 'error'].forEach(function(method) {
  console[method] = console[method].bind(
    console,
    new Date().toISOString()
  );
});

console.log("出错了!");
// 2014-05-18T09:00.000Z 出错了!

1.2 console.info()、console.debug()

console.info是console.log方法的别名,用法完全一样。只不过console.info方法会在输出信息的前面,加上一个蓝色图标。

console.debug方法与console.log方法类似,会在控制台输出调试信息。但是,默认情况下,console.debug输出的信息不会显示,只有在打开显示级别在verbose的情况下,才会显示。

1.3 console.warn(), console.error()

//console这块我日后再补充学习吧,一般对我现在阶段的项目经历来说log就足够了,还有一些常用的API知道就好。