JS高级
### 数据类型
1. 分类(2大类)
* 基本(值)类型
* Number: 任意数值
* String: 任意文本
* Boolean: true/false
* undefined: undefined
* null: null
* 对象(引用)类型
* Object: 一般对象类型
* Array: 特别的对象类型(下标/内部数据有序)
* Function: 特别的对象类型(可执行)
2. 判断
* typeof:
* 可以区别: 数值, 字符串, 布尔值, undefined, function
* 不能区别: null与对象, 一般对象与数组
* instanceof
* 专门用来判断对象数据的类型: Object, Array与Function
* ===
* 可以判断: undefined和null
- 1. 分类
- 2. 判断
- 相关问题
1. undefined与null的区别?
* undefined代表变量没有赋值
* null: 代表变量赋值了, 只是值为null
2. 什么时候将变量赋值为null?
* 初始化赋值: 将要作为引用变量使用, 但对象还没有确定
* 结束时: 将变量指向的对象成为垃圾对象
3. 理解变量类型与数据类型?
* js的变量本身是没有类型的, 变量的类型实际上是变量内存中数据的类型
* 变量类型:
* 基本类型: 保存基本类型数据的变量
* 引用类型: 保存对象地址值的变量
* 数据对象
* 基本类型
* 对象类型
- 1. undefined与null的区别?
- 2. 什么时候将变量赋值为null?
- 3. 严格区别变量类型与数据类型?
### 数据, 变量与内存
1. 什么是数据?
* 存储于内存中代表特定信息的'东东', 本质就是0101二进制
* 具有可读和可传递的基本特性
* 万物(一切)皆数据, 函数也是数据
* 程序中所有操作的目标: 数据
* 算术运算
* 逻辑运算
* 赋值
* 调用函数传参
...
2. 什么是内存?
* 内存条通电后产生的存储空间(临时的)
* 产生和死亡: 内存条(集成电路板)==>通电==>产生一定容量的存储空间==>存储各种数据==>断电==>内存全部消失
* 内存的空间是临时的, 而硬盘的空间是持久的
* 分配内存: 声明变量和函数或创建对象时, JS引擎会自动为此分配一定大小的内存来存放对应的数据
* 释放内存: 清空内存中的数据, 标识内存可以再分配使用(内存不释放就不能复用)
* 自动释放: 栈空间的局部变量
* 垃圾回调器回调: 堆空间的垃圾对象
* 一块内存包含2个数据
* 内部存储的数据(一般数据/地址数据)
* 内存地址值数据
* 内存分类
* 栈: 全局变量, 局部变量 (空间较小)
* 堆: 对象 (空间较大)
3. 什么是变量?
* 值可以变化的量, 由变量名与变量值组成
* 一个变量对应一块小内存, 变量名用来查找到内存, 变量值就是内存中保存的内容
4. 内存,数据, 变量三者之间的关系
* 内存是一个容器, 用来存储程序运行需要操作的数据
* 变量是内存的标识, 我们通过变量找到对应的内存, 进而操作(读/写)内存中的数据
- 1. 什么是数据?
- 2. 什么是内存?
- 3. 什么是变量?
- 4. 内存,数据, 变量三者之间的关系
- 相关问题
1. 问题1: var a = xxx, a内存中到底保存的是什么?
* xxx是一个基本数据
* xxx是一个对象
* xxx是一个变量
2. 关于引用变量赋值问题
* 2个引用变量指向同一个对象, 通过一个引用变量修改对象内部数据, 另一个引用变量也看得见
* 2个引用变量指向同一个对象,让一个引用变量指向另一个对象, 另一个引用变量还是指向原来的对象
3. 问题: 在js调用函数时传递变量参数时, 是值传递还是引用传递?
* 只有值传递, 没有引用传递, 传递的都是变量的值, 只是这个值可能是基本数据, 也可能是地址(引用)数据
* 如果后一种看成是引用传递, 那就值传递和引用传递都可以有
4. 问题: JS引擎如何管理内存?
1. 内存生命周期
1). 分配需要的内存
2). 使用分配到的内存
3). 不需要时将其释放/归还
2. 释放内存
* 为执行函数分配的栈空间内存: 函数执行完自动释放
* 存储对象的堆空间内存: 当内存没有引用指向时, 对象成为垃圾对象, 垃圾回收器后面就会回收释放此内存
- 关于赋值与内存的问题?
- 关于引用变量赋值问题?
- 关于数据传递问题?
- JS引擎如何管理内存?
### 对象
1. 什么是对象?
* 代表现实中的某个事物, 是该事物在编程中的抽象
* 多个数据的集合体(封装体)
* 用于保存多个数据的容器
2. 为什么要用对象?
* 便于对多个数据进行统一管理
3. 对象的组成
* 属性
* 代表现实事物的状态数据
* 由属性名和属性值组成
* 属性名都是字符串类型, 属性值是任意类型
* 方法
* 代表现实事物的行为数据
* 是特别的属性==>属性值是函数
4. 如何访问对象内部数据?
* .属性名: 编码简单, 但有时不能用
* ['属性名']: 编码麻烦, 但通用
- 1. 什么是对象?
- 2. 为什么要用对象?
- 3. 对象的组成
- 4. 如何访问对象内部数据?
- 相关问题
什么时候必须使用['属性名']的方式访问对象内部数据?
* 属性名不是合法的标识名
* 属性名不确定
- 什么时候必须使用['属性名']的方式?
### 函数
1. 什么是函数?
* 具有特定功能的n条语句的封装体
* 只有函数是可执行的, 其它类型的数据是不可执行的
* 函数也是对象
2. 为什么要用函数?
* 提高代码复用
* 便于阅读和交流
3. 如何定义函数?
* 函数声明
* 表达式
4. 调用(执行)函数
* test()
* new test()
* obj.test()
* test.call/apply(obj)
- 1. 什么是函数?
- 2. 为什么要用函数?
- 3. 如何定义函数?
- 4. 如何调用(执行)函数?
- 5. 回调函数
1. 什么函数才是回调函数?
* 你定义的
* 你没有直接调用
* 但最终它执行了(在特定条件或时刻)
2. 常见的回调函数?
* DOM事件函数
* 定时器函数
* ajax回调函数(后面学)
* 生命周期回调函数(后面学)
- 1. 什么函数才是回调函数?
- 2. 常见的回调函数?
- 6. IIEF
1. 理解
* 全称: Immediately-Invoked Function Expression 立即调用函数表达式
* 别名: 匿名函数自调用
2. 作用
* 隐藏内部实现
* 不污染外部命名空间
- 1. 理解
- 2. 作用
- 7. 函数中的this
function Person(color) {
// console.log(this)
this.color = color;
this.getColor = function () {
// console.log(this)
return this.color;
};
this.setColor = function (color) {
// console.log(this)
this.color = color;
};
}
Person("red"); //this是谁?
var p = new Person("yello"); //this是谁?
p.getColor(); //this是谁?
var obj = {};
p.setColor.call(obj, "black"); //this是谁?
var test = p.setColor;
test(); //this是谁?
function fun1() {
function fun2() {
console.log(this);
}
fun2(); //this是谁?
}
fun1();
## 函数高级
### 原型与原型链
- 原型(prototype)
1. 函数的prototype属性(图)
* 每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)
* 原型对象中有一个属性constructor, 它指向函数对象
2. 给原型对象添加属性(一般都是方法)
* 作用: 函数的所有实例对象自动拥有原型中的属性(方法)
- 1. 函数的protype属性
- 2. 给原型对象添加属性(一般都是方法)
- 显式原型与隐式原型
1. 每个函数function都有一个prototype,即显式原型
2. 每个实例对象都有一个__proto__,可称为隐式原型
3. 对象的隐式原型的值为其对应构造函数的显式原型的值
4. 内存结构(图)
5. 总结:
* 函数的prototype属性: 在定义函数时自动添加的, 默认值是一个空Object对象
* 对象的__proto__属性: 创建对象时自动添加的, 默认值为构造函数的prototype属性值
* 程序员能直接操作显式原型, 但不能直接操作隐式原型(ES6之前)
- 原型链
- 1. 原型链
1. 原型链(图解)
* 访问一个对象的属性时,
* 先在自身属性中查找,找到返回
* 如果没有, 再沿着__proto__这条链向上查找, 找到返回
* 如果最终没找到, 返回undefined
* 别名: 隐式原型链
* 作用: 查找对象的属性(方法)
2. 构造函数/原型/实体对象的关系(图解)
3. 构造函数/原型/实体对象的关系2(图解)
- 2. 构造函数/原型/实例对象的关系(图解)
- var o1 = new Object();
var o2 = {};
- 3. 构造函数/原型/实例对象的关系2(图解)
- function Foo(){ }
- 4. 原型继承
- 构造函数的实例对象自动拥有构造函数原型对象的属性(方法)
- 利用的就是原型链
- 5. 原型属性问题
1. 读取对象的属性值时: 会自动到原型链中查找
2. 设置对象的属性值时: 不会查找原型链, 如果当前对象中没有此属性, 直接添加此属性并设置其值
3. 方法一般定义在原型中, 属性一般通过构造函数定义在对象本身上
- 探索instanceof
1. instanceof是如何判断的?
* 表达式: A instanceof B
* 如果B函数的显式原型对象在A对象的原型链上, 返回true, 否则返回false
2. Function是通过new自己产生的实例
- 案例1
- function Foo() { }
var f1 = new Foo();
console.log(f1 instanceof Foo);
console.log(f1 instanceof Object);
- 案例2
- console.log(Object instanceof Function);
console.log(Object instanceof Object);
console.log(Function instanceof Function);
console.log(Function instanceof Object);
function Foo() {}
console.log(Object instanceof Foo);
- 面试题
/*
测试题1
*/
var A = function() {
}
A.prototype.n = 1
var b = new A()
A.prototype = {
n: 2,
m: 3
}
var c = new A()
console.log(b.n, b.m, c.n, c.m)
/*
测试题2
*/
var F = function(){};
Object.prototype.a = function(){
console.log('a()')
};
Function.prototype.b = function(){
console.log('b()')
};
var f = new F();
f.a()
f.b()
F.a()
F.b()
### 执行上下文与执行上下文栈
- 变量提升与函数提升
1. 变量声明提升
* 通过var定义(声明)的变量, 在定义语句之前就可以访问到
* 值: undefined
2. 函数声明提升
* 通过function声明的函数, 在之前就可以直接调用
* 值: 函数定义(对象)
3. 问题: 变量提升和函数提升是如何产生的?
- 执行上下文
1. 代码分类(位置)
* 全局代码
* 函数代码
2. 全局执行上下文
* 在执行全局代码前将window确定为全局执行上下文
* 对全局数据进行预处理
* var定义的全局变量==>undefined, 添加为window的属性
* function声明的全局函数==>赋值(fun), 添加为window的方法
* this==>赋值(window)
* 开始执行全局代码
3. 函数执行上下文
* 在调用函数, 准备执行函数体之前, 创建对应的函数执行上下文对象
* 对局部数据进行预处理
* 形参变量==>赋值(实参)==>添加为执行上下文的属性
* arguments==>赋值(实参列表), 添加为执行上下文的属性
* var定义的局部变量==>undefined, 添加为执行上下文的属性
* function声明的函数 ==>赋值(fun), 添加为执行上下文的方法
* this==>赋值(调用函数的对象)
* 开始执行函数体代码
- 1. 代码分类(位置)
- 2. 全局执行上下文
- 3. 函数执行上下文
- 执行上下文栈
- 理解
1. 在全局代码执行前, JS引擎就会创建一个栈来存储管理所有的执行上下文对象
2. 在全局执行上下文(window)确定后, 将其添加到栈中(压栈)
3. 在函数执行上下文创建后, 将其添加到栈中(压栈)
4. 在当前函数执行完后,将栈顶的对象移除(出栈)
5. 当所有的代码执行完后, 栈中只剩下window
- 流程分析
var a = 10
var bar = function (x) {
var b = 5
foo(x + b)
}
var foo = function (y) {
var c = 5
console.log(a + c + y)
}
bar(10)
- 面试题
/*
测试题1:
*/
function a() {}
var a;
console.log(typeof a)
/*
测试题2:
*/
if (!(b in window)) {
var b = 1;
}
console.log(b)
/*
测试题3:
*/
var c = 1
function c(c) {
console.log(c)
var c = 3
}
c(2)
### 作用域与作用域链
- 作用域
1. 理解
* 就是一块"地盘", 一个代码段所在的区域
* 它是静态的(相对于上下文对象), 在编写代码时就确定了
2. 分类
* 全局作用域
* 函数作用域
* 没有块作用域(ES6有了)
3. 作用
* 隔离变量,不同作用域下同名变量不会有冲突
- 作用域与执行上下文
1. 区别1
* 全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了。而不是在函数调用时
* 全局执行上下文环境是在全局作用域确定之后, js代码马上执行之前创建
* 函数执行上下文环境是在调用函数时, 函数体代码执行之前创建
2. 区别2
* 作用域是静态的, 只要函数定义好了就一直存在, 且不会再变化
* 上下文环境是动态的, 调用函数时创建, 函数调用结束时上下文环境就会被释放
3. 联系
* 上下文环境(对象)是从属于所在的作用域
* 全局上下文环境==>全局作用域
* 函数上下文环境==>对应的函数使用域
- 作用域链
1. 理解
* 多个上下级关系的作用域形成的链, 它的方向是从下向上的(从内到外)
* 查找变量时就是沿着作用域链来查找的
2. 查找一个变量的查找规则
* 在当前作用域下的执行上下文中查找对应的属性, 如果有直接返回, 否则进入2
* 在上一级作用域的执行上下文中查找对应的属性, 如果有直接返回, 否则进入3
* 再次执行2的相同操作, 直到全局作用域, 如果还找不到就抛出找不到的异常
var a = 2;
function fn1() {
var b = 3;
function fn2() {
var c = 4;
console.log(c);
console.log(b);
console.log(a);
console.log(d);
}
fn2();
}
fn1();
- 面试题
- 面试题1
var x = 10;
function fn() {
console.log(x);
}
function show(f) {
var x = 20;
f();
}
show(fn);
- 面试题2
var fn = function () {
console.log(fn)
}
fn()
var obj = {
fn2: function () {
console.log(fn2)
}
}
obj.fn2()
### 闭包
- 引子实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript">
/*
需求: 点击某个按钮, 提示"点击的是第n个按钮"
*/
</script>
</head>
<body>
<button>测试1</button>
<button>测试2</button>
<button>测试3</button>
</body>
</html>
- 理解闭包
1. 如何产生闭包?
* 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时, 就产生了闭包
2. 闭包到底是什么?
* 使用chrome调试查看
* 理解一: 闭包是嵌套的内部函数(绝大部分人)
* 理解二: 包含被引用变量(函数)的对象(极少数人)
* 注意: 闭包存在于嵌套的内部函数中
3. 产生闭包的条件?
* 函数嵌套
* 内部函数引用了外部函数的数据(变量/函数)
- 常见的闭包
1. 将函数作为另一个函数的返回值
2. 将函数作为实参传递给另一个函数调用
- 闭包的作用
1. 使用函数内部的变量在函数执行完后, 仍然存活在内存中(延长了局部变量的生命周期)
2. 让函数外部可以操作(读写)到函数内部的数据(变量/函数)
问题:
1. 函数执行完后, 函数内部声明的局部变量是否还存在?
2. 在函数外部能直接访问函数内部的局部变量吗?
- 闭包的生命周期
1. 产生: 在嵌套内部函数定义执行完时就产生了(不是在调用)
2. 死亡: 在嵌套的内部函数成为垃圾对象时
<script type="text/javascript">
function fun1() {
//问题2: 此时闭包产生了吗?
var a = 3;
function fun2() {
a++;
console.log(a);
}
return fun2;
}
//问题1: 此时闭包产生了吗?
var f = fun1();
//问题3: 此时闭包释放了吗?
f();
f();
//问题4: 此时闭包释放回收了吗?
//问题5: 如何让闭包释放回收呢?
</script>
- 闭包的应用: 自定义JS模块
闭包的应用 : 定义JS模块
* 具有特定功能的js文件
* 将所有的数据和功能都封装在一个函数内部(私有的)
* 只向外暴露一个包信n个方法的对象或函数
* 模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能
- 闭包的缺点及解决
1. 缺点
* 函数执行完后, 函数内的局部变量没有释放, 占用内存时间会变长
* 容易造成内存泄露
2. 解决
* 能不用闭包就不用
* 及时释放
- 面试题
- 面试题一
//代码片段一
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()()); //?
//代码片段二
var name2 = "The Window";
var object2 = {
name2 : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name2;
};
}
};
alert(object2.getNameFunc()()); //?
- 面试题二
function fun(n,o) {
console.log(o)
return {
fun:function(m){
return fun(m,n);
}
};
}
var a = fun(0); a.fun(1); a.fun(2); a.fun(3);//undefined,?,?,?
var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,?
var c = fun(0).fun(1); c.fun(2); c.fun(3);//undefined,?,?,?
## 面向对象高级
### 对象创建模式
- Object构造函数模式
方式1: Object构造函数模式
* 套路: 先创建空Object对象, 再动态添加属性/方法
* 适用场景: 起始时不确定对象内部数据
* 问题: 语句太多
- 对象字面量模式
方式2: 对象字面量模式
* 套路: 使用{}创建对象, 同时指定属性/方法
* 适用场景: 起始时对象内部数据是确定的
* 问题: 如果创建多个对象, 有重复代码
- 工厂模式
方式3: 工厂模式
* 套路: 通过工厂函数动态创建对象并返回
* 适用场景: 需要创建多个对象
* 问题: 对象没有一个具体的类型, 都是Object类型
- 自定义构造函数模式
方式4: 自定义构造函数模式
* 套路: 自定义构造函数, 通过new创建对象
* 适用场景: 需要创建多个类型确定的对象
* 问题: 每个对象都有相同的数据, 浪费内存
- 构造函数+原型的组合模式
方式5: 构造函数+原型的组合模式
* 套路: 自定义构造函数, 属性在函数中初始化, 方法添加到原型上
* 适用场景: 需要创建多个类型确定的对象
### 继承模式
- 原型链继承
方式1: 原型链继承
1. 套路
1. 定义父类型构造函数
2. 给父类型的原型添加方法
3. 定义子类型的构造函数
4. 创建父类型的对象赋值给子类型的原型
5. 将子类型原型的构造属性设置为子类型
6. 给子类型原型添加方法
7. 创建子类型的对象: 可以调用父类型的方法
2. 关键
1. 子类型的原型为父类型的一个实例对象
- 借用构造函数继承
方式2: 借用构造函数继承(假的)
1. 套路:
1. 定义父类型构造函数
2. 定义子类型构造函数
3. 在子类型构造函数中调用父类型构造
2. 关键:
1. 在子类型构造函数中通用super()调用父类型构造函数
- 组合继承
方式3: 原型链+借用构造函数的组合继承
1. 利用原型链实现对父类型对象的方法继承
2. 利用super()借用父类型构建函数初始化相同属性
## 线程机制与事件机制
### 进程与线程
- 进程(process)
- 程序的一次执行, 它占有一片独有的内存空间
- 可以通过windows任务管理器查看进程
- 线程(thread)
- 是进程内的一个独立执行单元
- 是程序执行的一个完整流程
- 是CPU的最小的调度单元
- 图解
- 相关知识
- 应用程序必须运行在某个进程的某个线程上
- 一个进程中至少有一个运行的线程: 主线程, 进程启动后自动创建
- 一个进程中也可以同时运行多个线程, 我们会说程序是多线程运行的
- 一个进程内的数据可以供其中的多个线程直接共享
- 多个进程之间的数据是不能直接共享的
- 线程池(thread pool): 保存多个线程对象的容器, 实现线程对象的反复利用
- 相关问题
- 何为多进程与多线程?
- 多进程运行: 一应用程序可以同时启动多个实例运行
- 多线程: 在一个进程内, 同时有多个线程运行
- 比较单线程与多线程?
- 多线程
- 优点
- 能有效提升CPU的利用率
- 缺点
- 创建多线程开销
- 线程间切换开销
- 死锁与状态同步问题
- 单线程
- 优点
- 顺序编程简单易懂
- 缺点
- 效率低
- JS是单线程还是多线程?
- js是单线程运行的
- 但使用H5中的 Web Workers可以多线程运行
- 浏览器运行是单线程还是多线程?
- 都是多线程运行的
- 浏览器运行是单进程还是多进程?
- 有的是单进程
- firefox
- 老版IE
- 有的是多进程
- chrome
- 新版IE
- 如何查看浏览器是否是多进程运行的呢?
- 任务管理器-->进程
### 浏览器内核
- 支撑浏览器运行的最核心的程序
- 不同的浏览器可能不一样
- Chrome, Safari : webkit
- firefox : Gecko
- IE : Trident
- 360,搜狗等国内浏览器: Trident + webkit
- 内核由很多模块组成
- js引擎模块 : 负责js程序的编译与运行
- html,css文档解析模块 : 负责页面文本的解析
- DOM/CSS模块 : 负责dom/css在内存中的相关处理
- 布局和渲染模块 : 负责页面的布局和效果的绘制(内存中的对象)
- ......
- 定时器模块 : 负责定时器的管理
- DOM事件响应模块 : 负责事件的管理
- 网络请求模块 : 负责ajax请求
### 定时器引发的思考
1. 定时器真是定时执行的吗?
* 定时器并不能保证真正定时执行
* 一般会延迟一丁点(可以接受), 也有可能延迟很长时间(不能接受)
2. 定时器回调函数是在分线程执行的吗?
* 在主线程执行的, js是单线程的
3. 定时器是如何实现的?
* 事件循环模型(后面讲)
- 1. 定时器真是定时执行的吗?
- 2. 定时器回调函数是在哪个线程执行的?
- 3. 定时器是如何实现的?
### JS是单线程执行的
1. 如何证明js执行是单线程的?
* setTimeout()的回调函数是在主线程执行的
* 定时器回调函数只有在运行栈中的代码全部执行完后才有可能执行
2. 为什么js要用单线程模式, 而不用多线程模式?
* JavaScript的单线程,与它的用途有关。
* 作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。
* 这决定了它只能是单线程,否则会带来很复杂的同步问题
3. 代码的分类:
* 初始化代码
* 回调代码
4. js引擎执行代码的基本流程
* 先执行初始化代码: 包含一些特别的代码
* 设置定时器
* 绑定监听
* 发送ajax请求
* 后面在某个时刻才会执行回调代码
- 1. 如何证明js执行是单线程的?
- 2. 为什么js要用单线程模式, 而不用多线程模式?
- 3. 代码的分类
- 4. js引擎执行代码的基本流程
### 浏览器的事件循环(轮询)模型
1. 所有代码分类
* 初始化执行代码: 包含绑定dom事件监听, 设置定时器, 发送ajax请求的代码
* 回调执行代码: 处理回调逻辑
2. js引擎执行代码的基本流程:
* 初始化代码===>回调代码
3. 模型的2个重要组成部分:
* 事件管理模块
* 回调队列
4. 模型的运转流程
* 执行初始化代码, 将事件回调函数交给对应模块管理
* 当事件发生时, 管理模块会将回调函数及其数据添加到回调列队中
* 只有当初始化代码执行完后(可能要一定时间), 才会遍历读取回调队列中的回调函数执行
- 模型原理图
- 相关重要概念
1. 执行栈
execution stack
所有的代码都是在此空间中执行的
2. 浏览器内核
browser core
js引擎模块(在主线程处理)
其它模块(在主/分线程处理)
3. 任务队列(callback queue)
task queue
4. 消息队列(callback queue)
message queue
5. 事件队列(callback queue)
event queue
6. 事件轮询
event loop
从任务队列中循环取出回调函数放入执行栈中处理(一个接一个)
7. 事件驱动模型
event-driven interaction model
8. 请求响应模型
request-response model
- 1. 执行栈
- execution stack
- 所有的代码都是在此空间中执行的
- 2. 浏览器内核
- browser core
- js引擎模块(在主线程处理)
- 其它模块(在主/分线程处理)
- 3. 任务队列
- task queue
- 4. 消息队列
- message queue
- 5. 事件队列
- event queue
- 6. 事件轮询
- event loop
- 从任务队列中循环取出回调函数放入执行栈中处理(一个接一个)
- 7. 事件驱动模型
- event-driven interaction model
- 8. 请求响应模型
- request-response model
- 执行流程
### H5 Web Workers(多线程)
- 介绍
- Web Workers 是 HTML5 提供的一个javascript多线程解决方案
- 我们可以将一些大计算量的代码交由web Worker运行而不冻结用户界面
- 但是子线程完全受主线程控制,且不得操作DOM。
所以,这个新标准并没有改变JavaScript单线程的本质
- 使用
- 创建在分线程执行的js文件
var onmessage =function (event){ //不能用函数声明
console.log('onMessage()22');
var upper = event.data.toUpperCase();//通过event.data获得发送来的数据
postMessage( upper );//将获取到的数据发送会主线程
}
- 在主线程中的js中发消息并设置回调
//创建一个Worker对象并向它传递将在新线程中执行的脚本的URL
var worker = new Worker("worker.js");
//接收worker传过来的数据函数
worker.onmessage = function (event) {
console.log(event.data);
};
//向worker发送数据
worker.postMessage("hello world");
- 图解
- 应用练习
编程实现斐波那契数列(Fibonacci sequence)的计算
F(0)=0,F(1)=1,..... F(n)=F(n-1)+F(n-2)
- 直接在主线程
var fibonacci =function(n) {
return n <2 ? n : fibonacci(n -1) + fibonacci(n -2);
};
console.log(fibonacci(48));
- 使用Worker在分线程
- 主线程
var worker = new Worker('worker2.js');
worker.addEventListener('message', function (event) {
var timer2 = new Date().getTime();
console.log('结果:' + event.data, '时间:' + timer2, '用时:' + ( timer2 - timer ));
}, false);
var timer = new Date().getTime();
console.log('开始计算: ', '时间:' + timer);
setTimeout(function () {
console.log('定时器函数在计算数列时执行了', '时间:' + new Date().getTime());
}, 1000);
worker.postMessage(40);
console.log('我在计算数列的时候执行了', '时间:' + new Date().getTime());
- 分线程
var fibonacci =function(n) {
return n <2 ? n : fibonacci(n -1) + fibonacci(n -2);
};
var onmessage = function(event) {
var n = parseInt(event.data, 10);
postMessage(fibonacci(n));
};
- 不足
1. 慢
1. 不能跨域加载JS
2. worker内代码不能访问DOM(更新UI)
: ', '时间:' + timer);
setTimeout(function () {
console.log('定时器函数在计算数列时执行了', '时间:' + new Date().getTime());
}, 1000);
worker.postMessage(40);
console.log('我在计算数列的时候执行了', '时间:' + new Date().getTime());
- 分线程
var fibonacci =function(n) {
return n <2 ? n : fibonacci(n -1) + fibonacci(n -2);
};
var onmessage = function(event) {
var n = parseInt(event.data, 10);
postMessage(fibonacci(n));
};
- 不足
1. 慢
1. 不能跨域加载JS
2. worker内代码不能访问DOM(更新UI)
3. 不是每个浏览器都支持这个新特性