数据类型的转换

强制转换

Number()

原始类型
// 数值:转换后还是原来的值Number(324) // 324// 字符串:如果能够被解析为数值,则转换为相应的数值Number('324') // 324// 字符串:如果不能够被解析为数值,返回 NaNNumber('324abc') // NaN// 空字符串转为0Number('') // 0// 布尔值:true 转成 1,false 转成 0Number(true) // 1Number(false) // 0// undefined:转成 NaNNumber(undefined) // NaN// null:转成0Number(null) // 0

(1)parseIntNumber函数都会主动过滤一个字符串前导和后缀的空格。
(2)Number函数将字符串转为数值,要比parseInt函数严格很多。基本上,只有有一个字符无奈转成数值,整个字符串就会被转为NaN

对象

(2)简略的规定是,Number办法的参数是对象时,将返回NaN,除非是蕴含单个数值的数组。

Number({a: 1}) // NaNNumber([1, 2, 3]) // NaNNumber([5]) // 5

(2)Number背地的转换规则比较复杂。

第一步,调用对象本身的valueOf办法。如果返回原始类型的值,则间接对该值应用Number函数,不再进行后续步骤。

第二步,如果valueOf办法返回的还是对象,则改为调用对象本身的toString办法。如果toString办法返回原始类型的值,则对该值应用Number函数,不再进行后续步骤。

第三步,如果toString办法返回的是对象,就报错。

var obj = {x: 1};Number(obj) // NaN// 等同于if (typeof obj.valueOf() === 'object') {  Number(obj.toString());} else {  Number(obj.valueOf());}

(3)默认状况下,对象的valueOf办法返回对象自身,所以个别总是会调用toString办法,而toString办法返回对象的类型字符串(比方[object Object])。所以,会有上面的后果。

Number({}) // NaN

(4)valueOftoString办法,都是能够自定义的。

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

String()

原始类型值
  • 数值:转为相应的字符串。
  • 字符串:转换后还是原来的值。
  • 布尔值true转为字符串"true"false转为字符串"false"
  • undefined:转为字符串"undefined"
  • null:转为字符串"null"
String(123) // "123"String('abc') // "abc"String(true) // "true"String(undefined) // "undefined"String(null) // "null"
对象

(1)String办法的参数如果是对象,返回一个类型字符串;如果是数组,返回该数组的字符串模式。

String({a: 1}) // "[object Object]"String([1, 2, 3]) // "1,2,3"

(2)转换规则

  1. 先调用对象本身的toString办法。如果返回原始类型的值,则对该值应用String函数,不再进行以下步骤。
  2. 如果toString办法返回的是对象,再调用原对象的valueOf办法。如果valueOf办法返回原始类型的值,则对该值应用String函数,不再进行以下步骤。
  3. 如果valueOf办法返回的是对象,就报错。
String({a: 1})// "[object Object]"// 等同于String({a: 1}.toString())// "[object Object]"
var obj = {  valueOf: function () {    return {};  },  toString: function () {    return {};  }};String(obj)// TypeError: Cannot convert object to primitive value
String({  toString: function () {    return 3;  }})// "3"String({  valueOf: function () {    return 2;  }})// "[object Object]"String({  valueOf: function () {    return 2;  },  toString: function () {    return 3;  }})// "3"

Boolean()

