### 彪哥

爱好前端

- 基础
  - 一、JS的一些名词概念
    - 什么是作用域？
    - 什么是闭包？
    - 什么是构造函数？
    - 什么是实例对象？
    - 什么是this？
    - 什么是原型？
    - 什么是原型链？
    - 什么是constructor？
    - 什么是包装对象？
  - 二、数据类型的转换
    - 1、强制（手动）转换
    - Number()
    - String()
    - Boolean()
    - 2、自动转换
    - 自动转换为布尔值（Boolean）
    - 自动转换为字符串（String）
    - 所有类型的值与字符串相加都会变成字符串
    - 自动转换为数值（Number）
    - 除加号与字符串运行会转成字符串外，其他运行基本都会自动转成数值
  - 三、错误处理机制
    - 1、Error实例对象
    - 2、原生错误类型
    - SyntaxError 对象（语法错误）
    - ReferenceError 对象（引用错误）
    - RangeError 对象（范围错误）
    - TypeError 对象（类型错误）
    - URIError 对象 （URI错误）
    - EvalError 对象 （eval错误）
    - 总结（开发者手动使用）
    - 3、自定义错误
    - 4、throw 语句 （中断程序并抛出错误）
    - 5、try...catch 结构（捕获错误，对错误进行处理，不中断）
    - 6、finally 代码块（在try..catch最后，必执行的）
  - 四、console对象
    - console.time()，console.timeEnd() （用于计算程序的用时）
  - 文档
- 内置对象
- 面向对象
- 异步操作
- DOM
- 事件
- 浏览器模型

- 《JavaScript教程》笔记

heBody
2020-01-12
目录

# 基础

> 说明：本章内容为博主在原教程基础上添加自己的学习笔记，来源https://wangdoc.com/javascript/
> (opens new window)，教程版权归原作者所有。

# # 基础篇

## # 一、JS的一些名词概念

#### # 什么是作用域？

变量存在的范围。

可分为全局作用域和函数作用域，ES6新增块级作用域。

#### # 什么是闭包？

闭包就是能够读取其他函数内部变量的函数。

- 闭包的形式：函数内部定义函数
- 本质上闭包就是将函数内部和外部连接起来的一座桥梁

闭包的作用：

- 可以读取函数内部变量
- 让这些变量始终保持在内存中，即闭包可以使得它诞生的环境一直存在。
- 封装对象的私有属性和私有方法

#### # 什么是构造函数？

用于构造(生成)实例的一个函数，使实例拥有构造函数内定于的属性和方法。

#### # 什么是实例对象？

实例对象就是通过new 构造函数生成的，拥有构造函数内定于的属性和方法的一个对象。

#### # 什么是this？

就是属性或方法当前所在的对象，指向当前运行环境（对象）

#### # 什么是原型？

每个函数都有一个prototype属性，指向一个对象，该对象称为原型对象。

#### # 什么是原型链？

所有对象都有自己的原型对象，由于原型对象也是对象，因此它也有自己的原型，这就会形成一个原型链。

最顶层的原型是Object.prototype。

> 读取对象属性时，JS会先在对象自身上找，找到就直接返回，如果找不到，会到原型上找，如果还是找不到，就会去原型的原型上找，最终会到最顶层的Object.prototype上找，还是找不到就会返回undefined。

#### # 什么是constructor？

prototype原型对象都有一个constructor属性，默认指向prototype对象所在的构造函数。

#### # 什么是包装对象？

包装对象指的是将原始类型（数字、字符串、布尔值）进行实例化。

## # 二、数据类型的转换

### # 1、强制（手动）转换

强制转换主要指使用`Number()`、`String()`和`Boolean()`三个函数，手动将各种类型的值，分别转换成数字、字符串、布尔值。

#### # 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({a: 1}) // NaN
    Number([1, 2, 3]) // NaN
    Number([5]) // 5
    Number([]) // 0

    //使用parseInt()转数组
    parseInt([1, 2, 3]) // 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

#### # String()

    // 原始类型的转换
    String(123) // "123"
    String('abc') // "abc"
    String(true) // "true"
    String(undefined) // "undefined"
    String(null) // "null"

    // 对象的转换
    String({a: 1}) // "[object Object]"
    String([1, 2, 3]) // "1,2,3"
    String([]) // ""  空字符串
    String(function(){}) // "function(){}"

1
2
3
4
5
6
7
8
9
10
11
12

#### # Boolean()

    // 除了这五个为false，其他都为true
    Boolean(undefined) // false
    Boolean(null) // false
    Boolean(0) // false
    Boolean(NaN) // false
    Boolean('') // false

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

    Boolean(1) // true
    Boolean(' ') // true // 注意字符串内有个空格

1
2
3
4
5
6
7
8
9
10
11
12
13
14

### # 2、自动转换

