五 语法专题

2018-08-13  本文已影响0人  智勇双全的小六

1 数据类型的转换

https://wangdoc.com/javascript/features/conversion.html#number

1.1 概述

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

let  a = 1;
a = "hello";

这种操作,在静态语言中是不允许的。
虽然变量对变量类型没要求,但是运算符对数据类型有要求。如果运算符发现运算子的类型与预期不符合,就会进行类型转换。

1.2 强制转换

1.2.1 Number

(1)原始类型的值

// 数值:转化后还是原来的值
Number(324) // 324
// 字符串,如果可以被解析为数字,则转化为数字,否则为 NaN
Number('324') // 324
Number('324abc') // NaN
// 空字符串、false 和 null 会被转为 0
Number('') // 0
Number(null) //0
Number(false) // 0
// undefined 转为 NaN
Number(undefined) // NaN
// 布尔值 true
Number(true) // 1

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

let a = "34ca"
Number(a) // NaN
parseInt(a) // 34

上面的代码中,parseInt 逐个解析字符串,而Number函数整体转化字符串的类型。
当然, parseInt 和 Number 都会自动过滤一个字符串的前后的空格。

parseInt("    23 ");  // 23
Number(" 34 ") // 34

(2)对象
Number 方法的参数就是对象时,将返回 NaN,除非是包含单个数值的数组。

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

Number 背后的转换规则比较复杂。

  1. 调用对象自身的 valueof 方法。如果返回的类型是原始类型的值,则直接对该值使用 Number 函数,不再进行后续方法。
  2. 如果 valueOf 返回的还是对象,则改为调用对象自身的 toString 方法。如果 toString 方法返回原始类型的值,则对改值使用 Number 函数。
  3. 如果 toString 返回的还是对象,则返回 NaN
    Number({}) // NaN
    Number([]) // 0

1.2.2 String

String 函数可以将任意类型的值转化为字符串,转化规则如下:
(1) 原始类型值

String(123) // "123"
String('abc') // "abc"
String(true) // "true"
String(undefined) // "undefined"
String(NaN) // "NaN"

(2) 对象
String 方法的参数如果是对象,则返回一个类型字符串;如果是数组,返回该数组的字符串形式。

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

String 方法背后的转换规则,与 Number 方法基本相同,只是互换了valueOf 方法和toString 方法的执行顺序。

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

1.2.3 Boolean

以下五个值的结果转化为 false,其他值全部为 true

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

空的对象在 Python 中就是 false

1.3 自动转换

遇到以下三种情况,JS 会自动进行数据类型转化。
第一种,不同数据类型的数据互相运算。

123 + "abc" // "123abc"

d第二种,对非布尔值的数据求布尔值。(这种 Python也会发生数据类型转化,而GO就非常严格,要求类型转化必须是显示的)

if("a"){
}

第三种,对于非数值类型的值使用一元运算符

+ "1" // 1
+ {a:1} // NaN

1.3.1 自动转换为布尔值

JS 遇到预期为布尔值的地方就会将非布尔值的参数自动转化为布尔值。
以下五个值的布尔值都是 false

NaN
''
undefined
0 -0
null

1.3.2 自动转化为字符串

一般使用加法时,如果一个运算子是字符串,另外一个就会转化为字符串。具体规则是,先将复合类型的值转为原始类型的值,再将原始类型的值转为字符串。

1.3.3 自动转化为数值

除了加法运算符优先转化为字符串,其他运算符都会把运算子转化为数值。

2 错误处理机制

https://wangdoc.com/javascript/features/error.html

2.1 Error 实例对象

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

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

Error 是一个构造函数,会生成一个实例对象。Error 构造函数接受一个参数,表示错误提示,可以从实例的 message 属性读到这个参数。

除了message 属性,还提供 namestack 属性,分别表示错误的名称和错误的堆栈,但它们是非标准的,不是每种实现都有。

if (error.name){
  console.log(error.name + ": " + error.message);
}

使用 stack 属性来查看错误发生时的堆栈。

function throwIt(){
  throw new Error('I am Error');
}

function catchIt(){
  try{
    throwIt();
  } catch(e){
    console.log(e.stack);
  }
}

catchIt();
// Error: I am Error
// at throwIt(~/examples/)
// at catchIt()
// at <anonymous>

上面的代码中,错误堆栈的最内部是 throwIt 函数,然后是 catchIt 函数,最后是函数的运行环境

2.2 原生错误类型

Error 实例对象是最一般的错误类型,在此基础上,还派生了 6 种其他的错误对象。

