Javascript高级笔记

JS高级学习笔记

基础总结深入理解

数据类型

1. 分类

  • 基本(值)类型
    • String: 任意字符串
    • Number: 任意的数字
    • Boolean: true/false
    • undefined: undefined
    • null: null
  • 对象(引用)类型
    • Object: 任意类型
    • Function: 一种特别的对象(可以执行)
    • Array: 一种特别的对象(对象下标)

2. 判断

  • 如何判断数据类型?
    • typeof
    • instanceof
    • ===

相关问题

  1. undefined与null的区别?

    undefined代表定义未赋值
    null定义并赋值了,只是值为null

  2. 什么时候给变量赋值为null呢?

    初识赋值,表示将要赋值为对象
    结束前,让对象成为垃圾对象(被垃圾回收器回收)

  3. 严格区别变量类型与数据类型?

    • 数据的类型
      • 基本类型
      • 对象类型
    • 变量的类型(变量内存值的类型)
      • 基本类型: 保存就是基本类型的数据
      • 引用类型: 保存的是地址值

数据_变量_内存

1. 什么是数据?

  • 存储在内存中代表特定信息的’玩意’,本质是以0101形式存储
  • 数据的特点
    • 可传递
    • 可运算
  • 哲学思想
    • 一切皆数据
  • 内存中操作的目标
    • 就是操作数据
  • 数据能进行哪些操作?
    • 算术运算
    • 赋值
    • 逻辑运算
    • 运行函数

2. 什么是内存?

  • 内存条通电后产生的可存储数据的空间(临时的)
  • 内存的产生和死亡
    • 内存条(一块电路板) -> 通电 -> 产生内存空间 -> 存储数据 -> 处理数据 -> 断电 -> 内存空间和数据都消失
  • 一块小内存的2个数据
    • 内部存储的数据
    • 地址值
  • 内存分类
    • 栈: 存储 全局变量/局部变量
    • 堆: 存储 对象

3. 什么是变量?

  • 可变化的量,由变量名和变量值组成
  • 每个变量都对应的一块小内存,变量名用来查找对应的内存,变量值就是内存中保存的数据

4. 内存,数据,变量三者之间的关系?

  • 内存用来存储数据的空间
  • 变量是内存的标识

关于赋值和内存的问题

  1. var a = xxx, a内存中到底保存的是什么?

    • xxx是基本数据,保存的就是这个数据
    • xxx是对象, 保存的就是对象的地址值
    • xxx是一个变量, 保存的xxx的内存内容(可能是基本数据, 也可能是地址值)
  2. 关于引用变量赋值问题

  • 2个引用变量指向同一对象, 通过一个变量修改对象内部数据, 另一个变量看到的是修改之后的数据
1
2
3
4
5
6
7
8
9
10
11
12
// 2个引用变量指向同一个对象
var obj1 = {name: 'tom'};
var obj2 = obj1;
// 通过一个变量修改对象内部数据
obj1.name = 'Jack';
// 另一个变量看到的是修改之后的数据
console.log(obj2.name) //Jack
function fn(obj){
obj.name = 'Dock';
}
fn(obj1);
console.log(obj2.name); //Dock
  • 2个引用变量指向同一个对象, 让其中一个引用变量指向另一个对象, 另一个引用变量依然指向前一个对象
1
2
3
4
5
6
7
8
// 2个引用变量指向同一个对象
var a = {age: 12};
var b = a;
// 让其中一个引用变量指向另一个对象
a = {name: 'BoB', age: 13};
b.age = 14;
// 另一个引用变量依然指向前一个对象
console.log(b.age, a.name, a.age); //12 'BoB' 13
  • 在JS调用函数时传递变量参数时, 是值传递还是引用传递
    • 理解1: 都是值(基本/地址值)传递
    • 理解2: 可能是值传递, 也可能是引用传递(地址值)
1
2
3
4
5
6
7
var a = 3;
// 实参a 传递 给形参a 肯定是值传递 形参a 是局部变量 值改变不会影响外部的变量a
function fn (a) {
a = a + 1;
}
fn(a);
console.log(a);
  • JS引擎如何管理内存?
    1. 内存生命周期
      • 分配小内存空间, 得到它的使用权
      • 存储数据, 可以反复进行操作
      • 释放小内存空间
    2. 释放内存
      • 局部变量: 函数执行完自动释放
      • 对象: 成为垃圾对象 -> 垃圾回收器回收