下面介绍自动转换，它是以强制转换为基础的。

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

第一种情况，不同类型的数据互相运算。

    123 + 'abc' // "123abc"

1

第二种情况，对非布尔值类型的数据求布尔值。

    if ('abc') {
      console.log('hello')
    }  // "hello"

1
2
3

第三种情况，对非数值类型的值使用一元运算符（即`+`和`-`）。

    + {foo: 'bar'} // NaN
    - [1, 2, 3] // NaN

1
2

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

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

#### # 自动转换为布尔值（Boolean）

JavaScript 遇到预期为布尔值的地方（比如`if`语句的条件部分），就会将非布尔值的参数自动转换为布尔值。系统内部会自动调用`Boolean`函数。

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

- `undefined`
- `null`
- `+0`或`-0`
- `NaN`
- `''`（空字符串）

下面这个例子中，条件部分的每个值都相当于`false`，使用否定运算符后，就变成了`true`。

    if ( !undefined
      && !null
      && !0
      && !NaN
      && !''
    ) {
      console.log('true');
    } // true

1
2
3
4
5
6
7
8

下面两种写法，有时也用于将一个表达式转为布尔值。它们内部调用的也是`Boolean`函数。

    // 三元运算符
    expression ? true : false

    // 取反运算符
    !! expression

1
2
3
4
5

#### # 自动转换为字符串（String）

JavaScript 遇到预期为字符串的地方，就会将非字符串的值自动转为字符串。具体规则是，先将复合类型的值转为原始类型的值，再将原始类型的值转为字符串。

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

##### # 所有类型的值与字符串相加都会变成字符串

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

1
2
3
4
5
6
7
8
9
10
11

##### #

这种自动转换不注意的话很容易出错。

    var obj = {
      width: '100'
    };

    obj.width + 20 // "10020"
    parerInt(obj.width) + 20 // 120

1
2
3
4
5
6

上面代码中，开发者可能期望返回`120`，但是由于自动转换，实际上返回了一个字符`10020`。正确做法是先把字符串转成数字。

#### # 自动转换为数值（Number）

JavaScript 遇到预期为数值的地方，就会将参数值自动转换为数值。系统内部会自动调用`Number`函数。

##### # 除加号与字符串运行会转成字符串外，其他运行基本都会自动转成数值

    '5' - '2' // 3
    '5' * '2' // 10
    true - 1  // 0
    false - 1 // -1
    '1' - 1   // 0
    '5' * []    // 0
    false / '5' // 0
    'abc' - 1   // NaN
    null + 1 // 1
    undefined + 1 // NaN

    true+true // 2

1
2
3
4
5
6
7
8
9
10
11
12

上面代码中，运算符两侧的运算子，都被转成了数值。

> 注意：`null`转为数值时为`0`，而`undefined`转为数值时为`NaN`。

数值与布尔值、null也会转为数值

    5+true // 6
    5+false // 5
    5+null //5

1
2
3

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

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

1
2
3
4

## # 三、错误处理机制

### # 1、Error实例对象

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

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

1
2

上面代码中，我们调用`Error`构造函数，生成一个实例对象`err`。`Error`构造函数接受一个参数，表示错误提示，可以从实例的`message`属性读到这个参数。抛出`Error`实例对象以后，整个程序就中断在发生错误的地方，不再往下执行。

JavaScript 语言标准只提到，`Error`实例对象必须有`message`属性，表示出错时的提示信息，没有提到其他属性。大多数 JavaScript 引擎，对`Error`实例还提供`name`和`stack`属性，分别表示错误的名称和错误的堆栈，但它们是非标准的，不是每种实现都有。

- message：错误提示信息
- name：错误名称（非标准属性）
- stack：错误的堆栈（非标准属性）

使用`name`和`message`这两个属性，可以对发生什么错误有一个大概的了解。

    var err = new Error('出错了');
    if (err.name) {
      console.log(err.name + ': ' + err.message); // Error: 出错了
    }

1
2
3
4

