JS高级

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. 不是每个浏览器都支持这个新特性


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值