(92)Wangdao.com_第二十五天_线程机制_H5 Web Workers 分线程任务_事件 Event

浏览器内核

支撑浏览器运行的最核心的程序

  • IE 浏览器内核            Trident内核,也是俗称的IE内核
    Chrome 浏览器内核            统称为 Chromium 内核或 Chrome 内核,以前是 Webkit 内核,现在是 Blink 内核
    Firefox 浏览器内核            Gecko 内核,俗称 Firefox 内核
    Safari 浏览器内核            Webkit 内核
    Opera 浏览器内核            最初是自己的 Presto 内核,后来是 Webkit,现在是 Blink 内核

  • 360浏览器、猎豹浏览器内核:IE+Chrome双内核;
    搜狗、遨游、QQ浏览器内核:Trident(兼容模式)+Webkit(高速模式);
    百度浏览器、世界之窗内核:IE内核;
    2345浏览器内核:以前是IE内核,现在也是IE+Chrome双内核;

  • 浏览器的事件循环模型(轮询)——主线程,分线程 协作执行

主线程,从上而下执行代码,将代码产生的相关任务分配给对应的分线程

分线程 每当有 事件回调函数 在满足一定条件后,加入回调队列的末尾

js 执行完所有代码以后,js  开启轮询,会不断地 轮询回调队列,执行队列中的回调函数

  • 执行栈: 所有代码都在 这个空间 中执行
  • 回调队列,遵循先进先出
  • 任务队列 task queue消息队列 message queue事件队列 event queue——都是指的 callback queue 回调队列
  • 事件轮询 event loop : 从任务队列中循环取出回调函数放入 执行栈 中执行
  • 事件驱动模型: event-driven interaction model
  • 请求响应模型: request-response model

  • 由许多模块组成:

主线程:

js 引擎模块            负责 js 程序的编译与运行

html/css 文档解析模块            负责 页面文本 的解析

DOM/CSS 模块            负责 DOM 和 css 在内存中的处理

布局和渲染模块            负责页面的布局和效果的绘制(内存中的对象)

分线程:

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

DOM事件响应模块            负责事件的管理

网络请求模块            负责 AJAX 的请求  

进程

程序的一次执行,占有一片独有的内存空间

单进程

多进程,同一时间可做多件事

线程

调度 CPU 执行具体程序,是CPU的最小调度单元

是进程内的一个独立执行单元

应用程序必须运行在某个进程的某个线程上

一个进程至少有一个运行的线程——主线程,进程启动后自动创建

一个进程内的数据 是可以被多个线程直接共享的

进程与进程之间的数据是不共享的

线程池: 保存多个线程对象的容器,实现线程对象的反复利用

  • 1. 单线程 优点: 顺序编程简单易懂
  • 2. 单线程 缺点: 效率低
  • 3. 多线程 优点: 能有效提升 CPU 的利用率
  • 4. 多线程 缺点: 创建多线程开销,线程切换开销,产生死锁和状态同步的问题
  • 浏览器 js 引擎 开启了一个进程,该进程有且只有一个线程
  • IE 最开始是 单进程的,速度慢,但是事件的处理更简单,某个页面崩溃,整个浏览器都崩溃了。
  • 现代浏览器 大多都是 多进程的,速度快,每个页面都是单独的进程,彼此互不影响。
  • 浏览器是有些是单进程,有些是多进程的。但是一定是多线程的——使用任务管理器查看
  • js 是单线程的,alert 会停止继续运行。
  • 但使用 H5 中的 Web Workers 可以多线程运行

 

H5 规范提供了 js 分线程——Web Workers 

需求: 主线程 如果再进行大量计算任务时,无法执行其他任务

1. index.js 使用分线程

  • var newWorker = new Worker(./worker.js);    // 开启一个分线程
    
    // 绑定接受分线程消息的事件
    newWorker.onmessage = function(e){
        console.log("分线程: "+e.data);
    };
    
    btn.onclick = function(){
        newWorker.postMessage(number.value);    // 主线程 通知分线程 开始执行
    };

 

2. worker.js 写 分线程 接收主线程发送的消息

  • var onmessage = function(event){
        // 接收消息的回调函数 event
        var data = event.data;
        var result = fibonacci(+data); // 计算
        postMassage(result);
    };

    function fibonacci(num){
    return (num < 3)?1:(fabonacci(num-1)+fabonacci(num-2));
    }

 

1. 事件 Event