`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) // 堆栈的最内层throwit函数
    //    at catchit (~/examples/throwcatch.js:3:9) // 向外一层catchit函数
    //    at repl:1:5 // 函数的运行环境

    // 堆栈信息说明
    // Error 错误
    //    at throwit 在throwit方法 (~/examples/throwcatch.js:9:11) 文件名：第几行：第几个字符
    //    at catchit 在catchit方法 (~/examples/throwcatch.js:3:9) 文件名：第几行：第几个字符
    //    at repl:1:5 // 函数的运行环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

上面代码中，错误堆栈的最内层是`throwit`函数，然后是`catchit`函数，最后是函数的运行环境。

### # 2、原生错误类型

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

#### # SyntaxError 对象（语法错误）

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

    // 变量名错误
    var 1a;
    // Uncaught SyntaxError: Invalid or unexpected token
    // 语法错误：无效或意外的标记符号

    // 缺少括号
    console.log 'hello');
    // Uncaught SyntaxError: Unexpected string
    // 语法错误：意外的字符串

1
2
3
4
5
6
7
8
9

上面代码的错误，都是在语法解析阶段就可以发现，所以会抛出`SyntaxError`。第一个错误提示是“token 非法”，第二个错误提示是“字符串不符合要求”。

#### # ReferenceError 对象（引用错误）

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

    // 使用一个不存在的变量
    unknownVariable
    // Uncaught ReferenceError: unknownVariable is not defined
    // 引用错误： unknownVariable 没有定义

1
2
3
4

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

    // 等号左侧不是变量
    console.log() = 1
    // Uncaught ReferenceError: Invalid left-hand side in assignment
    // 引用错误： 赋值中左边是无效的

    // this 对象不能手动赋值
    this = 1
    // ReferenceError: Invalid left-hand side in assignment
    // 引用错误：  赋值中左边是无效的

1
2
3
4
5
6
7
8
9

上面代码对函数`console.log`的运行结果和`this`赋值，结果都引发了`ReferenceError`错误。

#### # RangeError 对象（范围错误）

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

    // 数组长度不得为负数
    new Array(-1)
    // Uncaught RangeError: Invalid array length
    // 范围错误：无效的数组长度

1
2
3
4

#### # TypeError 对象（类型错误）

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

    new 123
    // Uncaught TypeError: number is not a function
    // 类型错误： 数字不是一个函数

    var obj = {};
    obj.unknownMethod()
    // Uncaught TypeError: obj.unknownMethod is not a function
    // 类型错误：obj.unknownMethod 不是一个函数

1
2
3
4
5
6
7
8

上面代码的第二种情况，调用对象不存在的方法，也会抛出`TypeError`错误，因为`obj.unknownMethod`的值是`undefined`，而不是一个函数。

#### # URIError 对象 （URI错误）

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

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

1
2

#### # EvalError 对象 （eval错误）

`eval`函数没有被正确执行时，会抛出`EvalError`错误。该错误类型已经不再使用了，只是为了保证与以前代码兼容，才继续保留。

#### # 总结（开发者手动使用）

以上这6种派生错误，连同原始的`Error`对象，都是构造函数。开发者可以使用它们，手动生成错误对象的实例。这些构造函数都接受一个参数，代表错误提示信息（message）。

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

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

1
2
3
4
5
6
7

### # 3、自定义错误

除了 JavaScript 原生提供的七种错误对象，还可以定义自己的错误对象。

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

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

    // 使用
    new UserError('这是自定义的错误！');

1
2
3
4
5
6
7
8
9
10

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

### # 4、throw 语句 （中断程序并抛出错误）

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

    var x = 0;
    if (x <= 0) {
      throw new Error('x 必须为正数');
    }
    // Uncaught Error: x 必须为正数

1
2
3
4
5

上面代码中，如果变量`x`小于等于`0`，就手动抛出一个错误，告诉用户`x`的值不正确，整个程序就会在这里中断执行。可以看到，`throw`抛出的错误就是它的参数，这里是一个`Error`实例。

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

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

1
2
3
4
5
6
7

上面代码中，`throw`抛出的是一个`UserError`实例。

实际上，`throw`可以抛出任何类型的值。也就是说，它的参数可以是任何值。

    // 抛出一个字符串
    throw 'Error！';
    // Uncaught Error！

    // 抛出一个数值
    throw 42;
    // Uncaught 42

    // 抛出一个布尔值
    throw true;
    // Uncaught true

    // 抛出一个对象
    throw {
      toString: function () {
        return 'Error!';
      }
    };
    // Uncaught {toString: ƒ}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

对于 JavaScript 引擎来说，遇到`throw`语句，程序就中止了。引擎会接收到`throw`抛出的信息，可能是一个错误实例，也可能是其他类型的值。

### # 5、try...catch 结构（捕获错误，对错误进行处理，不中断）

一旦发生错误，程序就中止执行了。JavaScript 提供了`try...catch`结构，允许对错误进行处理，选择是否往下执行。

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

1
2
3
4
5
6
7
8
9

上面代码中，`try`代码块抛出错误（上例用的是`throw`语句），JavaScript 引擎就立即把代码的执行，转到`catch`代码块，或者说错误被`catch`代码块捕获了。`catch`接受一个参数，表示`try`代码块抛出的值。

如果你不确定某些代码是否会报错，就可以把它们放在`try...catch`代码块之中，便于进一步对错误进行处理。

    try {
      f();
    } catch(e) {
      // 处理错误
    }

1
2
3
4
5

上面代码中，如果函数`f`执行报错，就会进行`catch`代码块，接着对错误进行处理。

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

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

1
2
3
4
5
6
7
8

上面代码中，`try`代码块抛出的错误，被`catch`代码块捕获后，程序会继续向下执行。

`catch`代码块之中，还可以再抛出错误，甚至使用嵌套的`try...catch`结构。

    var n = 100;

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

1
2
3
4
5
6
7
8
9
10
11
12

上面代码中，`catch`代码之中又抛出了一个错误。

为了捕捉不同类型的错误，`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);
      }
      // ...
    }

