js ---- 高级

基础总结深入


数据类型的分类和判断

①分类
  • 基本(值)类型
    • Number ----- 任意数值 -------- typeof
    • String ----- 任意字符串 ------ typeof
    • Boolean ---- true/false ----- typeof
    • undefined — undefined ----- typeof/===
    • null -------- null ---------- ===
  • 对象(引用)类型
    • Object ----- typeof/instanceof
    • Array ------ instanceof
    • Function ---- typeof
②判断
  • typeof:
    • 可以区别: 数值, 字符串, 布尔值, undefined, function
    • 不能区别: null与对象, 一般对象与数组
  • instanceof
    • 专门用来判断对象数据的类型: Object, Array与Function
  • ===
    • 可以判断: undefined和null
③常见问题
  1. undefined与null的区别?
    • undefined代表没有赋值
    • null代表赋值了, 只是值为null
  1. 什么时候给变量赋值为null呢?
    • var a = null //a将指向一个对象, 但对象此时还没有确定
    • a = null //让a指向的对象成为垃圾对象
  1. 严格区别变量类型与数据类型?
    • js的变量本身是没有类型的, 变量的类型实际上是变量内存中数据的类型
    • 变量类型:
      * 基本类型: 保存基本类型数据的变量
      * 引用类型: 保存对象地址值的变量
    • 数据对象
      * 基本类型
      * 对象类型

对象

  • 如何实现深拷贝

函数

var obj = {}
function test() {
	this.xxx = "xxxxxx"
}
//obj.test()
test.call(obj) //可以让一个函数成为指定任意对象的方法进行调用

回调函数

  • 常见的回调函数?
    DOM事件函数
    定时器函数
    ajax回调函数(后面学)
    生命周期回调函数(后面学)
  //1. DOM事件函数
  var btn = document.getElementById('btn')
  btn.onclick = function () {
    alert(this.innerHTML)
  }

  //2. 定时器函数
  setInterval(function () {
    alert('到点啦!')
  }, 2000)

匿名函数自调用

  • 自调用函数不会污染全局变量
  • 不用取变量名,立即执行函数
(function () {
	var a = 3
	console.log(a + 3)
})()
var a = 4
console.log(a)
;(function() {
	var a = 1
	function test() {
		console.log(++a);
	}
	window.$ = function () { //向外暴露一个区局函数
		return {
			test: test
		} 
	}
})()

$().test()

this 是谁

如何确定this的值

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

关于分号

  • 第一行前加分号:为了合并不出错误
  • () 前加分号
  • [] 前加分号

函数高级

原型

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

显式与隐式原型

  1. 每个函数function都有一个prototype,即显式原型
  2. 每个实例对象都有一个__proto__,可称为隐式原型
  3. 对象的隐式原型的值为其对应构造函数的显式原型的值
  4. 总结:
    函数的prototype属性: 在定义函数时自动添加的, 默认值是一个空Object对象
    对象的__proto__属性: 创建对象时自动添加的, 默认值为构造函数的prototype属性值
    程序员能直接操作显式原型, 但不能直接操作隐式原型(ES6之前)
    在这里插入图片描述

原型链

  1. 原型链(图解)
    • 访问一个对象的属性时,
      • 先在自身属性中查找,找到返回
      • 如果没有, 再沿着__proto__这条链向上查找, 找到返回
      • 如果最终没找到, 返回undefined
    • 别名: 隐式原型链
    • 作用: 查找对象的属性(方法)