2.2.1 SyntaxError 对象

SyntaxError 对象是解析代码时发生的语法错误。

var 1a;
// Uncaught SyntaxError: Invalid or unexpected token

2.2.2 ReferenceError 对象

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

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

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

// this 对象不能手动赋值
this  = 1
// ReferenceError: Invalid left-hand side in assignment

2.2.3 RangeError 对象

RangeError 对象是一个值超出有效范围时发生的错误。主要情况,一是数组长度为负数,二是 Number 对象的方法超出范围,以及函数堆栈超过最大值。

// 数组长度不能为负数
new Array(-1)

在 Python 中索引越界也会抛 一个 outOfRange 的错误,但是这种在 JS 中是不存在这种情况的。

2.2.4 TypeError 对象

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

new 123
// Uncaught TypeError: 123 is not a constructor
var obj = {};
obj.a();
// Uncaught TypeError: obj.a is not a function

2.2.5 URIError 对象

URIError 对象是 URI 相关函数的参数不正确时抛出的错误,encodeURI() / decodeURI / encodeURIComponent / decodeURICompoent() / escape()unescape() 这六个函数。

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

2.2.6 EvalError 对象

eval 函数没有被正确执行时,会抛出 EvalError 错误。该错误类型已经不再使用。保留只是为了跟代码兼容。

2.2.7 总结

以上 6 种错误类型连同原始的 Error 对象都是构造函数,开发者可以自定义函数。

var err1 = new Error("自定义 Error");
var err2 = new RangeError("I am Error");

2.3 自定义错误

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

UserError.prototype = new Error();
UserError.prototype.constructor = UserError;

上面代码自定义一个错误对象 UserError , 让它继承 Error 对象。然后,就可以生成这种自定义类型的错误了。

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

2.4 throw 语句

throw 是 Python 中的 raise。
但是 throw 想扔啥就扔啥。

throw "Error"; // 字符串
throw 42; // 数值
throw true; // 布尔值

2.5 try ... catch 结构

python 和 JS 都支持这种写法,这是我以前用的比较少的:

try{

}catch (a){
}catch (b){
}catch (c){
}

2.6 finally 代码块

3 编程风格

https://wangdoc.com/javascript/features/style.html

3.1 概述

3.2 缩进

我都是用 tab

3.3 区块

如果循环和判断只有一行,可以省略大括号。这种臭毛病在 JAVA 中也有。我不喜欢

3.4 圆括号

圆括号的作用有两种:一种表示函数的调用;另外一种是表达式的组合。

// 圆括号表示函数的调用
console.log("abc");
// 圆括号表示表达式的组合
(1 + 2) * 3

表示表达式组合的时候,加空格比较好

3.5 行尾的分号

添加分号更好,我认为。

3.5.1 不使用分号的情况

  1. for 和 while 循环
  2. if switch try
  3. 函数的声明
    但是注意,函数表达式是需要添加分号
var f = ()=>{};

3.5.2 分号的自动添加

除了以上三种情况,JS 会自动添加。
如果 continue / break / return 和 throw 这四个语句后面,直接跟换行符,则会自动添加分号。这意味着,如果return 语句返回的是一个对象的字面量,起首的大括号一定要写在同一行,否则达不到预期的效果。

return 
{ first: 'jane'};

// 解释成
return;
{first : 'jane'}

// 实际案例
function a(){
  return
  {first : 'Jane'};
}

另外不写分号,有些 JS 代码压缩工具不会自动添加分号,遇到没有分号结尾的代码就会让他保护原装。
不写分号,可能会导致脚本合并除错。所以有的代码库,会在第一行语句开始前加上分号

;var a = 1;

3.6 全局变量

https://wangdoc.com/javascript/features/style.html#%E6%A6%82%E8%BF%B0
全局变量对于任何一个代码块都是可读可写的,应该避免使用全局变量。

3.7 变量的声明

JS 会自动将变量声明提升到代码块的头部。

if (!x){
  var x = {};
}

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

这意味着,变量 x 是 if 代码块之前就存在的。为了避免可能出现的问题,最好把变量声明都放在代码块的头部。

for (var i = 0; i < 10; i++){
  //...
}

// 写成
var i;
for(i = 0;i<10;i++){
  // ...
}

这样的写法,就容易看出 i 是一个全局的循环变量 i。
另外,所有函数都应该在使用之前定义。函数内部的变量声明,都应该放在函数的头部。(不然它也会提升到头部的。)

3.8 with 语句

最好不用 with

