js高级教程

数据类型

1.分类:基本类型(保存基本类型数据的变量)和引用类型(保存对象地址值的变量)

  • 基本类型:Number: 任意数值
    String: 任意文本
    Boolean: true/false
    undefined: undefined
    null: null
  • 引用类型:object、Array、Function

2.判断

  • typeof(typeof xxx)
    可以区别: 数值, 字符串, 布尔值, undefined, function
    不能区别: null与对象, 一般对象与数组
  • instanceof(xxx instanceof Object / Array / Function)
    专门用来判断对象数据的类型: Object, Array与Function

数据、变量、内存

1.内存包含2个数据:
内部存储的数据(一般数据/地址数据)
内存地址值数据
2. 内存分类:
栈: 全局变量, 局部变量 (空间较小)
堆: 对象 (空间较大)
只有当定义一个对象时才会保存地址值,其他都是直接保存数据
3.内存,数据, 变量三者之间的关系
内存是一个容器, 用来存储程序运行需要操作的数据
变量是内存的标识, 我们通过变量找到对应的内存, 进而操作(读/写)内存中的数据
例如
在这里插入图片描述

	  var obj = { name: "tom" };
      var a = obj;
      a = "jerry";
      console.log(a, obj);

此时a的保存内容不是地址值,更改成了jerry,而obj不作改变。
在这里插入图片描述

a.name="jerry"

输出:a里面保存的是地址值,根据这个地址值去寻找,找到name:“Tom”,且将name的值修改了,此时obj也会变化
在这里插入图片描述

关于引用变量赋值问题

  • 2个引用变量指向同一个对象, 通过一个引用变量修改对象内部数据, 另一个引用变量也看得见
  • 2个引用变量指向同一个对象,让一个引用变量指向另一个对象, 另一个引用变量还是指向原来的对象
	  var obj1 = { name: "tom" };
      var obj2 = obj1;
      obj1.name = "jack";
      console.log(obj2.name); //jack
      //1. 2个引用变量指向同一个对象(保存同一个地址值), 通过一个引用变量修改对象内部数据, 另一个引用变量也看得见
      var obj1 = {};
      var obj2 = obj1;
      obj2.name = "Tom";
      console.log(obj1.name);//Tom
      function f1(obj) {
        obj.age = 12;
      }
      f1(obj2);
      console.log(obj1.age);//12

      //2. 2个引用变量指向同一个对象,让一个引用变量指向另一个对象, 另一个引用变量还是指向原来的对象
      var obj3 = { name: "Tom" };
      var obj4 = obj3;
      obj3 = { name: "JACK" }; //kack
      console.log(obj4.name);//tom
      function f2(obj) {
        obj = { name: "Bob" };//垃圾回收
        //例如:obj=obj4,obj={ name: "Bob" },obj不再指向obj4的地址值,所以不会改变obj4.name的值
      }
      f2(obj4);
      console.log(obj4.name);//tom

JS引擎如何管理内存

问题: JS引擎如何管理内存?

  1. 内存生命周期
    1). 分配小内存空间,得到它的使用权
    2). 存储数据,可以反复进行操作
    3). 释放小内存空间
  2. 释放内存
  • 为执行函数分配的栈空间内存(局部变量): 函数执行完自动释放
  • 存储对象的堆空间内存(对象): 当内存没有引用指向时, 对象成为垃圾对象, 垃圾回收器后面就会回收释放此内存
      var a = 3;
      var obj = {}; //这两个占用三个空间,a = 3、obj、{}
      obj = null; // 此时占用两个空间,a = 3、obj,{}已经释放

      function fn() {
        var b = {};
      }
      fn(); // 局部变量是函数执行时创建,执行完毕后函数内部所有的局部变量自动释放。b是自动释放,b所指向的对象是在后面的某个时刻由垃圾回收器回收

对象(属性名不确定、有特殊符号)

/*情形一: 属性名不是合法的标识名*/
  /*需求: 添加一个属性: content-type: text/json */
  //  p.content-type = 'text/json' //不正确
  p['content-type'] = 'text/json'

/*情形二: 属性名不确定*/
  var prop = 'xxx'
  var value = 123
  // p.prop = value  //不正确
  p[prop] = value
  console.log(p['content-type'], p[prop])

IIEF(立即执行函数、匿名函数自调用)

作用

  • 隐藏内部实现
  • 不污染外部命名空间
  • 用它来编写js模块
 (function (i) {
        var a = 4;
        function fn() {
          console.log("fn ", i + a);
        }
        fn();
      })(3);