在这里插入图片描述

  1. 构造函数/原型/实体对象的关系(图解)
    var o1 = new Object();
    var o2 = {};
    在这里插入图片描述

  2. 构造函数/原型/实体对象的关系2(图解)
    function Foo(){ }
    var Foo = new Function()
    Function = new Function()
    所有函数的__proto__都是一样的
    在这里插入图片描述

  3. 函数的显示原型指向的对象默认是空Object实例对象(但Object不满足)

  4. 所有函数都是Function的实例(包含Function)

  5. Object的原型对象是原型链尽头

  /*
  1. 函数的显示原型指向的对象默认是空Object实例对象(但Object不满足)
   */
  console.log(Fn.prototype instanceof Object) // true
  console.log(Object.prototype instanceof Object) // false
  console.log(Function.prototype instanceof Object) // true
  /*
  2. 所有函数都是Function的实例(包含Function)
  */
  console.log(Function.__proto__===Function.prototype)
    /*
  3. Object的原型对象是原型链尽头
   */
  console.log(Object.prototype.__proto__) // null

原型属性

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

  }
  Fn.prototype.a = 'xxx'
  var fn1 = new Fn()
  console.log(fn1.a, fn1)

  var fn2 = new Fn()
  fn2.a = 'yyy'
  console.log(fn1.a, fn2.a, fn2)

  function Person(name, age) {
    this.name = name
    this.age = age
  }
  Person.prototype.setName = function (name) {
    this.name = name
  }
  var p1 = new Person('Tom', 12)
  p1.setName('Bob')
  console.log(p1)

  var p2 = new Person('Jack', 12)
  p2.setName('Cat')
  console.log(p2)
  console.log(p1.__proto__===p2.__proto__) // true

在这里插入图片描述

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) // true
  console.log(f1 instanceof Object) // true

在这里插入图片描述

  /*
  案例2
   */
  console.log(Object instanceof Function) // true
  console.log(Object instanceof Object) // true
  console.log(Function instanceof Function) // true
  console.log(Function instanceof Object) // true

  function Foo() {}
  console.log(Object instanceof  Foo) // false

在这里插入图片描述

测试题

  /*
  测试题1
   */
  function A () {

  }
  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)
// 1,undefined,2,3
  /*
   测试题2
   */
  function F (){}
  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()
  console.log(f)
  console.log(Object.prototype)
  console.log(Function.prototype)

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

变量声明提升,函数声明提升

  /*
  面试题 : 输出 undefined
   */
  var a = 3
  function fn () {
    console.log(a)
    var a = 4
  }
  fn()
  console.log(b) //undefined  变量提升
  fn2() //可调用  函数提升
  // fn3() //不能  变量提升

  var b = 3
  function fn2() {
    console.log('fn2()')
  }
  var fn3 = function () {
    console.log('fn3()')
  }

执行上下文

1. 代码分类(位置)
  * 全局代码
  * 函数(局部)代码

2. 全局执行上下文
  * 在执行全局代码前将window确定为全局执行上下文
  * 对全局数据进行预处理
    * var定义的全局变量==>undefined, 添加为window的属性
    * function声明的全局函数==>赋值(fun), 添加为window的方法
    * this==>赋值(window)

  * 开始执行全局代码
  console.log(a1, window.a1)
  window.a2()
  console.log(this)

  var a1 = 3
  function a2() {
    console.log('a2()')
  }
  console.log(a1)
3. 函数执行上下文
  * 在调用函数, 准备执行函数体之前, 创建对应的函数执行上下文对象(虚拟的, 存在于栈中)
  * 对局部数据进行预处理
    * 形参变量==>赋值(实参)==>添加为执行上下文的属性
    * arguments==>赋值(实参列表), 添加为执行上下文的属性
    * var定义的局部变量==>undefined, 添加为执行上下文的属性
    * function声明的函数 ==>赋值(fun), 添加为执行上下文的方法
    * this==>赋值(调用函数的对象)
  * 开始执行函数体代码
  function fn(x, y) {
    console.log(x, y)
    console.log(b1)
    console.log(b2)
    console.log(arguments)
    console.log(this)

    // console.log(b3)

    var b1 = 5
    function b2 () {

    }
    b3 = 6
  }
  fn()

执行上下文栈

  1. 在全局代码执行前, JS引擎就会创建一个栈来存储管理所有的执行上下文对象

  2. 在全局执行上下文(window)确定后, 将其添加到栈中(压栈)

  3. 在函数执行上下文创建后, 将其添加到栈中(压栈)

  4. 在当前函数执行完后,将栈顶的对象移除(出栈)

  5. 当所有的代码执行完后, 栈中只剩下window

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