事件的本质是: 程序各个组成部分之间的一种通信方式,也是 异步编程 的一种实现

DOM 的事件操作 分为: 监听 触发        都定义在 EventTarget 接口____

所有节点对象都部署了 EventTarget 接口,其他一些需要事件通信的浏览器内置对象(比如,XMLHttpRequest、AudioNode、AudioContext)也部署了这个接口。

.addEventListener();        绑定事件的监听函数

.removeEventListener();        移除事件的监听函数

.dispatchEvent();        触发事件

 

  • EventTarget.addEventListener(eventType, listener[, useCapture]);

用于在当前节点或对象上,定义一个特定事件的监听函数。

一旦这个事件发生,就会执行监听函数。

可以为针对当前对象的同一个事件,添加多个不同的监听函数。这些函数按照添加顺序触发,即先添加先触发。

如果为同一个事件多次添加同一个监听函数,该函数只会执行一次,多余的添加将自动被去除(不必使用removeEventListener方法手动去除)。

  • 该方法没有返回值
  • 参数: 

eventType        事件名称,大小写敏感。

listener        监听函数。事件发生时,会调用该监听函数。

  • 注意: 
  • 除了监听函数,还可以是一个 具有 handleEvent 方法的对象
  • btn.addEventListener('click',   {
                                        handleEvent: function (event) {
                                            console.log('click');
                                        }
                                    }, false);

[useCapture]        是否在捕获阶段(capture)触发(参见后文《事件的传播》部分)默认为 false(监听函数只在冒泡阶段被触发)。该参数可选

  • 注意: 
  • 除了布尔值 useCapture,还可以是一个属性配置对象
  • 该对象有以下属性:

capture: 布尔值,表示该事件是否在捕获阶段触发监听函数。

once: 布尔值,表示监听函数是否只触发一次,然后就自动移除。

passive: 布尔值,表示监听函数不会调用事件的 preventDefault 方法。默认为 true 如果监听函数调用了,浏览器将忽略这个要求,并在监控台输出一行错误。

  • 希望事件监听函数只执行一次,可以打开属性配置对象的once属性
  • ele.addEventListener('click', function (event) {
      // 只执行一次的代码
    }, {once: true});
  • 如果希望向监听函数传递参数,可以用匿名函数包装一下监听函数
  • var box = document.getElementById('div1');
    box.addEventListener('click', function () {
        myPrint('Hello'); 
    }, false);
    
    function myPrint(x) {
        console.log(x);
    }
  • EventTarget.removeEventListener(eventType, listener);

用来移除 .addEventListener() 方法添加的事件监听函数。

该方法没有返回值

listener 在绑定时是 匿名函数,那么将无法直接用 .removeEventListener() 移除。

 

  • EventTarget.dispatchEvent(Event对象的实例)

在当前节点上触发指定事件,从而触发监听函数的执行。

  • 返回一个布尔值

只要有一个监听函数调用了 Event.preventDefault(),则返回值为 false,否则为 true

  • ele.addEventListener('click', hello, false);
    
    var event = new Event('click');
    ele.dispatchEvent(event);    // 主动触发 click 事件

     

2. 浏览器的事件模型

就是通过监听函数(listener)对事件做出反应。

事件发生后,浏览器监听到了这个事件,就会执行对应的监听函数。

____这是事件驱动编程模式(event-driven)的主要编程方式

  • 绑定事件监听函数的三种方法:

在 html 标签 中直接绑定

  • 只会在冒泡阶段触发
  • 缺点: 违反了 HTML 与 JavaScript 代码相分离的原则
  • <body onload="doSomething()">    <!-- 等同于 document.body.setAttribute("onload", "doSomething()"); -->
    <div id="box" onclick="console.log('触发事件')">    // this 输出 box

在 js 代码中,给元素对象 的事件属性绑定监听函数

  • 众所周知的 DOM0 级事件模型 绑定方式,也只会在冒泡阶段触发
  • 缺点: 同一个事件只能定义一个监听函数,也就是说,如果定义两次onclick属性,后一次定义会覆盖前一次
  • window.onload = doSomething;
    
    div.onclick = function (event) {
        console.log('触发事件');
    };

使用 EventTarget.addEventListener() 绑定

  • 众所周知的 DOM2 级事件模型 绑定方式
  • window.addEventListener('DOMContentLoaded', doSomething, false);
  • 优点:

除了 DOM 节点,其他对象(比如window、XMLHttpRequest等)也有这个接口,等于是整个 JavaScript 统一的监听函数接口

能够指定在哪个阶段(捕获阶段还是冒泡阶段)触发监听函数

同一个事件可以添加多个监听函数

 

3. 事件的传播

使得同一个事件会在多个节点上触发

  • 分成三个阶段

“捕获阶段”(capture phase)        从 window 对象 传导到 目标节点
“目标阶段”(target phase)        在目标节点上触发
“冒泡阶段”(bubbling phase)        从 目标节点 传导回 window对象

  • var phases = {
        1: 'capture',
        2: 'target',
        3: 'bubble'
    };
    
    var div = document.querySelector('div');
    var p = document.querySelector('p');
    
    div.addEventListener('click', callback, true);
    div.addEventListener('click', callback, false);
    
    p.addEventListener('click', callback, true);
    p.addEventListener('click', callback, false);
    
    function callback(event) {
        console.log("Tag: " + event.currentTarget.tagName,
                    "EventPhase: " + phases[event.eventPhase]);
    };
    
    // 点击以后的结果
    // Tag: 'DIV'. 
    // EventPhase: 'capture'
    
    // Tag: 'P'
    // EventPhase: 'target'
    
    // Tag: 'P'
    // EventPhase: 'target'
    
    // Tag: 'DIV'
    //EventPhase: 'bubble'
  • 注意:

浏览器总是假定 click 事件的目标节点,就是点击位置嵌套最深的那个节点

事件传播的最上层对象是window,接着依次是document,html(document.documentElement)和body(document.body)

也就是说,上例的事件传播顺序,

在捕获阶段依次为window、document、html、body、div、p

在冒泡阶段依次为p、div、body、html、document、window。

  • 事件委派 / 事件代理(delegation)

把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件

  • 优点:

只要定义一个监听函数,就能处理多个子节点的事件,而不用在每个<li>节点上定义监听函数

而且以后再添加子节点,监听函数依然有效

  • 如果希望事件到某个节点为止,不再传播,可以使用事件对象的 event.stopPropagation() 方法

但是,stopPropagation方法只会阻止事件的传播,

不会阻止该事件触发 <p> 节点的其他 click 事件的监听函数。

也就是说,不是彻底取消click事件

  • p.addEventListener('click', function (event) {
      event.stopPropagation();
      console.log(1);    // 不会触发
    });
    
    p.addEventListener('click', function(event) {
      // 会触发
      console.log(2);
    });
  • 如果想要彻底取消该事件不再触发后面所有click的监听函数,可以使用 event.stopImmediatePropagation() 方法

 

4. Event 对象

Event 对象本身就是一个构造函数,可以用来生成新的实例

 

  • event = new Event(type, options);

接收两个参数: 

  • type        字符串,表示 事件的名称
  • options        对象,表示 事件对象的配置。

该对象主要有下面两个属性

  • bubbles        布尔值,可选,默认为 false,表示事件对象是否冒泡

如果不是显式指定 bubbles 属性为 true,生成的事件就只能在 “捕获阶段” 触发监听函数

  • cancelable        布尔值,可选,默认为false,表示事件是否可以被取消

即能否用 event.preventDefault() 取消这个事件。

一旦事件被取消,就好像从来没有发生过,不会触发浏览器对该事件的默认行为

  • 实例属性

Event.bubbles

表示当前事件是否会冒泡。

返回一个布尔值,除非显式声明,Event构造函数生成的事件,默认是不冒泡的。

该属性为只读属性,用来判断 Event 实例是否可以冒泡

Event.eventPhase

表示事件目前所处的阶段。

返回一个整数常量,该属性只读

  • 0,事件目前没有发生。
    1,事件目前处于捕获阶段,即处于从祖先节点向目标节点的传播过程中。
    2,事件到达目标节点,即 Event.target 属性指向的那个节点。
    3,事件处于冒泡阶段,即处于从目标节点向祖先节点的反向传播过程中。

Event.cancelable

表示事件是否可以取消。

返回一个布尔值,该属性为只读属性,

一般用来判断 Event 实例是否可以被取消

  • 当 Event.cancelable 属性为true 时,调用 Event.preventDefault(); 就可以取消这个事件,阻止浏览器对该事件的默认行为。
  • 如果事件不能取消,调用 Event.preventDefault() 会没有任何效果。
  • 所以使用这个方法之前,最好用 Event.cancelable 属性 判断一下是否可以取消
  • /**** 封装 禁止浏览器默认行为 ****/
    function
    preventEvent(event) { if (event.cancelable) { event.preventDefault(); } else { console.warn('This event couldn\'t be canceled.'); console.dir(event); } }