this

对象不构成单独的作用域
1.如果jumps是箭头函数,因为对象不构成单独的作用域,导致jumps箭头函数定义时的作用域就是全局作用域。
如果jumps是普通函数,该方法内部的this指向cat

const cat = {
  lives: 9,
  jumps: () => {
    this.lives--;//报错,this不指向cat,指向全局
  }
  jumps: function(){
    this.lives--;//不报错,this指向cat,普通函数的this,谁调用就指向谁
  }
}
  1. this是什么?
  • 一个关键字, 一个内置的引用变量
  • 在函数中都可以直接使用this
  • this代表调用函数的当前对象
  • 在定义函数时, this还没有确定, 只有在执行时才动态确定(绑定)的
  1. 如何确定this的值?
  • test()
  • obj.test()
  • new test()
  • test.call(obj)
    前置知识:
  • 本质上任何函数在执行时都是通过某个对象调用的
    function Person(color) {
        console.log(this); //windows
        this.color = color;
        this.getColor = function () {
          console.log(this); //windows
          return this.color;
        };
        this.setColor = function (color) {
          console.log(this); //windows
          this.color = color;
        };
      }
      Person("red"); //this是谁? windows
      var p = new Person("yello"); //this是谁? p
      p.getColor(); //this是谁? p
      var obj = {};
      p.setColor.call(obj, "black"); //this是谁? obj
      var test = p.setColor;
      test(); //this是谁? windows
      function fun1() {
        console.log(this); //windows
        function fun2() {
          console.log(this);
        }
        fun2(); //this是谁?windows
      }
      fun1(); //windows;

是否加分号问题

可加可不加。
在下面2种情况下不加分号会有问题

  • 小括号开头的前一条语句
  • 中方括号开头的前一条语句
  • 解决办法: 在行首加分号
// 情形一: 小括号开头的前一条语句
  var a = 3
  ;(function () {

  })
  /*
  错误理解: 将3看成是函数调用
   var a = 3(function () {

   })
   */

  // 情形二: 中方括号开头的前一条语句
  var b = a
  ;[1, 3, 5].forEach(function (item) {
    console.log(item)
  })
  /*
   错误理解:
   a = b[5].forEach(function(e){
   console.log(e)
   })

函数原型与原型链

1.函数的prototype属性:

  • 每个函数都有一个prototype属性,它默认指向一个object空对象(即称为:原型对象)
  • 原型对象上有一个属性constructor,它指向函数对象
    2.给原型对象添加属性(一般都是方法)
  • 作用:函数的所有实例对象自动拥有原型中的属性和方法
    原型上面的方法是给实例对象用的
    在这里插入图片描述

例题:
在这里插入图片描述

显示与隐式原型

  1. 每个函数function都有一个prototype,即显式原型(属性)
  2. 每个实例对象都有一个__proto__,可称为隐式原型
  3. 对象的隐式原型的值为其对应构造函数的显式原型的值(fn.proto === Fn.prototype)
  4. 内存结构(图)
  5. 总结:
  • 函数的prototype属性: 在定义函数时自动添加的, 默认值是一个空Object对象
  • 对象的__proto__属性: 创建对象时自动添加的, 默认值为构造函数的prototype属性值
  • 程序员能直接操作显式原型, 但不能直接操作隐式原型(ES6之前)
 function Fn() {
        //创建时:内部执行一个语句:this.prototype={}
      }
      var fn = new Fn(); //内部语句:this.__proto__=Fn.prototype
      console.log(fn);
      var fn = new Fn();
      console.log(Fn.prototype === fn.__proto__); //true

      Fn.prototype.test = function () {
        console.log("test()");
      };
      //通过实例对象调用原型上的方法
      fn.test();

在这里插入图片描述

原型链

1.函数的显式原型指向的对象默认式空Object对象(object除外,因为它的__proto__为nulll)
2.所有函数都是Function的实例(包括Function)
3.object的原型对象是原型链的尽头
在这里插入图片描述

var O1=new Object();
var o2={}

在这里插入图片描述

function Foo(){  }

在这里插入图片描述

原型链属性

  1. 读取对象的属性值时: 会自动到原型链中查找
  2. 设置对象的属性值时: 不会查找原型链, 如果当前对象中没有此属性, 直接添加此属性并设置其值
  3. 方法一般定义在原型中, 属性一般通过构造函数定义在对象本身上
 function Fn() {}
      Fn.prototype.a = "xxx";
      var fn1 = new Fn();
      var fn2 = new Fn();
      fn2.a = "ccc"; //设置属性值不会去查找原型链
      console.log(fn1.a, fn2.a, fn1, fn2);

在这里插入图片描述

function Fn() {}
      Fn.prototype.a = "xxx";
      var fn1 = new Fn();
      var fn2 = new Fn();
      fn2.__proto__.a = "yyy"; //操作隐式原型上的属性
      console.log(fn1.a, fn2.a, fn1, fn2);

在这里插入图片描述

instanceof

在这里插入图片描述

在这里插入图片描述

变量(函数)提升

  1. 变量声明提升
  • 通过var定义(声明)的变量, 在定义语句之前就可以访问到
  • 值: undefined
  1. 函数声明提升
  • 通过function声明的函数, 在之前就可以直接调用
  • 值: 函数定义(对象)
     var a = 4;
     function fn() {
     console.log(a); //输出undefined,变量提升之前没有赋值
        var a = 5;
      }
      fn();

      /*变量提升*/
      console.log(a1); //可以访问, 但值是undefined
      /*函数提升*/
      a2(); // 可以直接调用

      var a1 = 3;
      function a2() {
        console.log("a2()");
      }