<!--
1. 依次输出什么?
2. 整个过程中产生了几个执行上下文?
-->
<script type="text/javascript">
  console.log('global begin: '+ i)
  var i = 1
  foo(1);
  function foo(i) {
    if (i == 4) {
      return;
    }
    console.log('foo() begin:' + i);
    foo(i + 1);
    console.log('foo() end:' + i);
  }
  console.log('global end: ' + i)
</script>


global begin: undefined
foo() begin:1
foo() begin:2
foo() begin:3
foo() end:3
foo() end:2
foo() end:1
global end: 1
  /*
  测试题1: 先预处理变量, 后预处理函数
  */
  function a() {}
  var a;
  console.log(typeof a) //function

  /*
  测试题2: 变量预处理, in操作符
   */
  if (!(b in window)) {
    var b = 1;
  }
  console.log(b) //undefined

  /*
  测试题3: 预处理, 顺序执行
   */
  var c = 1
  function c(c) {
    console.log(c)
    var c = 3
  }
  c(2) //报错 c is not a function

作用域与作用域链

作用域

1. 理解

  * 就是一块"地盘", 一个代码段所在的区域

  * 它是静态的(相对于上下文对象), 在编写代码时就确定了

2. 分类

  * 全局作用域

  * 函数作用域

  * 没有块作用域(ES6有了)

3. 作用

  * 隔离变量,不同作用域下同名变量不会有冲突

在这里插入图片描述

作用域与执行上下文

1. 区别1

  * 全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了。而不是在函数调用时

  * 全局执行上下文环境是在全局作用域确定之后, js代码马上执行之前创建

  * 函数执行上下文环境是在调用函数时, 函数体代码执行之前创建

2. 区别2

  * 作用域是静态的, 只要函数定义好了就一直存在, 且不会再变化

  * 上下文环境是动态的, 调用函数时创建, 函数调用结束时上下文环境就会被释放

3. 联系

  * 上下文环境(对象)是从属于所在的作用域

  * 全局上下文环境==>全局作用域

  * 函数上下文环境==>对应的函数使用域

在这里插入图片描述

作用域链

1. 理解

  * 多个上下级关系的作用域形成的链, 它的方向是从下向上的(从内到外)

  * 查找变量时就是沿着作用域链来查找的

2. 查找一个变量的查找规则

  * 在当前作用域下的执行上下文中查找对应的属性, 如果有直接返回, 否则进入2

  * 在上一级作用域的执行上下文中查找对应的属性, 如果有直接返回, 否则进入3

  * 再次执行2的相同操作, 直到全局作用域, 如果还找不到就抛出找不到的异常

在这里插入图片描述

测试

  var x = 10;
  function fn() {
    console.log(x);
  }

  function show(f) {
    var x = 20;
    f();
  }

  show(fn); // 10
  var fn = function () {
    console.log(fn)
  }
  fn()//fn函数
  
  var obj = {
    fn2: function () {
      console.log(fn2)//报错,fn2不是函数
      console.log(this.fn2)//fn2函数
    }
  }
  obj.fn2()

闭包

循环添加事件监听(引入闭包)

<button>测试1</button>
<button>测试2</button>
<button>测试3</button>

<!--
需求: 点击某个按钮, 提示"点击的是第n个按钮"
-->
<script type="text/javascript">
  var btns = document.getElementsByTagName('button')
  /* //有问题
  for(var i=0,length=btns.length;i<length;i++) {
    var btn = btns[i]
    btn.onclick = function () {
      alert('第'+(i+1)+'个')
    }
  }*/

  //解决一: 保存下标
  /*for(var i=0,length=btns.length;i<length;i++) {
    var btn = btns[i]
    btn.index = i
    btn.onclick = function () {
      alert('第'+(this.index+1)+'个')
    }
  }*/

  //解决办法: 利用闭包
  for(var i=0,length=btns.length;i<length;i++) {
    (function (i) {
      var btn = btns[i]
      btn.onclick = function () {
        alert('第'+(i+1)+'个')
      }
    })(i)
  }