对象

  1. 什么是对象?

    • 多个数据的封装体
    • 用来保存多个数据的容器
    • 一个对象代表现实中的一个事物
  2. 为什么要用对象?

    • 统一管理多个数据
  3. 对象的组成

    • 属性
      • 属性名(字符串)和属性值(任意)组成
    • 方法
      • 一种特别的属性(属性值是函数)
  4. 如何访问对象内部数据?

    • .属性名
      • 编码简单,有时不能用
    • []
      • 编码麻烦,能通用
  5. 什么时候必须使用['属性名']的方式?

    • 属性名包含特殊字符: - 空格
    • 变量名不确定

函数

了解函数

  1. 什么是函数?
    • 实现特定功能的n条语句的封装体
    • 只有函数是可以执行的, 其他类型的数据不能执行
  2. 为什么要用函数?
    • 提高代码复用
    • 便于封装
    • 函数的核心思想
      • 封装
  3. 如何定义函数?
    • 函数声明
    • 表达式
  4. 如何调用(执行)函数?
    • test() : 直接调用
    • obj.test() : 通过对象调用
    • new.test() : new调用
    • test.call/apply(obj) : 临时让test成为obj的方法进行调用

回调函数

  1. 什么函数才是回调函数?
    • 你定义的
    • 你没有掉,但最终它执行了
  2. 常见的回调函数?
    • dom事件回调函数
    • 定时器回调函数
    • ajax请求回调函数
    • 生命周期回调函数
1
<button id='btn'>测试点击事件</button>
1
2
3
4
5
6
7
8
// dom事件回调函数
document.getElementById('btn').onclick = function(){
alert(this.innerHTML);
}
// 定时回调函数
setTimeout(function(){
alert("我是定时器回调函数");
}, 2000);

IIFE(立即调用函数表达式)

  1. 理解
    • 全称: Immediately-Invoked Function Expression
    • 中文解释: 立即调用的函数表达式
  2. 作用
    • 隐藏实现
    • 不会污染外部(全局)命名空间
    • 用它来编写js模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 匿名函数自调用
// 这样写不会污染(全局)命名空间
function(){
var a = 3;
console.log(a + 3);
}()
// 与匿名函数中的a 不是同一个a
var a = 4;
console.log(a);

;function(){
var a = 1;
function test(){
console.log(++a);
}
// 这样写是向外暴露一个全局函数
window.$ = function(){
return {
test: test
}
}
}()
$().test();

函数中的this

  1. this是什么?

    • 任何函数本质上都是通过某个对象来调用的, 如果没有直接指定的就是window
    • 所有函数内部都有一个变量this
    • 它的值是调用函数的当前对象
    • 自己的理解
      • this其实就是函数的调用者, 可能是window,可能是某个对象
  2. 如何确定this的值?

    • test() : window
    • p.test() : p
    • new test() : 新创建的对象
    • p.call(obj) : obj

总结

数据类型:两大类
基本类型(值): String Number Boolean undefined null
对象类型(引用): Object Function(可以执行) Array(有序)
判断数据类型的方法(3种)
typeof(返回数据类型名, 不能区别null、对象和数组)
instanceof(返回true/false, 专门用来判断Object、Array、Function)
===(返回true/false, 只能判断undefined和null)

数据是存储在内存中代表特定信息的东西
内存是通电后临时产生可存储数据的空间
变量是可变化的量, 变量以及变量的值会以数据的形式保存到内存中

对象是多个数据的封装体

函数是实现特定功能的n条语句的封装体

回调函数是自己定义, 没有调用却执行了的特殊函数

IIFE 立即调用函数表达式

this代表函数的调用者

函数高级(重点内容)

原型与原型链

原型

  1. 函数的prototype属性
    • 函数都有prototype属性,默认指向一个Object空对象(既称为原型对象),每个原型对象中有一个属性constructor,它指向函数对象
  2. 给原型对象添加属性(一般是方法)
    • 作用:函数的所有实例对象自动拥有原型中的属性(方法)