Event.cancelBubble

阻止事件的传播。        如果设为true,相当于执行Event.stopPropagation()

是一个布尔值

Event.defaultPrevented

表示该事件是否调用过 Event.preventDefault() 方法。

返回一个布尔值,该属性只读。

Event.currentTarget

返回 事件当前所在的节点,即正在执行的监听函数所绑定的那个节点,随事件传播过程而变化

Event.target

返回 原始触发事件的那个节点,即事件最初发生的节点,事件传播过程中是固定不变的

Event.type

表示事件类型。

返回一个字符串,事件的类型是在生成事件的时候。

该属性只读

Event.timeStamp

返回一个毫秒时间戳,表示事件发生的时间。

它是相对于网页加载成功开始计算的。

  • 实例计算鼠标移动速度,每秒移动的 px 值
  • var previousX;
    var previousY;
    var previousT;
    
    window.addEventListener('mousemove', function(event) {
        if (previousX !== undefined &&
            previousY !== undefined &&
            previousT !== undefined ){
            var deltaX = event.screenX - previousX;    // 获取  当前的 xOffset
            var deltaY = event.screenY - previousY;    // 获取  当前的 yOffset
            var deltaD = Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));
    
            var deltaT = event.timeStamp - previousT;
            console.log(deltaD / deltaT * 1000);    // 速度 = 路程 / 时间
        };
    
        previousX = event.screenX;    // 获取移动前的 x
        previousY = event.screenY;    // 获取移动前的 y
        previousT = event.timeStamp;    // 获取当前时间戳
    });

Event.isTrusted

表示该事件是否由真实的用户行为产生。

返回一个布尔值

比如,用户点击链接会产生一个 click 事件,该事件是用户产生的;Event 构造函数生成的事件,则是脚本产生的。

Event.detail

该属性返回一个数值,表示事件的某种信息。

只有浏览器的 UI (用户界面)事件才具有此属性。

具体含义与事件类型相关。

比如,

对于 click 和 dbclick 事件,Event.detail 是鼠标按下的次数(1表示单击,2表示双击,3表示三击);

对于鼠标滚轮事件,Event.detail 是滚轮正向滚动的距离,负值就是负向滚动的距离,返回值总是 3 的倍数。

  • 实例方法

Event.preventDefault()

取消浏览器对当前事件的默认行为。

比如点击链接后,浏览器默认会跳转到另一个页面,使用这个方法以后,就不会跳转了;

再比如,按一下空格键,页面向下滚动一段距离,使用这个方法以后也不会滚动了。

再比如,浏览器的默认行为是单击会选中单选框,取消这个行为,就导致无法选中单选框

该方法生效的前提是,事件对象的 cancelable 属性为true,如果为 false,调用该方法没有任何效果

注意,该方法只是取消事件对当前元素的默认影响,不会阻止事件的传播。

如果要阻止传播,可以使用 event.stopPropagation() 或 event.stopImmediatePropagation() 方法

  • 实例: 为文本输入框设置校验条件。如果用户的输入不符合条件,就无法将字符输入文本框。
  • // HTML 代码为
    // <input type="text" id="my-input" />
    var input = document.getElementById('my-input');
    input.addEventListener('keypress', checkName, false);
    
    function checkName(e) {
        if (e.charCode < 97 || e.charCode > 122) {
            e.preventDefault();
        }
    }

    上面代码为文本框的 keypress 事件设定监听函数后,将只能输入小写字母,否则输入事件的默认行为(写入文本框)将被取消,导致不能向文本框输入内容

Event.stopPropagation()

阻止事件在 DOM 中继续传播,防止再触发定义在别的节点上的监听函数,

但是不包括在当前节点上其他的事件监听函数

Event.stopImmediatePropagation()

阻止同一个事件的所有监听函数被调用,不管监听函数定义在当前节点还是其他节点。

也就是说,该方法阻止事件的传播,比Event.stopPropagation()更彻底

Event.composedPath()

返回一个节点数组,成员是事件的最底层节点依次冒泡经过的所有上层节点

 

posted @ 2018-12-10 21:05  耶梦加德  阅读(230)  评论(0编辑  收藏  举报