1
2
3
4
5
6
7
8
9
10

上面代码中，`catch`捕获错误之后，会判断错误类型（`EvalError`还是`RangeError`），进行不同的处理。

### # 6、finally 代码块（在try..catch最后，必执行的）

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

    function cleansUp() {
      try {
        throw new Error('出错了……');
        console.log('此行不会执行');
      } finally { // 不管有没有出错都会执行finally
        console.log('完成清理工作');
      }
      console.log('此行不会执行');
    }

    cleansUp()
    // 完成清理工作
    // Uncaught Error: 出错了……
    //    at cleansUp (<anonymous>:3:11)
    //    at <anonymous>:10:1

    // 由于没有catch语句，所以会打印出错误信息，并中断除finally以外的代码。
    // 如果有catch语句则会执行catch内的代码块，而不会打印错误信息。且不会中断代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

上面代码中，由于没有`catch`语句块，一旦发生错误，代码就会中断执行。中断执行之前，会先执行`finally`代码块，然后再向用户提示报错信息。

    function idle(x) {
      try {
        console.log(x);
        return 'result';
      } finally {
        console.log('FINALLY');
      }
    }

    idle('hello')
    // hello
    // FINALLY

1
2
3
4
5
6
7
8
9
10
11
12

上面代码中，`try`代码块没有发生错误，而且里面还包括`return`语句，但是`finally`代码块依然会执行。而且，这个函数的返回值还是`result`。

下面的例子说明，`return`语句的执行是排在`finally`代码之前，只是等`finally`代码执行完毕后才返回。

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

    countUp()
    // 0
    count
    // 1

1
2
3
4
5
6
7
8
9
10
11
12
13

上面代码说明，`return`语句里面的`count`的值，是在`finally`代码块运行之前就获取了。

下面是`finally`代码块用法的典型场景。

    openFile(); // 打开文件

    try {
      writeFile(Data); // 写入文件
    } catch(e) {
      handleError(e); // 如果写入出错这处理错误
    } finally {
      closeFile();// 不管是否出错都会执行关闭文件
    }

1
2
3
4
5
6
7
8
9

上面代码首先打开一个文件，然后在`try`代码块中写入文件，如果没有发生错误，则运行`finally`代码块关闭文件；一旦发生错误，则先使用`catch`代码块处理错误，再使用`finally`代码块关闭文件。

下面的例子充分反映了`try...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
    // 3

    result
    // false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

上面代码中，`catch`代码块结束执行之前，会先执行`finally`代码块。

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

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

    try {
      f(); // 接收到返回值false，并没有接收到错误
    } catch(e) {
      // 此处不会执行
      console.log('caught outer "bogus"');
    }

    //  捕捉到内部错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

上面代码中，进入`catch`代码块之后，一遇到`throw`语句，就会去执行`finally`代码块，其中有`return false`语句，因此就直接返回了，不再会回去执行`catch`代码块剩下的部分了。

`try`代码块内部，还可以再使用`try`代码块。

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

1
2
3
4
5
6
7
8
9
10
11
12
13

上面代码中，`try`里面还有一个`try`。内层的`try`报错（`console`拼错了），这时会执行内层的`finally`代码块，然后抛出错误，被外层的`catch`捕获。

## # 四、console对象

### # console.time()，console.timeEnd() （用于计算程序的用时）

这两个方法用于计时，可以算出一个操作所花费的准确时间。

    console.time('Array initialize');

    var array= new Array(1000000);
    for (var i = array.length - 1; i >= 0; i--) {
      array[i] = new Object();
    };

    console.timeEnd('Array initialize');
    // Array initialize: 1914.481ms

1
2
3
4
5
6
7
8
9

`time`方法表示计时开始，`timeEnd`方法表示计时结束。它们的参数是计时器的名称。调用`timeEnd`方法之后，控制台会显示“计时器名称: 所耗费的时间”。

## # 文档

学习文档：https://wangdoc.com/javascript/
(opens new window)

编辑
(opens new window)
#JavaScript
上次更新: 2022/12/14, 19:36:42
内置对象

内置对象→

最近更新

  01
Git修改分支名
08-11

  02
CSS给table的tbody添加滚动条
06-29

  03
我做了一个手写春联小网页，祝大家虎年暴富 原创
01-28

更多文章>

Copyright © 2016-2022 Hesb | MIT License

- 跟随系统
- 浅色模式
- 深色模式
- 阅读模式