显式原型与隐式原型

  1. 每个函数function都有一个prototype, 既显式原型(属性)
  2. 每个实例对象都有一个proto, 可称为隐式原型(属性)
  3. 对象的隐式原型的值为其对应构造函数的显式原型的值

    总结
    函数的prototype属性: 在定义函数时自动添加的,默认值是一个空Object对象
    对象的__proto__属性: 创建对象时自动添加的, 默认值为构造函数的prototype属性值
    程序员能直接操作显式原型,但不能直接操作隐式原型(ES6之前)

原型链

  • 说明
    • 访问一个对象的属性时,先在自身属性中查找,找到返回,如果没有,则沿着__proto__这条链向上查找,找到返回,如果最终没有找到,返回undefined
  • 别名: 隐式原型链
  • 作用
    • 查找对象的属性(方法)
  • 理解原型链的三个基本点
    1. 函数的显式原型指向的对象默认是空Object实例对象(但Objec不满足)
    2. 所有函数都是Function的实例(包含Function)
    3. Object的原型对象是原型链的尽头

原型链_属性问题

  1. 读取对象属性值时: 会自动到原型链中查找
  2. 设置对象属性值时: 不会查找原型链, 如果当前对象中没有此属性,直接添加此属性并设置其值
  3. 方法一般定义在原型中, 属性一般通过构造函数定义在对象本身内

instanceof

  1. instanceof是如何判断的?
    • 表达式: A instanceof B
    • 如果B函数的显式原型对象在A对象的原型链上, 返回true, 否则返回false
  2. Function是通过new自己产生的实例

执行上下文与执行上下文栈

变量提升与函数提升

  1. 变量声明提升
    • 通过var定义(声明)的变量, 在定义语句之前就可以访问到
      • 值为undefined
  2. 函数声明提升
    • 通过function声明的函数, 在之前就可以直接调用
      • 值为函数定义(对象)

        总结一句话就是变量定义前可以使用变量,但值 为undefined

执行上下文(帮助理解内部是如何执行的)

  1. 代码分类(位置)
    • 全局代码
    • 函数(局部)代码
  2. 全局执行上下文(流程)
    • 在执行全局代码前将window确定为全局执行上下文
    • 对全局数据进行预处理
      • var定义的全局变量==>undefined,添加为window的属性
        • 将var定义的变量作为添加到window中
      • function声明的全局函数==>赋值(fun), 添加为window的方法
        • 将function声明的函数作为方法添加到window中
      • this==>赋值(window)
      • 最后才开始执行全局代码
  3. 函数执行上下文(流程)
    • 在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象(虚拟的,存在与栈中)
    • 对局部数据进行预处理
      • 形参变量==>赋值(实参)==>添加为执行上下文的属性
      • arguments==>赋值(实参列表),添加为执行上下文的属性
      • var定义的局部变量==>undefined,添加为执行上下文的方法
      • this==>赋值(调用函数的对象)
      • 最后才开始执行函数体代码

执行上下文栈

  1. 在全局代码执行前,JS引擎就会创建一个栈来存储管理所有的执行上下文对象
  2. 在全局执行上下文(window)确定后,将其添加到栈中(压栈)
  3. 在函数执行上下文创建后,将其添加到栈中(压栈)
  4. 在当前函数执行完后,将栈顶的对象移出(出栈)
  5. 所有的代码执行完后,栈中只剩下window

![](F:\素材整理\素材库.library\images\KCKGAPGDU5S33.info[译] 理解 JavaScript 中的执行上下文和执行栈 - 掘金_thumbnail.png)

作用域与作用域链

作用域

  1. 什么是作用域?
    • 就是一块”地盘”,一个代码段所在的区域
    • 特点:它是静态的(相当于上下文对象),在编写代码时就确定了
  2. 作用域分类
    • 全局作用域
    • 函数作用域
    • 块作用域(ES6存在)
  3. 作用域的作用
    • 隔离变量,不同作用域下同名变量不会有冲突