实行上下文

  1. 代码分类(位置)
  • 全局代码
  • 函数代码
  1. 全局执行上下文(执行前需要做准备工作)
  • 在执行全局代码前将window确定为全局执行上下文
  • 对全局数据进行预处理
    • var定义的全局变量==>undefined, 添加为window的属性
    • function声明的全局函数==>赋值(fun), 添加为window的方法
    • this==>赋值(window)
  • 开始执行全局代码
  1. 函数执行上下文
  • 在调用函数, 准备执行函数体之前, 创建对应的函数执行上下文对象(虚拟的,存在于栈中)
  • 对局部数据进行预处理
    • 形参变量==>赋值(实参)==>添加为执行上下文的属性
    • arguments==>赋值(实参列表), 添加为执行上下文的属性
    • var定义的局部变量==>undefined, 添加为执行上下文的属性
    • function声明的函数 ==>赋值(fun), 添加为执行上下文的方法
    • this==>赋值(调用函数的对象)
  • 开始执行函数体代码
      /*
  测试题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 a = 3;
      }
      c(2); //报错

作用域

函数的作用域是在定义时就确定好的
由于fn函数是在全局中定义的,此时x也是在本身找,找不到去全局,所以为10而不是20

      /*
   问题: 结果输出多少?
   */
      var x = 10;
      function fn() {
        console.log(x);//输出10
      }
      function show(f) {
        var x = 20;
        f();
      }
      show(fn);
  /*
   说说它们的输出情况
   */

  var fn = function () {
    console.log(fn)
  }
  fn()

  var obj = {
    fn2: function () {
      console.log(fn2)//报错,如果是this.fn2可以找到这个fn2函数
    }
  }
  obj.fn2()

闭包

引入:for循环例题
由于var定义的函数没有块级作用域,而且setTimeout是一个回调函数,在某个时刻才会发送,for循环完毕之后i=3,所以输出的是三个3

  for (var i = 0; i < 3; i++) {
        setTimeout(() => {
          console.log(i); //输出三个i
        }, 100);
      }

如果改成let定义i,可以输出012
因为let有块级作用域,所以i的值不会受外面的i影响

  for (let i = 0; i < 3; i++) {
        setTimeout(() => {
          console.log(i); //输出0 1 2
        }, 100);
      }

改成闭包:

 for (let i = 0; i < 3; i++) {
        (function (i) {//此处的i是局部变量
          setTimeout(() => {
            console.log(i); //输出0 1 2
          }, 100);
        })(i);//此处的i是全局变量
      }

常见的闭包

  1. 将函数作为另一个函数的返回值
  2. 将函数作为实参传递给另一个函数调用
 //1.将函数作为另外一个函数的返回值
      function fn1() {
        var a = 2;
        function fn2() {
          a++;
          console.log(a);
        }
        return fn2;
      }
      var f = fn1();
      f();
      f();
      // 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模块

  • 具有特定功能的js文件
  • 将所有的数据和功能都封装在一个函数内部(私有的)
  • 只向外暴露一个包含n个方法的对象或函数
  • 模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能
<script type="text/javascript" src="05_coolModule.js"></script>
    <script type="text/javascript">
      var module = coolModule();
      module.doSomething();
      module.doOtherthing();
    </script>

外部05_coolModule.js"文件

/**
 * 自定义模块1
 */