</script>

闭包的理解

1. 如何产生闭包?
  * 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时, 就产生了闭包

2. 闭包到底是什么?
  * 使用chrome调试查看
  * 理解一: 闭包是嵌套的内部函数(绝大部分人)
  * 理解二: 包含被引用变量(函数)的对象(极少数人)
  * 注意: 闭包存在于嵌套的内部函数中

3. 产生闭包的条件?
  * 函数嵌套
  * 内部函数引用了外部函数的数据(变量/函数)
  function fn1 () {
    var a = 3
    function fn2 () { //执行函数定义就会产生闭包(不用调用内部函数)
      console.log(a)
    }
  }
  fn1()

在这里插入图片描述

常见的闭包

  // 1. 将函数作为另一个函数的返回值
  function fn1() {
    var a = 2

    function fn2() {
      a++
      console.log(a)
    }

    return fn2
  }
  var f = fn1()
  f() // 3
  f() // 4
  // 2. 将函数作为实参传递给另一个函数调用
  function showMsgDelay(msg, time) {
    setTimeout(function () {
      console.log(msg)
    }, time)
  }
  showMsgDelay('hello', 1000)

闭包的作用一:延长局部变量生命周期

  1. 使用函数内部的变量在函数执行完后, 仍然存活在内存中(延长了局部变量的生命周期)

  2. 让函数外部可以操作(读写)到函数内部的数据(变量/函数)

问题:

  1. 函数执行完后, 函数内部声明的局部变量是否还存在?

  2. 在函数外部能直接访问函数内部的局部变量吗?

function fun1() {
  var a = 3;

  function fun2() {
    a++;            //引用外部函数的变量--->产生闭包
    console.log(a);
  }

  return fun2;
}
var f = fun1();  //由于f引用着内部的函数-->内部函数以及闭包都没有成为垃圾对象

f();   //间接操作了函数内部的局部变量
f();

闭包的生命周期

  1. 产生: 在嵌套内部函数定义执行完时就产生了(不是在调用)
  2. 死亡: 在嵌套的内部函数成为垃圾对象时
  function fun1() {
    //此处闭包已经产生
    var a = 3;

    function fun2() {
      a++;
      console.log(a);
    }

    return fun2;
  }
  var f = fun1();

  f();
  f();
  f = null //此时闭包对象死亡

闭包作用二:闭包自定义js模块

闭包的应用2 : 定义JS模块
  * 具有特定功能的js文件
  * 将所有的数据和功能都封装在一个函数内部(私有的)
  * 只向外暴露一个包信n个方法的对象或函数
  * 模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能
/**
 * 自定义模块1
 */
function coolModule() {
  //私有的数据
  var msg = 'atguigu'
  var names = ['I', 'Love', 'you']

  //私有的操作数据的函数
  function doSomething() {
    console.log(msg.toUpperCase())
  }
  function doOtherthing() {
    console.log(names.join(' '))
  }

  //向外暴露包含多个方法的对象
  return {
    doSomething: doSomething,
    doOtherthing: doOtherthing
  }
}


  var module = coolModule()
  module.doSomething()
  module.doOtherthing()

推荐使用方式二: 可以直接使用window对象来调用模块中的方法

/**
 * 自定义模块2
 */
(function (window) {
  //私有的数据
  var msg = 'atguigu'
  var names = ['I', 'Love', 'you']
  //操作数据的函数
  function a() {
    console.log(msg.toUpperCase())
  }
  function b() {
    console.log(names.join(' '))
  }

  window.coolModule2 =  { //这里的window在代码压缩时会被替代掉,否则不会
    doSomething: a,
    doOtherthing: b
  }
})(window)//这样写的好处在于有利于代码压缩


  coolModule2.doSomething()
  coolModule2.doOtherthing()