作用域与执行上下文

  • 作用域与执行上下文的区别
    • 区别一
      • 全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了。而不是在函数调用时
      • 全局执行上下文环境是在全局作用域确定之后,js代码马上执行之前创建
      • 函数执行上下文是在调用函数时,函数体代码执行之前创建
    • 区别二
      • 作用域是静态的,只要函数定义好了就一直存在,且不会再变化
      • 执行上下文是动态的,调用函数时创建,函数调用结束时就会自动释放
  • 作用域与执行上下文的联系
    • 执行上下文(对象)是从属于所在的作用域
    • 全局上下文环境==>全局作用域
    • 函数上下文环境==>对应的函数使用域

作用域链

  • 什么是作用域链?
    • 多个上下级关系的作用域形成的链,它的方向是从下向上的(从内到外)
    • 查找变量时就是沿着作用域链来查找的
  • 查找一个变量的查找规则
    • 在当前作用域下的执行上下文中查找对应的属性,如果有直接返回,否则进入2
    • 在上一级作用域的执行上下文中查找对应的属性,如果有直接返回,否则进入3
    • 再次执行2的相同操作,直到全局作用域,如果还找不到就抛出找不到的异常

闭包

了解闭包

  1. 如何产生闭包?
    • 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包
  2. 闭包是什么?
    • 闭包是嵌套的内部函数
    • 包含被引用变量(函数)的对象
    • 注意:
      • 闭包存在于嵌套的内部函数中
  3. 产生闭包的条件?
    • 函数嵌套
    • 内部函数引用了外部函数的数据(变量/函数)
1
2
3
4
5
6
7
8
9
10
11
function fn1() {
var a = 2;
function fn2() {//产生了闭包
a++;
console.log(a);
}
return fn2
}
var f = fn1();
f() // 3
f() // 4

闭包的作用

  • 使用函数内部的变量在函数执行完厚,仍然存活在内存中(延长了局部变量的生命周期)
  • 让函数外部可以操作(读写)到函数内部的数据(变量/函数)

闭包的生命周期

  • 产生
    • 在嵌套内部函数定义执行完时就产生了(不是在调用)
  • 死亡
    • 在嵌套的内部函数成为垃圾对象时

闭包的应用

  • 定义JS模块
    • 具有特定功能的js文件
    • 将所有的数据和功能都封装在一个函数内部(私有的)
    • 只向外暴露一个包含n个方法的对象或函数
    • 模块的使用者,只需要通过模块暴露的对象调用方法来实现对应的功能

闭包的缺点以及如何解决

  • 缺点
    • 函数执行完厚,函数内的局部变量没有释放,占用内存时间会变长
    • 容易造成内存泄漏
  • 解决
    • 能不用闭包就不用闭包
    • 及时释放
      • 变量 = null

内存溢出与内存泄露

  1. 内存溢出
    • 一种程序运行出现的错误
    • 当程序运行需要的内存超过了剩余的内存时, 就出抛出内存溢出的错误
  2. 内存泄露
    • 占用的内存没有及时释放
    • 内存泄露积累多了就容易导致内存溢出
    • 常见的内存泄露:
      • 意外的全局变量
      • 没有及时清理的计时器或回调函数
      • 闭包

总结

原型分为显式原型和隐式原型

每个函数都有一个prototype,也就是显式原型

每个实例对象都有一个__proto__,也就是隐式原型

实例对象的隐式原型的值等于构造函数的显式原型的值

原型链:通过隐式原型查找属性或方法的一条路径,Object的原型对象是原型链的尽头

原型链属性问题:读取对象属性或方法时才会查找原型链,设置时则不会

A instanceof B

B函数的显式原型对象在A对象的原型链上,返回true,否则返回false

变量声明提升:通过var声明的变量, 在执行语句前,就会保存到window对象中,只是没有赋值

函数声明提升:通过function声明的函数,在声明语句前就可以调用

执行上下文:当前javascript代码被解析和执行时所在的环境的抽象概念

特点:动态生成 调用函数时就会自动生成一个执行上下文 结束时会自动销毁

作用域:一个代码所在区域

特点:静态,作用域在编写代码时就确定了

闭包:一个函数可以访问另一个函数作用域中的变量,前者是闭包

作用:让子函数能访问到父函数中的变量