function coolModule(msg) {
  //私有的数据

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

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

也可以将该属性定义在全局中,直接使用

/**
 * 自定义模块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 =  {
    doSomething: a,
    doOtherthing: b
  }
})(window)

练习题:

    /*
   说说它们的输出情况
   */

      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

内存溢出与内存泄露

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

Object构造函数模式

方式一: Object构造函数模式

  • 套路: 先创建空Object对象, 再动态添加属性/方法
  • 适用场景: 起始时不确定对象内部数据
  • 问题: 语句太多
 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
  }

对象字面量模式

  • 套路: 使用{}创建对象, 同时指定属性/方法
  • 适用场景: 起始时对象内部数据是确定的
  • 问题: 如果创建多个对象, 有重复代码
  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)
  console.log(p2)

自定义构造函数模式

方式四: 自定义构造函数模式

  • 套路: 自定义构造函数, 通过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. 子类型的原型为父类型的一个实例对象
/**
 * 6.原型链继承
 * 套路:
 * 1-定义父类型和子类型的构造函数
 * 2-给父类型、子类型原型添加方法(给子类型原型添加方法要3、4定义之后)
 * 3-子类型的原型为父类型的一个实例对象
 * 4-更正子类型的构造函数为子类型
 * 5-创建的子类型的对象:可以调用父类型的方法
 */
		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. 套路:
  2. 定义父类型构造函数
  3. 定义子类型构造函数
  4. 在子类型构造函数中调用父类型构造
  5. 关键:
  6. 在子类型构造函数中通用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.name = name; 相当于Person.call(this, name, age);
        // this.age = 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; //修正constructor属性
      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. 线程: CPU的基本调度单位, 是程序执行的一个完整流程
  3. 进程与线程
  • 一个进程中一般至少有一个运行的线程: 主线程
  • 一个进程中也可以同时运行多个线程, 我们会说程序是多线程运行的
  • 一个进程内的数据可以供其中的多个线程直接共享
  • 多个进程之间的数据是不能直接共享的
  1. 浏览器运行是单进程还是多进程?
  • 有的是单进程
    • firefox
    • 老版IE
  • 有的是多进程
    • chrome
    • 新版IE
  1. 如何查看浏览器是否是多进程运行的呢?
  • 任务管理器==>进程
  1. 浏览器运行是单线程还是多线程?
  • 都是多线程运行的

浏览器内核

  1. 什么是浏览器内核?
  • 支持浏览器运行的最核心的程序
  1. 不同的浏览器可能不太一样
  • Chrome, Safari: webkit
  • firefox: Gecko
  • IE: Trident
  • 360,搜狗等国内浏览器: Trident + webkit
  1. 内核由很多模块组成
  • js引擎模板:负责js程序的编译与运行

  • html,css文档解析模块 : 负责页面文本的解析

  • dom/css模块 : 负责dom/css在内存中的相关处理

  • 布局和渲染模块 : 负责页面的布局和效果的绘制

  • 定时器模块 : 负责定时器的管理

  • 网络请求模块 : 负责服务器请求(常规/Ajax)

  • 事件响应模块 : 负责事件的管理

js是单线程执行的

  1. 如何证明js执行是单线程的?
  • setTimeout()的回调函数是在主线程执行的
  • 定时器回调函数只有在运行栈中的代码全部执行完后才有可能执行
  1. 为什么js要用单线程模式, 而不用多线程模式?
  • JavaScript的单线程,与它的用途有关。
  • 作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。
  • 这决定了它只能是单线程,否则会带来很复杂的同步问题
  1. 代码的分类:
  • 初始化代码
  • 回调代码
  1. js引擎执行代码的基本流程
  • 先执行初始化代码: 包含一些特别的代码
    • 设置定时器
    • 绑定监听
    • 发送ajax请求
  • 后面在某个时刻才会执行回调代码

事件循环模型

  1. 所有代码分类
  • 初始化执行代码(同步代码): 包含绑定dom事件监听, 设置定时器, 发送ajax请求的代码
  • 回调执行代码(异步代码): 处理回调逻辑
  1. js引擎执行代码的基本流程:
  • 初始化代码===>回调代码
  1. 模型的2个重要组成部分:
  • 事件(定时器、DOM事件、AJAX)管理模块
  • 回调队列
  1. 模型的运转流程
  • 执行初始化代码, 将事件回调函数交给对应模块管理
  • 当事件发生时, 管理模块会将回调函数及其数据添加到回调列队中
  • 只有当初始化代码执行完后(可能要一定时间), 才会遍历读取回调队列中的回调函数执行
    在这里插入图片描述
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值