3.9 相等和严格相等

使用严格相等

3.10 语句的合并

没事合并语句就是有病

3.11 自增和自减运算符

不要用 ++ ,而是 += 1

3.12 switch .. case 结构

o ,朕知道了

4 console 对象与控制台

4.1 console 对象

Chrome 控制台上的各个板块:

4.2 console 对象的静态方法

4.2.1 console.log(), console.info(), console.debug()

console.log 可以接受一个或多个参数,将它们连接起来输出。

console.log(1,"2")

console.log 还支持字符串格式化:

console.log("%s + %s",1,2)
// 1 + 2

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

console.log("%cThis text is styled", 'color: red; background: yellow; font-size: 24px;')

info / debug 都是不同的级别而已,debug 默认情况下在不显示,只有开启 All levels 才显示。

4.2.2 console.warn(), console.error()

warn 和 error 也是不同的日志级别而已, error 的出现会有一个小红叉,warn 的出现会有一个黄色的惊叹号

4.2.3 console.table()

可以把 对象/列表 以表格的形式打印出来。

var a = [{a:0,b:1,c:2},{a:00,b:11,c:22},{a:000,b:111,c:222}]
index a b c
0 0 1 2
1 00 11 22
2 000 111 222

4.2.4 console.count()

count 方法用于计数,输出它被调用了多少次。

function greet(user){
  console.count();
  return "hi " + user;
}

greet('bob');
// default: 1
// hi bob
greet("jack");
// default: 2
// hi jack

该函数还可以接受一个字符串作为参数,作为标签,可以区分不同参数的调用次数,真是挺神奇的。

function greet(user){
  console.count(user);
  return "hi " + user;
}

greet('bob');
bob: 1
greet("jack");
jack: 1

4.2.5 console.dir(), console.dirxml()

console.dir({f1: "foo", "f2": "bar"})
// objct
// f1: "foo"
// f2: "bar"

该方法对于输出 DOM 对象非常有用,会显示 DOM 对象的所有属性。

console.dir(document.body);

在 Node 的环境下,还可以指定以代码高亮的形式输出。

console.dir(obj, {colors: true});

dirxml 方法主要以目录数的形式,显示 DOM 节点。

console.dirxml(document.body)

如果不是 DOM 节点,而是普通的 JS 对象,则输出与 console.dir 相同。

4.2.6 console.assert()

console.assert 会对条件进行断言,如果被断言的语句为假,不会终端程序,而是输出一条错误日志。

console.assert(false, "判断条件不成立");
// Assertion failed: 判断条件不成立

相当于:

try{
  if (not false){
    throw new Error("判断条件不成立");
  }
} catch(e){
    console.error(e);
}

下面一个例子,判断子节点的个数是不是大于等于 2。

console.assert(document.body.childNodes.length < 0, "节点大于等于 2");

4.2.7 console.time(), console.timeEnd()

这两个方法用于计时,可以算出一个操作可以花费的准确事件。这两个方法是一对,传入的参数是计时器的名称

console.time("Array");
var array = new Array(1000000);
for(var i = array.length - 1; i >= 0; i--){
  array[i] = new Object();
}
console.timeEnd("Array");
// Array initialize: 1914.481 ms;

4.2.8 console.group(), console.groupEnd(), console.groupCollapsed()

console.group("g1");
console.log("一级分组的内容")

console.group("g2");
console.log("二级分组内容");

console.groupEnd(); // 二级分组结束
console.groupEnd(); // 一级分组结束

4.2.9 console.trace(), console.clear()

console.trace 方法显示当前执行的代码在堆栈中的调用路径。

console.trace()
// 代码在堆栈中的路径

console.clear用于清除当前控制台的所有输出,将光标回置到第一行。

4.3 控制台命令 API

浏览器控制台中,除了使用 console对象,还可以使用一些控制台自带的命令行。
(1) $_
$_ 属性返回上一个表达式的值。

2 + 2 
// 4
$_
// 4

(2) 0-4
....
感觉没啥用

4.4 debugger 语句

debugger 语句用于除错,设置断点。如果有正在运行的除错工具,程序运行到 debugger 语句时会自动停下。如果没有除错工具,debugger 语句不会产生任何结果,JS 引擎会自动跳过这一句。

Chrome 浏览器中,当代码运行到debugger语句时,就会暂停运行,自动打开脚本源码界面。

for(var i = 0; i < 5; i++){
  console.log(i);
  if (i === 2) {
    debugger;
  }
}
上一篇下一篇

猜你喜欢

热点阅读