闭包的缺点

1. 缺点
  * 函数执行完后, 函数内的局部变量没有释放, 占用内存时间会变长
  * 容易造成内存泄露

2. 解决
  * 能不用闭包就不用
  * 及时释放
  function fn1() {
    var a = 2;

    function fn2() {
      a++;
      console.log(a);
    }

    return fn2;
  }
  var f = fn1();

  f(); // 3
  f(); // 4

  f = null // 释放
1. 内存溢出
  * 一种程序运行出现的错误
  * 当程序运行需要的内存超过了剩余的内存时, 就出抛出内存溢出的错误

2. 内存泄露
  * 占用的内存没有及时释放
  * 内存泄露积累多了就容易导致内存溢出
  * 常见的内存泄露:
    * 意外的全局变量
    * 没有及时清理的计时器或回调函数
    * 闭包
  // 1. 内存溢出
  /*var obj = {}
  for (var i = 0; i < 100000; i++) {
    obj[i] = new Array(10000000)
  }
  console.log('------')*/

  // 2. 内存泄露
    // 意外的全局变量
  function fn () {
    a = [] //不小心没有var定义
  }
  fn()
    // 没有及时清理的计时器
  setInterval(function  () {
    console.log('----')
  }, 1000)

面试题

  //代码片段一
  var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
      return function(){
        return this.name;
      };
    }
  };
  alert(object.getNameFunc()());  //?  the window


  //代码片段二: 有闭包
  var name2 = "The Window";
  var object2 = {
    name2 : "My Object",
    getNameFunc : function(){
      var that = this;
      return function(){
        return that.name2;
      };
    }
  };
  alert(object2.getNameFunc()()); //?  my object


//面试题3
  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,0,0,0

  var b = fun(0).fun(1).fun(2).fun(3)//undefined,0,1,2

  var c = fun(0).fun(1)
  c.fun(2)
  c.fun(3)//undefined,0,1,1

面向对象高级

对象创建模式

Object构造函数模式

方式一: Object构造函数模式
  * 套路: 先创建空Object对象, 再动态添加属性/方法
  * 适用场景: 起始时不确定对象内部数据
  * 问题: 语句太多
  /*
  一个人: name:"Tom", age: 12
   */
  var p = new Object()
  p = {}
  p.name = 'Tom'
  p.age = 12
  p.setName = function (name) {
    this.name = name
  }
  p.setaAge = function (age) {
    this.age = age
  }

  console.log(p)

对象字面量模式

方式二: 对象字面量模式
  * 套路: 使用{}创建对象, 同时指定属性/方法
  * 适用场景: 起始时对象内部数据是确定的
  * 问题: 如果创建多个对象, 有重复代码
  var p = {
    name: 'Tom',
    age: 23,
    setName: function (name) {
      this.name = name
    }
  }
  console.log(p.name, p.age)
  p.setName('JACK')
  console.log(p.name, p.age)

  var p2 = {
    name: 'BOB',
    age: 24,
    setName: function (name) {
      this.name = name
    }
  }

工厂模式

方式三: 工厂模式
  * 套路: 通过工厂函数动态创建对象并返回
  * 适用场景: 需要创建多个对象
  * 问题: 对象没有一个具体的类型, 都是Object类型
  // 工厂函数: 返回一个需要的数据的函数
  function createPerson(name, age) {
    var p = {
      name: name,
      age: age,
      setName: function (name) {
        this.name = name
      }
    }
    return p
  }

  var p1 = createPerson('Tom', 12)
  var p2 = createPerson('JAck', 13)
  console.log(p1)

自定义构造函数模式

方式四: 自定义构造函数模式
  * 套路: 自定义构造函数, 通过new创建对象
  * 适用场景: 需要创建多个类型确定的对象
  * 问题: 每个对象都有相同的数据, 浪费内存
  function Person(name, age) {
    this.name = name
    this.age = age
    this.setName = function (name) {
      this.name = name
    }
  }

  var p1 = new Person('Tom', 12)
  var p2 = new Person('Tom2', 13)
  console.log(p1, p1 instanceof Person)

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