面向对象高级(重点内容)

对象的创建模式

Object构造函数模式

  • 创建流程

    • 先创建空的Object对象,再动态添加属性/方法
  • 适用场景

    • 起始时不确定对象内部数据
  • 存在问题

    • 语句太多
  • 实现代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 创建空Object对象
    var obj = new Object();
    var obj = {};
    // 动态添加属性/方法
    obj.name = 'Tom';
    obj.age = 14;
    obj.setName = function(name){
    this.name = name
    }

对象字面量模式

  • 创建流程

    • 使用{}创建对象,同时指定属性/方法
  • 适用场景

    • 起始时对象内部属性是确定的
  • 存在问题

    • 如果创建多个对象,代码冗余
  • 实现代码

    1
    2
    3
    4
    5
    6
    7
    8
    // {}创建对象, 同时指定属性/方法
    var obj = {
    name: 'Tom',
    age: 14,
    setName: function(name){
    this.name = name;
    }
    }

工厂模式

  • 创建流程

    • 通过工厂函数动态创建对象并返回
  • 适用场景

    • 需要创建多个对象
  • 存在问题

    • 对象没有具体的类型,都是Object类型
  • 实现代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 创建工厂函数
    function createPerson(name, age){
    var obj = {
    name: name,
    age: age,
    setName: function(name){
    this.name = name
    }
    }
    return obj
    }

自定义构造函数模式

  • 创建流程

    • 自定义构造函数,通过new创建对象
  • 适用场景

    • 需要创建多个类型确定的对象
  • 存在问题

    • 每个对象都有相同的数据,浪费内存
  • 实现代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 创建自定义构造函数
    function Person(name, age){
    this.name = name;
    this.age = age;
    this.setName = function(name){
    this.name = name
    }
    }
    // new创建对象
    var p1 = new Person('Tom', 12);
    p1.setName('YF');
    var p2 = new Person('Bom', 14);
    p2.setName('YangFan');

构造函数+原型的组合模式

  • 创建流程

    • 自定义构造函数,属性在函数中初始化,方法添加到原型中
  • 适用场景

    • 需要创建多个类型确定的对象
  • 实现代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // 自定义构造函数
    function Person(name, age){
    this.name = name;
    this.age = age;
    }
    // 方法添加到原型中
    Person.prototype.setName = function(name){
    this.name = name;
    }

    // new 创建对象
    var p1 = new Person('Tom', 14);
    p1.setName('YF');
    var p2 = new Person('Bom', 12);
    p2.setName('YangFan');

继承模式

原型链继承

  • 实现流程
    1. 定义父类型构造函数
    2. 给父类型的原型添加方法
    3. 定义子类型的构造函数
    4. 创建父类型的对象赋值给子类型的原型
    5. 将子类型的原型的构造属性设置为子类型
    6. 给子类型原型添加方法
    7. 创建子类型的对象: 可以调用父类型的方法
  • 实现继承的关键
    • 子类型的原型为父类的一个实例对象
  • 实现代码
    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
    // 定义父类型构造函数
    function Supper(){
    this.supPro = "Supper property";
    }
    // 给父类型的原型添加方法
    Supper.prototype.showSupPro = function(){
    console.log(this.supPro);
    }
    // 定义子类型的构造函数
    function Sub(){
    this.subPro = "Sub property";
    }
    // 创建父类型的对象赋值给子类型的原型
    Sub.prototype = new Supper();
    // 将子类型的原型的构造属性设置为子类型
    Sub.prototype.constructor = Sub;
    // 给子类型原型添加方法
    Sub.prototype.showSubPro = function(){
    console.log(this.subPro);
    }

    // 创建子类型对象
    var s1 =new Sub();
    // 调用父类型方法
    s1.showSupPro();

借用构造函数继承(不是真正意义上的继承)

  • 实现流程
    • 定义父类型构造函数
    • 定义子类型构造函数
    • 在子类型中构造函数中调用父类型构造
  • 实现继承的关键
    • 在子类型构造函数中通过call()调用父类型构造函数
  • 实现代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 定义弗雷西构造函数
    function Person(name, age){
    this.name = name;
    this.age = age;
    }
    // 定义子类型构造函数
    function Student(name, age, price){
    // 在子类型中构造函数中调用父类型构造
    Person.call(this, name, age);
    this.price = price;
    }

    var s1 = new Student('Tom', 14, 1000);
    console.log(s1.name, s1.age, s1.price);