(1)它的转换规则绝对简略:除了以下五个值的转换后果为false,其余的值全副为true

  • undefined
  • null
  • 0(蕴含-0+0
  • NaN
  • ''(空字符串)
Boolean(undefined) // falseBoolean(null) // falseBoolean(0) // falseBoolean(NaN) // falseBoolean('') // false
Boolean(true) // trueBoolean(false) // false

(2)所有对象(包含空对象)的转换后果都是true

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

主动转换

(1)主动转换的规定是这样的:预期什么类型的值,就调用该类型的转换函数。
(2)因为主动转换具备不确定性,而且不易除错,倡议在预期为布尔值、数值、字符串的中央,全副应用Boolean()Number()String()函数进行显式转换。
(3)加法运算符(+)有可能把运算子转为字符串

null + 1 // 1undefined + 1 // NaN

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

+'abc' // NaN-'abc' // NaN+true // 1-false // 0

错误处理机制

Error 实例对象

(1)JavaScript 原生提供Error构造函数,所有抛出的谬误都是这个构造函数的实例。

var err = new Error('出错了');err.message // "出错了"

(2)Error的属性

  • message:谬误提示信息
  • name:谬误名称(非标准属性)
  • stack:谬误的堆栈(非标准属性)
if (error.name) {  console.log(error.name + ': ' + error.message);}
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

原生谬误类型

(1)SyntaxError`对象是解析代码时产生的语法错误。

// 变量名谬误var 1a;// Uncaught SyntaxError: Invalid or unexpected token// 短少括号console.log 'hello');// Uncaught SyntaxError: Unexpected string

(2)ReferenceError对象是援用一个不存在的变量时产生的谬误。

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

(3)将一个值调配给无奈调配的对象,比方对函数的运行后果赋值。

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

(4)RangeError对象是一个值超出无效范畴时产生的谬误。次要有几种状况,一是数组长度为正数,二是Number对象的办法参数超出范围,以及函数堆栈超过最大值。

// 数组长度不得为正数new Array(-1)// Uncaught RangeError: Invalid array length

(5)TypeError对象是变量或参数不是预期类型时产生的谬误。比方,对字符串、布尔值、数值等原始类型的值应用new命令,就会抛出这种谬误,因为new命令的参数应该是一个构造函数。

new 123// Uncaught TypeError: number is not a funcvar obj = {};obj.unknownMethod() // 调用对象不存在的办法// Uncaught TypeError: obj.unknownMethod is not a function

(6)URIError对象是 URI 相干函数的参数不正确时抛出的谬误,次要波及encodeURI()decodeURI()encodeURIComponent()decodeURIComponent()escape()unescape()这六个函数。

decodeURI('%2')// URIError: URI malformed

(7)eval函数没有被正确执行时,会抛出EvalError谬误。该谬误类型曾经不再应用了,只是为了保障与以前代码兼容,才持续保留。
(8)以上这6种派生谬误,连同原始的Error对象,都是构造函数。开发者能够应用它们,手动生成谬误对象的实例。这些构造函数都承受一个参数,代表谬误提示信息(message)。

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

自定义谬误

function UserError(message) {  this.message = message || '默认信息';  this.name = 'UserError';}UserError.prototype = new Error();UserError.prototype.constructor = UserError;
new UserError('这是自定义的谬误!');

throw 语句

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

if (x <= 0) {  throw new Error('x 必须为负数');}// Uncaught ReferenceError: x is not defined

(2)throw也能够抛出自定义谬误。

function UserError(message) {  this.message = message || '默认信息';  this.name = 'UserError';}throw new UserError('出错了!');// Uncaught UserError {message: "出错了!", name: "UserError"}

(3)throw能够抛出任何类型的值。也就是说,它的参数能够是任何值。

// 抛出一个字符串throw 'Error!';// Uncaught Error!// 抛出一个数值throw 42;// Uncaught 42// 抛出一个布尔值throw true;// Uncaught true// 抛出一个对象throw {  toString: function () {    return 'Error!';  }};// Uncaught {toString: ƒ}

try...catch 构造

(1)avaScript 提供了try...catch构造,容许对谬误进行解决,抉择是否往下执行。

try {  throw new Error('出错了!');} catch (e) {  console.log(e.name + ": " + e.message);  console.log(e.stack);}// Error: 出错了!//   at <anonymous>:3:9//   ...

(2)catch代码块捕捉谬误之后,程序不会中断,会依照失常流程继续执行上来。

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

(3)catch代码块之中,还能够再抛出谬误,甚至应用嵌套的try...catch构造。

var n = 100;try {  throw n;} catch (e) {  if (e <= 50) {    // ...  } else {    throw e;  }}// Uncaught 100

(4)为了捕获不同类型的谬误,catch代码块之中能够退出判断语句。

try {  foo.bar();} catch (e) {  if (e instanceof EvalError) {    console.log(e.name + ": " + e.message);  } else if (e instanceof RangeError) {    console.log(e.name + ": " + e.message);  }  // ...}

finally 代码块

(1)示意不论是否呈现谬误,都必须在最初运行的语句。

function cleansUp() {  try {    throw new Error('出错了……');    console.log('此行不会执行');  } finally {    console.log('实现清理工作');  }}cleansUp()// 实现清理工作// Uncaught Error: 出错了……//    at cleansUp (<anonymous>:3:11)//    at <anonymous>:10:1

(2)return语句的执行是排在finally代码之前,只是等finally代码执行结束后才返回。

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

(3)catch代码块完结执行之前,会先执行finally代码块。

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// 3result// false

(4)进入catch代码块之后,一遇到throw语句,就会去执行finally代码块,其中有return false语句,因而就间接返回了,不再会回去执行catch代码块剩下的局部了。

function f() {  try {    throw '出错了!';  } catch(e) {    console.log('捕捉到外部谬误');    throw e; // 这句原本会等到finally完结再执行  } finally {    return false; // 间接返回  }}try {  f();} catch(e) {  // 此处不会执行  console.log('caught outer "bogus"');}

(5)try代码块外部,还能够再应用try代码块。

try {  try {    consle.log('Hello world!'); // 报错  }  finally {    console.log('Finally');  }  console.log('Will I run?');} catch(error) {  console.error(error.message);}// Finally// consle is not defined

编程格调

(1)缩进,可应用空格,也能够应用TAB
(2)总是应用大括号示意区块。
(3)JavaScript 会主动增加句末的分号,导致一些难以觉察的谬误。

return{  key: value};// 相当于return;{  key: value};

(4)圆括号

  1. 示意函数调用时,函数名与左括号之间没有空格。
  2. 示意函数定义时,函数名与左括号之间没有空格。
  3. 其余状况时,后面地位的语法元素与左括号之间,都有一个空格。

(5)行尾不应用分号的状况

  • for 和 while 循环
  • 分支语句:if,switch,try
  • 函数的申明语句

(6)do...while循环是有分号的。
(7)函数表达式依然要应用分号。

var f = function f() {};

(8)如果没有应用分号,大多数状况下,JavaScript 会主动增加。

var a = 1// 等同于var a = 1;

(9)如果下一行的开始能够与本行的结尾连在一起解释,JavaScript 就不会主动增加分号。

// 等同于 var a = 3vara=3// 等同于 'abc'.length'abc'.length// 等同于 return a + b;return a +b;// 等同于 obj.foo(arg1, arg2);obj.foo(arg1,arg2);// 等同于 3 * 2 + 10 * (27 / 6)3 * 2+10 * (27 / 6)
x = y(function () {  // ...})();// 等同于x = y(function () {...})();
// 引擎解释为 c(d+e)var a = b + c(d+e).toString();// 引擎解释为 a = b/hi/g.exec(c).map(d)// 正则表达式的斜杠,会当作除法运算符a = b/hi/g.exec(c).map(d);// 解释为'b'['red', 'green'],// 即把字符串当作一个数组,按索引取值var a = 'b'['red', 'green'].forEach(function (c) {  console.log(c);})// 解释为 function (x) { return x }(a++)// 即调用匿名函数,后果f等于0var a = 0;var f = function (x) { return x }(a++)

(10)只有下一行的开始与本行的结尾,无奈放在一起解释,JavaScript 引擎才会主动增加分号。

if (a < 0) a = 0console.log(a)// 等同于上面的代码,// 因为 0console 没有意义if (a < 0) a = 0;console.log(a)

(11)如果一行的起首是“自增”(++)或“自减”(--)运算符,则它们的后面会主动增加分号。

a = b = c = 1a++b--cconsole.log(a, b, c)// 1 2 0
// 等同于a = b = c = 1;a;++b;--c;

(12)如果continuebreakreturnthrow这四个语句前面,间接跟换行符,则会主动增加分号。
(13)因为解释引擎主动增加分号的行为难以预测,因而编写代码的时候不应该省略行尾的分号。
(14)有的代码库在第一行语句开始前,会加上一个分号。能够防止与其余脚本合并时,排在后面的脚本最初一行语句没有分号,导致运行出错的问题。

;var a = 1;// ... 

(15)倡议防止应用全局变量。如果不得不应用,能够思考用大写字母示意变量名,这样更容易看出这是全局变量,比方UPPER_CASE
(16)JavaScript 会主动将变量申明“晋升”(hoist)到代码块(block)的头部。

if (!x) {  var x = {};}// 等同于var x;if (!x) {  x = {};}

(17)所有函数都应该在应用之前定义。函数外部的变量申明,都应该放在函数的头部。
(18)with能够缩小代码的书写,然而会造成混同。

with (o) { foo = bar;} 

下面的代码,能够有四种运行后果:

o.foo = bar;o.foo = o.bar;foo = bar;foo = o.bar; 

这四种后果都可能产生,取决于不同的变量是否有定义。因而,不要应用with语句。
(19)相等运算符会主动转换变量类型,造成很多意想不到的状况。

0 == ''// true1 == true // true2 == true // false0 == '0' // truefalse == 'false' // falsefalse == '0' // true' trn ' == 0 // true 

因而,倡议不要应用相等运算符(==),只应用严格相等运算符(===)。
(20)倡议不要将不同目标的语句,合并成一行。
(21)所有的++运算符都能够用+= 1代替。

++x// 等同于x += 1;

(22)switch...case不应用大括号,不利于代码模式的对立。此外,这种构造相似于goto语句,容易造成程序流程的凌乱,使得代码构造凌乱不堪,不合乎面向对象编程的准则。倡议switch...case构造能够用对象构造代替。

function doAction(action) {  switch (action) {    case 'hack':      return 'hack';    case 'slash':      return 'slash';    case 'run':      return 'run';    default:      throw new Error('Invalid action.');  }}

下面的代码倡议改写成对象构造。

function doAction(action) {  var actions = {    'hack': function () {      return 'hack';    },    'slash': function () {      return 'slash';    },    'run': function () {      return 'run';    }  };  if (typeof actions[action] !== 'function') {    throw new Error('Invalid action.');  }  return actions[action]();}