方式五: 构造函数+原型的组合模式
  * 套路: 自定义构造函数, 属性在函数中初始化, 方法添加到原型上
  * 适用场景: 需要创建多个类型确定的对象
  function Person (name, age) {
    this.name = name
    this.age = age
  }
  Person.prototype.setName = function (name) {
    this.name = name
  }
  var p1 = new Person('Tom', 12)
  var p2 = new Person('JAck', 23)
  p1.setName('TOM3')
  console.log(p1)

  Person.prototype.setAge = function (age) {
    this.age = age
  }
  p1.setAge(23)
  console.log(p1.age)

  Person.prototype = {}
  p1.setAge(34)
  console.log(p1)
  var p3 = new Person('BOB', 12)
  p3.setAge(12)

继承模式

原型链继承

方式1: 原型链继承
  1. 套路
    1. 定义父类型构造函数
    2. 给父类型的原型添加方法
    3. 定义子类型的构造函数
    4. 创建父类型的对象赋值给子类型的原型
    5. 将子类型原型的构造属性设置为子类型
    6. 给子类型原型添加方法
    7. 创建子类型的对象: 可以调用父类型的方法

  2. 关键
    1. 子类型的原型为父类型的一个实例对象
  function Supper() { //父类型
    this.superProp = 'The super prop'
  }
  //原型的数据所有的实例对象都可见
  Supper.prototype.showSupperProp = function () {
    console.log(this.superProp)
  }

  function Sub() { //子类型
    this.subProp = 'The sub prop'
  }

  // 子类的原型为父类的实例
  Sub.prototype = new Supper()
  // 修正Sub.prototype.constructor为Sub本身
  Sub.prototype.constructor = Sub

  Sub.prototype.showSubProp = function () {
    console.log(this.subProp)
  }

  // 创建子类型的实例
  var sub = new Sub()
  // 调用父类型的方法
  sub.showSubProp()
  // 调用子类型的方法
  sub.showSupperProp()	

借用构造函数继承(假继承)

方式2: 借用构造函数继承(假的)
1. 套路:
  1. 定义父类型构造函数
  2. 定义子类型构造函数
  3. 在子类型构造函数中调用父类型构造
2. 关键:
  1. 在子类型构造函数中通用super()调用父类型构造函数
  function Person(name, age) {
    this.name = name
    this.age = age
  }

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

  var s = new Student('Tom', 20, 12000)
  console.log(s.name, s.age, s.price)

组合继承(原型 + 构造)

方式3: 原型链+借用构造函数的组合继承
1. 利用原型链实现对父类型对象的方法继承
2. 利用call()借用父类型构建函数初始化相同属性
  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
  Student.prototype.setPrice = function (price) {
    this.price = price
  }

  var s = new Student('Tom', 12, 10000)
  s.setPrice(11000)
  s.setName('Bob')
  console.log(s)
  console.log(s.constructor)

线程机制与事件机制

浏览器内核

1. 什么是浏览器内核?
  * 支持浏览器运行的最核心的程序
2. 不同的浏览器可能不太一样
  * Chrome, Safari: webkit
  * firefox: Gecko
  * IE: Trident
  * 360,搜狗等国内浏览器: Trident + webkit
3. 内核由很多模块组成
  * html,css文档解析模块 : 负责页面文本的解析
  * dom/css模块 : 负责dom/css在内存中的相关处理
  * 布局和渲染模块 : 负责页面的布局和效果的绘制
  * 布局和渲染模块 : 负责页面的布局和效果的绘制

  * 定时器模块 : 负责定时器的管理
  * 网络请求模块 : 负责服务器请求(常规/Ajax)
  * 事件响应模块 : 负责事件的管理

事件循环模型

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

悠闲的线程池

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值