原型链+借用构造函数的组合继承

  • 实现关键

    • 利用原型链实现对父类型对象的方法的继承
    • 利用call()借用父类型构建函数初始化相同属性
  • 实现代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    function Person(name, age){
    this.name =name;
    this.age = age;
    }
    Person.prototype.setName = function(name){
    this.name = name;
    }

    function Student(name, age, price){
    Person.call(this, name, age);
    this.price = price;
    }

    Student.prototype = new Person(); // 这一步是为了让子类型能使用到父类型的方法
    Student.prototype.constructor = Student; // 这一步是为了修正constructor属性
    Student.prototype.setPrice = function(price){
    this.price = price;
    }

    var s1 =new Student('Tom', 14, 10000);
    s1.setName('Bom');
    s1.setPrice(8000);
    console.log(s1);

知识点

  • new一个对象背后做了什么?
    • 创建一个空对象
    • 给对象设置proto,值为构造函数的prototype属性值 this.proto = Fn.prototype
    • 执行构造函数体(给对象添加属性/方法)

线程机制与事件机制

线程与进程

  • 进程

    • 每个程序必须有一个进程,占有一片独立的内存空间
    • 可以通过windows任务管理查看进程
  • 线程

    • 进程内的一个地理执行单元
    • 执行程序的一个完整流程
    • CPU的最小调度单元
  • 进程与线程的关系

    • 一个进程至少有一个线程(主)
    • 程序是在某个进程中的某个线程执行的

浏览器内核模块组成

  • 主线程

    • JS引擎模块
      • 负责js程序的编译与运行
    • HTML/CSS文档解析模块
      • 负责页面文本的解析
    • DOM/CSS模块
      • 负责dom/css在内存中的相关处理
    • 布局和渲染模块
      • 负责页面的布局和效果的绘制(内存中的对象)
  • 分线程

    • 定时器模块
      • 负责定时器的管理
    • DOM事件模块
      • 负责事件的管理
    • 网络请求模块
      • 负责Ajax请求

JS线程

  • JS是单线程执行的(回调函数也是在主线程)
  • H5提出了实现多线程的方案: Web Workers
  • 只能是主线程更新界面

定时器问题

  • 定时器并不真正完全定时
  • 如果在主线程执行了一个长时间的操作,可能导致延时才处理

事件处理机制

  • 代码分类

    • 初始化执行代码
      • 包含绑定DOM事件监听、设置定时器、发送Ajax请求的代码
    • 回调执行代码
      • 处理回调逻辑
  • JS引擎执行代码的基本流程

    • 初始化代码 ==> 回调代码
  • 模型的两个重要组成部分

    • 事件管理模块
    • 回调队列
  • 模型的运转流程

    • 执行初始化代码,将事件回调函数交给对应模块管理
    • 当事件发生时,管理模块会将回调函数及其数据添加到回调列队中
    • 只有当初始化代码执行完后(可能要一定时间),才会遍历读取回调队列中的回调函数执行

H5 Web Workers

  • 可以让js在分线程执行

  • Worker实现代码

    1
    2
    3
    4
    5
    6
    7
    var woker = new Worker('worker.js');
    // 用来接收另一个线程发送过来的数据的回调
    worker.onMessage = function(event){
    event.data;
    }
    // 向另一个线程发送数据
    worker.postMessage(data1);
  • 问题

    • worker内代码不能操作DOM更新UI

    • 不是每个浏览器都支持这个新特性

    • 不能跨域加载JS

      总结

      一个进程至少有一个线程(主)

      浏览器内核模块组成

      主线程中有JS引擎模块HTML/CSS文档解析模块DOM/CSS模块布局和渲染模块

      分线程中有定时器模块DOM事件模块网络请求模块

      JS是单线程执行的

      定时器并不真正完全定时

      在事件处理机制中代码分为初始化执行代码和回调执行代码

      Workers是H5中的分线程执行功能,但是存在一些问题