汇总前端基础学习(系列二:JS/ES5->ES6,模块化开发规范CommonJs/AMD/CMD)

928 阅读33分钟

最全的(前端知识)汇总,以便自身复习

上一篇内容:HTML/CSS2-CSS3

由于篇幅过于长,知识体系过于庞杂,请谨慎学习, 转述的我都有放置链接(难理解的也放了demo测试或者图)

技术规范(基础了解,大佬尽可跳过)

JS/ES5>>>>>ES6

ES5/ES6等历程了解 js

JS基础知识(三部分:ECMAScript/DOM/BOM)

BOM

tips: browser object model(即浏览器对象模型,核心是window

BOM是一系列在浏览器环境中使用对象的统称,这些对象提供了访问浏览器的功能。

  • window对象有双重角色

通俗讲就是JavaScript访问浏览器的一个接口 ECMAScript规定的Global对象(全局对象)

介绍:我们定义的全局变量和全局函数都是window对象的属性和方法

Snipaste_2019-08-14_22-50-24.png

镇山宝图(window对象思维导图)

2014093009570524.gif

MDN全window文档,可以查阅

查阅资料

  • navigator对象

查阅资料 思否 JavaScript BOM对象-Navigator浏览器对象

  • screen对象

参考JS高级程序设计-第3版,挺好的书(有需要找我要电子书)

  • history对象

深入学习History对象管理浏览器会话历史 BOM之history对象 history对象 前端离开页面事件总结

  • location对象

HTML5 Location 对象剖析

  • document对象

javascript之document对象

navigator对象

navigator对象:包含大量有关Web浏览器的信息,在检测浏览器及操作系统上非常有用(window.navigator或者navigator) 每个浏览器中的 navigator 对象也都有一套自己的属性

基本大概有需要的

//以下也可以通过window.navigator获取到

navigator.appCodeName  //浏览器代码名的字符串表示

navigator.appName  //官方浏览器名的字符串表示

navigator.appVersion  //浏览器版本信息的字符串表示

navigator.cookieEnabled  //如果启用cookie返回true,否则返回false

navigator.javaEnabled  //如果启用java返回true,否则返回false

navigator.platform  //浏览器所在计算机平台的字符串表示

navigator.plugins  //安装在浏览器中的插件数组

navigator.taintEnabled  //如果启用了数据污点返回true,否则返回false

navigator.userAgent  //用户代理头的字符串表示

Navigator.onLine  //返回一个布尔值,表示用户当前在线还是离线(浏览器断线)

  • js代码来确认当前浏览器的种类以及版本
var UserAgent =window.navigator.userAgent.toLowerCase();
function getBrowserType(UserAgent){
var result={
      isIE6: /msie 6.0/.test(UserAgent), // IE6
      isIE7: /msie 7.0/.test(UserAgent), // IE7
      isIE8: /msie 8.0/.test(UserAgent), // IE8
      isIE9: /msie 9.0/.test(UserAgent), // IE9
      isIE10: /msie 10.0/.test(UserAgent), // IE10
      isIE11: /msie 11.0/.test(UserAgent), // IE11
      isLB: /lbbrowser/.test(UserAgent), // 猎豹浏览器
      isUc: /ucweb/.test(UserAgent), // UC浏览器
      is360: /360se/.test(UserAgent), // 360浏览器
      isBaidu: /bidubrowser/.test(UserAgent), // 百度浏览
      isSougou: /metasr/.test(UserAgent), // 搜狗浏览器
      isChrome: /chrome/.test(UserAgent), 
      //Chrome浏览器
      isFirefox: /firefox/.test(UserAgent), // 火狐浏览器
      isOpera: /opera/.test(UserAgent), // Opera浏览器
      isSafiri: /safari/.test(UserAgent) && !/chrome/.test
 (UserAgent), // safire浏览器
      isQQ: /qqbrowser/.test(UserAgent)//qq浏览器
    };
 return result;
}
console.log(getBrowserType(UserAgent));

screen 对象

只用来表明客户端的能力,包括浏览器窗口外部的显示器的信息,如像素高度和宽度等。每个浏览器中的screen对象都包含着各不相同的属性

  • 各大浏览器实现的属性

    ps:提及兼容性高的

属性 说明 IE FireFox Safari/Chrome Opera
availHeight 返回显示屏幕的高度 (除 Windows 任务栏之外)
availWidth 返回显示屏幕的宽度 (除 Windows 任务栏之外)
width 返回显示器屏幕的宽度
height 返回显示屏幕的高度
colorDepth 返回目标设备或缓冲器上调色板的比特深度(多数系统32位)
pixelDepth 返回显示屏幕的颜色分辨率(比特每像素) ×
availLeft 返回未被系统部件占用的最左侧的像素 × ×
availTop 返回未被系统部件占用的最上方的像素 × ×

history对象

History对象允许我们操作浏览器会话历史,即加载当前页面的标签页窗口或frame窗口的访问历史

  • 属性(window.history获取)

    • history.length:包括当前页面在内的会话历史中的记录数量

    • history.state:属性用来保存记录对象(返回当前页面的state对象,上述的state对象)【区别不同会话历史纪录】

  • 导航方法(window.history获取)

    • history.back():返回会话历史记录中的上一个页面

    • history.forward():进入会话历史记录中的下一个页面

    • history.go():加载会话历史记录中的某一个页面,该页面当前页面在会话历史中的相对位置定位,-1代表当前页面的上一个记录,1代表当前页面的下一个页面

  • 增改记录

    • 方法

      • history.pushState():在浏览历史中添加【向浏览器历史添加了一个状态】

        • state —— 状态对象是一个由pushState()方法创建的、与历史纪录相关的javascript对象,当用户定向到一个新的状态时,会触发popstate事件

        • title —— 新页面的标题,但是所有浏览器目前都忽略这个值,因此这里可以填null

        • url —— 必须与当前页面URL同源

            语法: history.pushState(state, title, url);
          
          
              var stateObj = { foo: "bar" };
              history.pushState(stateObj, "page 2", "bar.html");
          
      • history.replaceState():在浏览历史中修改记录(更新当前会话历史记录,如更新当前会话记录的状态对象或URL。)

  • 事件

    • popstate:监听history对象的变化【每当同一个文档的浏览历史(即history对象)出现变化时,就会触发popstate事件】

      仅仅调用pushState方法或replaceState方法,并不会触发该事件,只有用户点击浏览器倒退按钮和前进按钮,或者使用javascript调用back()、forward()、go()方法时才会触发

往返缓存概念(浏览器有一个特性)

  1. 可以在用户使用浏览器的“后退”和“前进”按钮时加快页面的转换速度

  2. 这个缓存中不仅保存着页面数据,还保存了DOM和javascript的状态

  3. 如果页面位于bfcache中,那么再次打开该页面时就不会触发load事件

  • 进入页面与离开页面事件 pageshow与 pagehide=》(无法执行alert操作,window 对象 和 dom 对象都已经被销毁了)

    • pageshow:页面加载时触发,包括第一次加载和从缓存加载两种情况 ps:第一次加载时,它的触发顺序排在load事件后面

         window.onpageshow = function(e){
                e = e || event;
                console.log(e.persisted,进入加载事件);
            }
      
    • pagehide:该事件会在浏览器卸载页面的时候触发,而且是在unload事件之前触发

          window.onpagehide = function(e){
           e = e || event;
           console.log(e.persisted);
       }
      

前端离开页面事件总结(出现提示框有点会有点问题,因为alert是阻塞)

  • Onblur :监控(窗口 页面 组件)失去焦点事件

  • Onfocus :监控(窗口 页面 组件)得到焦点事件

  • onbeforeunload:在即将离开当前页面(刷新或关闭)时执行 JavaScript

       window.onbeforeunload=function(e){     
        var e = window.event||e;  
        e.returnValue=("确定离开当前页面吗?");
      } 
    

location对象

Snipaste_2019-08-15_11-04-44.png

  • 属性

以http://www.baidu.com:8080/index.php?name=kang&when=2011#first为例

属性 描述
protocol 设置或返回当前 URL 的协议。 http:
hostname 设置或返回当前 URL 的主机名。 www.baidu.com
host 设置或返回主机名和当前 URL 的端口号 www.baidu.com:8080
port 返回当前 URL 的端口号 8080
pathname 设置或返回当前 URL 的路径部分 /index.php
search 设置或返回从问号 (?) 开始的 URL(查询部分) ?name=kang&when=2011
hash 设置或返回从井号 (#) 开始的 URL(锚) #first
href 设置或返回完整的 URL 全链接

window.location和document.location互相等价。 location 的8个属性都是可读写,但是只有href与hash的写才有意义

  • 方法

    • assign() :加载新的文档

      • 导航到一个新页面的方法

        window.location.assign("http://www.mozilla.org"); // or
        window.location = "http://www.mozilla.org";
        
    • reload() :重新加载当前文档

    如果文档已改变,reload() 会再次下载该文档。如果文档未改变,则该方法将从缓存中装载文档。这与用户单击浏览器的刷新按钮的效果是完全一样的。如果把该方法的参数设置为 true,那么无论文档的最后修改日期是什么,它都会绕过缓存,从服务器上重新下载该文档

          window.location.reload(true);
    
    • replace() :用新的文档替换当前文档

    不会在 History 对象中生成一个新的记录。当使用该方法时,新的 URL 将覆盖 History 对象中的当前记录。

          window.location.replace('http://example.com/'); 
    

document 对象

兼容问题

  • document.documentElement.body || document.body 区别 document.body 和 document.documentElement 的区别

    • document.documentElement:返回html dom中的root 节点 即html
    • document.body:返回html dom中的body节点 即body
  • 获取 scrollTop 方面的差异

    兼容性解决方案:var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;

DTD概念普及 W3C HTML中 !DOCTYPE 的解释与作用

  1. Document Type Definition,中文翻译为:文档类型定义【总体上应该分为三类: HTML5,HTML4.0,XHTML。】

  2. 早期的版本基于SGML,所以需要套用SGML的解析规则。DTD的作用在于定义SGML文档的文档类型以便于浏览器解析。

  3. 随着技术的进步,现在HTML5 不基于 SGML,所以也就不需要引用 DTD了

牵扯处一个问题什么是!DOCTYPE,以及作用(面试题)

  1. DOCTYPE是document type(文档类型)的简写,在Web设计中用来说明你用的XHTML或者HTML是什么版本

  2. 没有<!DOCTYPE>声明,那么不同的浏览器将会以自己不同的怪异的模式去解析渲染页面,这样页面在不同的浏览器上呈现出来的效果也就不一样,人们把这称之为“怪异模式”

  3. 如果声明了,将会开启“严格模式”,又有人称之为“标准模式”,浏览器将已w3c标准来解析渲染页面

  • document获取到当前页面很多数据信息【属性】

    • 所有图片images/表单forms/链接links/锚点anchors的集合

        document.images/document.forms/document.links/document.anchors
      
    • 当前页的cookie/域名domain/获取载入当前文档的文档的url/标题title/当前url

        document.cookie/document.domain/document.referrer/document.title/document.URL
      
  • 方法

    • document.write() :并将原有内容替换为一些不同的HTML片段【在页面加载后调用,会发生自动的 document.open()】

    不要和 window.open() 方法混淆。document.open 可用于重写当前的文档内容或者追加内容, 而 window.open 是提供了打开一个新的窗口的方法,当前的网页文档内容会被保留

    • document.open():文档中的所有节点会被清除

窗口控制

window.scrollBy(x,y)/window.scrollTo(x,y)挺有用的,移动文档

MoveTo和MoveBy的区别(可引申为To和By的区别)

  • By 和 To 的区别(move/resize/scroll)--窗口移动变化(浏览器禁止)/窗口大小变化(浏览器可能会禁止掉)/内容滚动

    • By 算的是相对于节点对象的当前位置

    • To 算的是绝对位置,不考虑当前节点对象在哪

moveBy()/moveTo()----->在chrome,IE浏览器上运行,都不显示效果

原因:为了保护计算机的安全性

定时器

  • setInterval : 周期性的调用一个函数或者执行一段代码
  • setTimeout : 指定延迟一定的时间后调用一个函数或者执行某一个动作
  • clearInterval :清除setInterval设置的动作
  • clearTimeout :清除setTimeout设置的动作

----语法不多赘述

  • 重点

javascript都是以单线程的方式运行于浏览器的javascript引擎中的 settimeout和setinterval的作用只是把你要执行的代码在你设定的一个时间点插入js引擎维护的一个代码队列中 插入代码队列并不意味着你的代码就会立马执行的,理解这一点很重要 settimeout和setinterval还有点不一样

扯出最最重要的知识点:Event Loop(面试题) 通杀 Event Loop 面试题 / Js 的事件循环(Event Loop)机制以及实例讲解

js是单线程的脚本语言,在同一时间只能做同一件事,为了协调事件、用户交互脚本UI渲染网络处理等行为,防止主线程阻塞Event Loop方案应运而生

  • 知识点的普及

    • 执行栈

      当执行某个函数、用户点击一次鼠标,Ajax完成,一个图片加载完成等事件发生时,只要指定过回调函数,这些事件发生时就会进入任务队列中,等待主线程读取,遵循先进先出原则

    • 主线程

      1. 主线程跟执行栈是不同概念,主线程规定现在执行执行栈中的哪个事件
      2. 主线程循环:即主线程会不停的从执行栈中读取事件,会执行完所有栈中的同步代码
    • 任务队列(Task Queue)

      1. 遇到一个异步事件后,并不会一直等待异步事件返回结果,而是会将这个事件挂在与执行栈不同的队列中--》 任务队列
    • 顺序讲解: 当主线程执行栈中所有的代码执行完之后主线程将会去查看任务队列是否有任务。如果有,那么主线程会依次执行那些任务队列中的回调函数

    • 宏任务与微任务(很重要)

      • 异步任务分为 宏任务(macrotask) 与 微任务 (microtask),不同的API注册的任务会依次进入自身对应的队列中,然后等待 Event Loop 将它们依次压入执行栈中执行。

      • 宏任务(以下都是) script(整体代码)、setTimeout、setInterval、UI 渲染、 I/O、postMessage、 MessageChannel、setImmediate(Node.js 环境)、UI rendering (浏览器独有)

      • 微任务(以下都是) Promise.then()、 MutaionObserver、process.nextTick(Node.js环境)、Object.observe

    • Event Loop(事件循环) 【所有任务看成两个队列:执行队列与事件队列】

      --->先执行script --->遇到宏任务(放入事件列表),遇到微任务(先依次压入执行栈),等script里的同步任务完成 --->再执行执行栈里面的微任务 --->最后执行事件列表里的宏任务

注意:new Promise() 构造函数里面是同步代码,而非微任务

promise.then 虽然和 process.nextTick 一样,都将回调函数注册到微任务,但优先级不一样。process.nextTick 的微任务队列 总是优先于 promise 的 微任务队列 执行

在 I/O Callbacks 中注册的 setTimeout 和 setImmediate,永远都是 setImmediate 先执行。

ss

        setTimeout(function () {
            console.log(1);
        });
        new Promise(function(resolve,reject){
            console.log(2)
            resolve(3)
        }).then(function(val){
            console.log(val);
        })
        console.log(4);

    1. 遇到setTimeout(宏任务)压到事件列表
    2. 进入Promise,遇到console.log(2),打印2
    3. 遇到resolve(3),回调函数【Promise.then是微任务】压入执行栈
    3. 遇到console.log(4),执行4-------》执行完script
    4. 执行微任务resolve(3),转到then,打印3
    4. 最后执行事件列表,执行宏任务setTimeout,打印1

--》 2 4 3 1

  • 巩固提高

                   console.time("start")
    
                   setTimeout(function () {
                       console.log(2);
                   }, 10);
                   setImmediate(function () {
                       console.log(1);
                   });
    
                   new Promise(function (resolve) {
                       console.log(3);
                       resolve();
                       console.log(4);
                   }).then(function () {
                       console.log(5);
                       console.timeEnd("start")
                   });
                   console.log(6);
                   process.nextTick(function () {
                       console.log(7);
                   });
                   console.log(8);
                   // requestAnimationFrame(() => console.log(9))。
    

讲完了这个执行顺序(贼重要)问题,回到计时器

关于setTimeout()你所不知道的地方,详解setTimeout()

  • setTimeout和setInterval的运行机制

将指定的代码移出本次执行,等到下一轮Event Loop时,再检查是否到了指定时间。如果到了,就执行对应的代码;如果不到,就等到再下一轮Event Loop时重新判断。这意味着, setTimeout和setInterval指定的代码,必须等到本次执行的所有代码都执行完,才会执行

由于前面的任务到底需要多少时间执行完,是不确定的,所以没有办法保证,setTimeout和setInterval指定的任务,可能会不一定按照预定时间执行

  1. setTimeout来代替setInterval

                 timer=setTimeout(fn,1000)
    
                 function fn(){  
                     console.log("循环")
                     //argement.callee代表代表上下文正在执行的函数--》这些都会在后面一一细说
                     setTimeout(arguments.callee,1000)  
                   }
    
  2. setTimeout(fn,0)来置换顺序

  3. setTimeout(fn,time),在执行过程中time是能改变的(调整快慢)

  4. setTimeout循序回调(传参)

            setTimeout(function(a,b){
               console.log(a+b);
             },1000,1,1);
    

注意点:setTimeout()中回调函数中的this(会指向全局,利用apply/call解决) 你所不知道的setTimeout

为什么不使用 setInterval-->跳帧---》解决方案,比如容易内存泄漏 深度解密setTimeout和setInterval——为setInterval正名 / 从setTimeout-setInterval看JS线程(setInterval缺点) / 阮一峰的线程与进程

对话框

  • alert :显示警告框的信息; 无返回值

  • confirm:参数就只有一个.显示提示框的信息. 按确定,返回true; 按取消返回false.

                      var truthBeTold = window.confirm("单击“确定”继续。单击“取消”停止。"); 
                      if (truthBeTold) { 
                          window.alert("欢迎访问我们的 Web 页!"); 
                      } else {
                          window.alert("再见啦!");
                       }
    
  • prompt:参数,有两个, 第一个参数,显示提示输入框的信息. 第二个参数,用于显示输入框的默认值. 返回,用户输入的值.

                   var data = window.prompt("输入验证码", "")
                      if( data == 'ss' )
                      {
                          alert("ok");
                      }
                      else
                      {
                          alert("false");
                      }
    

(重要)浏览器窗口属性

JavaScript中的各种宽高属性 / JS 相关宽高理解,有图表可以查阅

  • 了解跨浏览器确定窗口大小是很不容易的:

    Snipaste_2019-08-15_16-39-01.png

    1. window.innerHeight/innerWidth:第一种方式获取的是可视区的宽高,包括了滚动条的宽度

      ps:IE 8 及更早 IE版本不支持这两个属性。

    2. document.body.clientHeight/clientWidth: 第二种方式获取的是可视区的宽高,不包括滚动条的宽度以及边框border【 获取body宽度】

      兼容性写法:document.documentElement.clientHeight/clientWidth(获取整个页面)

      document.documentElement对象的clientWidth..... / 如何使用 JavaScript 获取页面、窗口的高度?

     // 页面可视区的宽度
var oClientWidth =  document.documentElement.clientWidth || document.body.clientWidth || 0;
     
    // 页面可视区的高度
var oClientHeight =  document.documentElement.clientHeight || document.body.clientHeight || 0;

  1. document.body.offsetWidth/offsetHeight :是可视区的宽高(即元素占据的空间,包括填充和边框,不包括滚动条)【 获取body宽高度】

    兼容性写法:document.documentElement.offsetWidth/offsetHeight(获取整个页面)

// scrollWidth 表示整个网页正文的宽度
    var scrollWidth = document.documentElement.offsetWidth || document.body.offsetHeight;
 
// scrollHeight 表示整个网页正文的高度
    var scrollHeight = document.documentElement.offsetWidth || document.body.offsetHeight;

  1. document.body.scrollWidth/scrollHeight: 网页正文全文宽高【 获取body正文全文宽高】

    兼容性写法:document.documentElement.scrollWidth/scrollHeight(获取整个页面正文全文宽高)

// scrollWidth 表示整个网页正文的宽度
    var scrollWidth = document.documentElement.scrollWidth || document.body.scrollWidth;
 
// scrollHeight 表示整个网页正文的高度
    var scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;

  • 页面正文卷起长度

    • pageYOffset/pageXOffset ------ IE 8 及 更早 IE 版本不支持该属性,但可以使用 document.documentElement.scrollLeft 和 document.documentElement.scrollTop 属性

    解决兼容:scrollTop/scrollLeft

 // scrollTop 网页被卷起的高度
    var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
 
    // scrollLeft 网页左边被卷起的宽度
    var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0;


  • window 相关宽高

    Snipaste_2019-08-15_16-40-08.png

  • Event对象的5种坐标

  1. clientX和clientY,相对于浏览器(可是区左上角0,0)的坐标

  2. screenX和screenY,相对于设备屏幕左上角(0,0)的坐标

  3. offsetX和offsetY,相对于事件源左上角(0,0)的坐标

  4. pageX和pageY,相对于整个网页左上角(0,0)的坐标

  5. X和Y,本来是IE属性,相对于用CSS动态定位的最内层包容元素

DOM

关于DOM级别的一些问题 DOM事件全整理之从DOM事件级别,DOM事件流到事件委托

文档对象模型 (DOM) 是HTML和XML文档的编程接口。它提供了对文档的结构化的表述,并定义了一种方式可以使从程序中对该结构进行访问,从而改变文档的结构,样式和内容。

DOM描绘了一个层次化的节点树,允许开发人员添加、移除和修改页面的某一部分。

简单来讲,DOM就是一组API(接口)。它将一份结构化文档看做一棵树,这棵树由各种各样的节点构成,也即节点树

精辟:DOM标准的目标是让“任何一种程序设计语言”能操控使用“任何一种标记语言”编写出的“任何一份文档”。“操控”具体含义为能通过DOM提供的API对文档的内容、结构、样式进行访问和修改

ss

DOM树

获取节点

  • 方法

    • getElementById():根据元素的id来获取该元素对象,通过该方法获取的元素对象是唯一的,可以直接对其进行操作

    • getElementsByClassName():根据元素的class类名来获取该元素对象,通过该方法获取的元素对象是一个伪数组,需要通过伪数组的方式对其进行访问

    • getElementsByTagName():根据元素的标签名来获取该元素对象,通过该方法获取的元素对象是一个伪数组,需要通过伪数组的方式对其进行访问

是 HTML5 新增加的一个 js 函数,对于一些老浏览器例如 IE9 以下的浏览器是不支持这个函数的,所以在那些需要处理兼容性问题的

getElementsByClassName()方法不兼容ie低版本解决方案

        if(!document.getElementsByClassName)//判断浏览器是否支持这个方法
              {
                  document.getElementsByClassName=function(cname){
                      var selected=new Array();
                      var alltag=document.getElementsByTagName("*");//获取所有标签
                      for(var i=0;i<alltag.length;i++)
                      {
                          var t=alltag[i];
                          alert(t.className);
                          if(t.className==cname)    //比较标签的class与所要查找的class是否相同
                          {
                              selected.push(t);          //将相同的存入数组
                          }
                      }
                      return selected;
                  }
              }

querySelector与getElement...差异以及区别 querySelector和getElementById性能分析与使用选择 / 为何getElementsByTagName()比querySelectorAll()快100倍

  • document.querySelector():返回文档中匹配指定的CSS选择器的第一元素

    document.querySelector("#demo");

  • document.querySelectorAll():是 HTML5中引入的新方法,返回文档中匹配的CSS选择器的所有元素节点列表

    var x = document.querySelectorAll(".example");

节点指针(节点下的指定节点)

认识DOM文档的遍历指针以及如何获取根节点和body节点 / 《JavaScript高级程序设计》学习笔记(四)

知识点普及:Node类型

DOM1级定义了一个Node接口,该接口由DOM中的所有节点类型实现。这个Node接口在JavaScript中是作为Node类型实现。除了IE之外,在其他浏览器中都可以访问到这个类型。JavaScript中所有节点类型都继承自Node类型,因此所有节点类型都共享着相同的基本属性和方法

在DOM结构树中,每一个独立的DOM节点都定义了一系列的指针(节点属性),通过这些指针,我们可以遍历DOM结构中我们想找到的任何对象。如果指针指向的元素不存在,则对应指针的属性值为null

  • nodeName和nodeValue属性-------》【扩展到js原生事件委托机制(事件的捕获冒泡)】

    • 知识点普及

    用nodeName和nodeValue属性可以了解节点的具体信息,其值完全取决于节点的类型 nodeName中保存的始终都是元素的标签名,而nodeValue的值则始终为null

Javascript性能优化 - 事件委托 / JavaScript 事件委托详解 / 事件冒泡机制与委托机制 / JavaScript事件委托原理 / JavaScript事件代理和委托(Delegation)

  • 事件委托机制

    • 场景:

      • 元素过多时的性能问题

      ps:假设有一个多行多列的表格,我们想让用户单击每个单元格都能看到与其中内容相关的更多信息(比如,通过提示条)。为此,可以为每个单元格都绑定click事件.问题是,如果表格中要绑定单击事件的有10列500行,那么查找和遍历5000个单元格会导致脚本执行速度明显变慢,而保存5000个td元素和相应的事件处理程序也会占用大量内存

      • 动态添加元素的无法直接绑定事件

      ps:假设一个动态翻页的表格,每一次翻页都是重新插入的表格。通过给第一页添加事件,对后续通过js异步加载的页面没有作用

提示:使用事件委托时,如果注册到目标元素上的其他事件处理程序使用.stopPropagation()阻止了事件传播,那么事件委托就会失效。

捕获冒泡

  • 事件触发过程:现代浏览器(指 IE6-IE8 除外的浏览器,包括 IE9+、FireFox、Safari、Chrome 和 Opera 等)事件流包含三个过程,分别是捕获阶段、目标阶段和冒泡阶段 JavaScript 浏览器事件解读

    打算

    • 捕获阶段

    当我们对 DOM 元素进行操作时,比如鼠标点击、悬浮等,就会有一个事件传输到这个 DOM 元素,这个事件从 Window 开始,依次经过 docuemnt、html、body,再不断经过子节点直到到达目标元素,从 Window 到达目标元素父节点的过程称为捕获阶段,注意此时还未到达目标节点。

    • 目标阶段

    捕获阶段结束时,事件到达了目标节点的父节点,最终到达目标节点,并在目标节点上触发了这个事件,这就是目标阶段。

    • 冒泡阶段

    当事件到达目标节点之后,就会沿着原路返回,这个过程有点类似水泡从水底浮出水面的过程,所以称这个过程为冒泡阶段。

addEventListener(eventName, handler, useCapture) 函数。第三个参数是 useCapture,代表是否在捕获阶段进行事件处理, 如果是 false, 则在冒泡阶段进行事件处理,如果是 true,在捕获阶段进行事件处理,默认是 false。这么设计的主要原因是当年微软和 netscape 之间的浏览器战争打得火热,netscape 主张捕获方式,微软主张冒泡方式,W3C 采用了折中的方式,即先捕获再冒泡。(true 时表捕获阶段,false时为冒泡阶段)

    main.addEventListener("mousedown",function(e){ //e.target是鼠标经过的那个元素
            var nodename = e.target.nodeName.toLocaleLowerCase();   //标签转小写
            var classname=e.target.className;  //获取class
        if(e.target && nodename == "div" || nodename=="span") {
    if(classname=="head"||classname=="msg"||classname=="foot"||classname=="tip"|| nodename=="span"){
                    if(classname=="tip"){
                      ...........
                    }else if(nodename=="span"){
                      ........
                    }else{
                      ............  
                    }
                }
            }
        },true);
  • 事件委托是事件冒泡的一个应用,但有时候我们并不希望事件冒泡

    • event.stopPropagation():阻止冒泡事件,不让事件向documen上蔓延,但是默认事件任然会执行,当你掉这个方法的时候,如果点击一个链接,这个链接仍然会被打开;

      ps:但IE不支持,IE中提供的是,cancelBubble属性,默认为false,当它设置为true时,就是阻止事件冒泡

    • event.preventDefault():阻止默认事件,调用此方法是,链接不会被打开,但是会发生冒泡,冒泡会传递到上一层的父元素;

    • return false:这个方法比较暴力,他会同时阻止事件冒泡也会阻止默认事件;写上此代码,链接不会被打开,事件也不会传递到上一层的父元素,可以理解为 return false 就等于同时调用了event.stopPropagation()和event.preventDefault()。

  • 讲到冒泡=>其实可以扯到onmouseover/onmouseout 以及onmouseenter/onmouseleave

事件冒泡以及onmouseenter 和 onmouseover 的不同

  • 冒泡 onmouseover/onmouseout是有冒泡的

    onmouseenter/onmouseleave是没有冒泡的

    • mouseover

      • event.target —— 是鼠标经过的那个元素。
      • event.relatedTarget —— 是鼠标上一次经过的元素。
    • mouseout

      • event.target —— 是鼠标离开的元素。
      • event.relatedTarget —— 是当前指针位置下的(鼠标进入的)元素。
  • Node(节点)对应的5个指针

    1. parentNode:返回指定节点的父节点

    2. previousSibling:返回指定节点的上一个相邻节点

    3. nextSibling:返回指定节点的下一个相邻节点

    4. firstChild:返回指定元素的第一个子节点

    5. lastChild:返回指定元素的最后一个子节点

    • childNodes

      • test.firstChild=test.childNodes[0] 始终相等

      • test.lastChild=test.[test.childNodes.length-1] 始终相等

      • 在只有一个子节点的情况下,firstChild和lastChild指向同一个节点,如果没有子节点,firstChild和lastChild的值均为null。

        Snipaste_2019-08-15_22-56-29.png

操作节点

JavaScript常见原生DOM操作API总结 JavaScript操作DOM常用的API

  • 创建节点

    • createElement:传入指定的一个标签名来创建一个元素

          let elem = document.createElement("div");
          elem.id = 'test';
          elem.style = 'color: red';
          elem.innerHTML = '我是新创建的节点';
          document.body.appendChild(elem);
      
    • createTextNode:创建一个文本节点

        var node = document.createTextNode("我是文本节点");
        document.body.appendChild(node);
      
    • cloneNode:返回调用该方法的节点的一个副本

      1. 是否采用深度克隆,如果为true,则该节点的所有后代节点也都会被克隆,如果为false,则只克隆该节点本身

      2. 要调用appendChild方法才能添加到文档树中

      3. 如果复制的元素有id,则其副本同样会包含该id,由于id具有唯一性,所以在复制节点后必须要修改其id

      4. 调用接收的是否深度克隆参数最好传入,如果不传入该参数,不同浏览器对其默认值的处理可能不同

      5. 需要克隆的元素,事件都启用,最好使用事件委托机制

             var parent = document.getElementById("parent");
           document.getElementById("btnCopy").onclick = function(){
               var parent2 = parent.cloneNode(true);
               parent2.id = "parent2";
               document.body.appendChild(parent2);
           }
        
    • createDocumentFragment:创建一个 DocumentFragment ,也就是文档碎片,它表示一种轻量级的文档,主要是用来存储临时节点,大量操作DOM时用它可以大大提升性能。

                   (function()
                   {
                     var start = Date.now();
                     var str = '', li;
                     var ul = document.getElementById('ul');
                     var fragment = document.createDocumentFragment();
                     for(var i=0; i<1000; i++)
                     {
                         li = document.createElement('li');
                         li.textContent = '第'+(i+1)+'个子节点';
                         fragment.appendChild(li);
                     }
                     ul.appendChild(fragment);
                   })();
      
  • 插入节点

    • appendChild:前面已经用到多次,就是将指定的节点添加到调用该方法的节点的子元素的末尾。

    • insertBefore(newElement, referenceElement):用来添加一个节点到一个参照节点(referenceElement)之前

                    <div id="parent">
                        父节点
                        <div id="child">                
                            子元素
                        </div>
                    </div>
                    <input type="button" id="insertNode" value="插入节点" />
      
                    var parent = document.getElementById("parent");
                    var child = document.getElementById("child");
                    document.getElementById("insertNode").onclick = function(){
                        var newNode = document.createElement("div");
                        newNode.textContent = "新节点"
                        parent.insertBefore(newNode,child);
                    }
      
  • 替换节点 :

    • parent.replaceChild(newChild,oldChild)用于使用一个节点替换另一个节点【oldChild是被替换的节点】

                <body>
                  <div id="parent">
                    父节点
                    <div id="child">
                        子元素
                    </div>
                  </div>
                  <input type="button" id="insertNode" value="替换节点" />
                </body>
                <script>
                  var parent = document.getElementById("parent");
                  var child = document.getElementById("child");
                  document.getElementById("insertNode").onclick = function(){
                      var newNode = document.createElement("div");
                      newNode.textContent = "新节点"
                      parent.replaceChild(newNode,child)
                  }
      
  • 移除节点

    • removeChild:parent.removeChild(node)

    原生 JavaScript 的 DOM 操作汇总

    Snipaste_2019-08-15_23-56-43.png

回忆jquery的节点操作 jQuery DOM 操作(基本操作、内部插入、外部插入、包裹操作) / jQuery DOM节点的创建、插入、删除

属性操作

JavaScript操作DOM常用的API

  • element.setAttribute(name, value)

        let div1 = document.getElementById("div1"); 
        div1.setAttribute("align", "center");
    
  • element.getAttribute(attributeName)

       let div1 = document.getElementById("div1");
       let align = div1.getAttribute("align");
    
  • element.removeAttribute(attrName)

       let div = document.getElementById("div1")
       div.removeAttribute("style");
    

文本操作

暂了解,基本不使用除了前四种

s

---》获取CSS衍生出一个问题getComputedStyle()与currentStyle()、style()方法

  1. obj.style:这个方法只能JS只能获取写在html标签中的写在style属性中的值(style=”…”),而无法获取定义在<style type="text/css" 里面的属性

  2. IE中使用的是obj.currentStyle方法,而FF是用的是getComputedStyle 方法

解决兼容问题

function getStyle(element, attr) {
    if(element.currentStyle) {
            return element.currentStyle[attr];
    } else {
            return getComputedStyle(element, false)[attr];
    }
}

收集DOM操作的一些坑提及一些知识普及(建议观看内容)

JavaScript 操作 DOM 的那些坑 /// 建议看看在新窗口中打开页面?小心有坑! /// 前端性能优化之 DOM 篇 /// 前端性能优化之图片篇 /// 关于反爬虫(爬虫与反爬虫),看这一篇就够了 /// 浏览器特性与安全策略 /// 【干货】浅析浏览器安全 /// 如何应对网站反爬虫策略?如何高效地爬大量数据? /// 浅析最烦人的反爬虫手段 /// 反AdBlock屏蔽广告

事件(比较重要的)

  1. IE8不兼容addEvntListener,只能使用attachEvent()和detachEventListener()方法。IE从IE9开始支持addEventListener和removeEventListener. addEventListener/attachEvent兼容IE浏览器与标准浏览器

  2. 鼠标滚轮事件:mousewheel:》当用户使用鼠标滚轮与页面进行交互、在垂直方向上滚动页面时(无论上下),就会触发mousewheels事件,这个事件可以在任何元素上触发,最终会冒泡到document或者window上。

    1. 对应的event对象还有wheelDelta属性,值是120/-120的倍数

               document.addEventListener('mousewheel',function(event){  
                         console.log(event.wheelDelta)    
                 })
      
  3. 键盘事件

    Snipaste_2019-08-16_11-56-05.png
    Snipaste_2019-08-16_11-56-30.png

    • 键盘事件用来描述键盘行为,主要有keydown、keypress、keyup三个事件

    • 按下enter键

         function enterOperate(evt){
           if(evt.keyCode == 13){
             alert("按下enter键");
           }
         }
         document.body.addEventListener("keydown", enterOperate)
      
    • 组合按键

            function combinationKey(evt){
          if( evt.shiftKey && evt.keyCode == 73){
            alert(“触发了shift + i组合键”);
          }
        }
      
  4. 移动端的事件:移动端WEB开发,click,touch,tap事件浅析 移动端js触摸touch详解

程序员发展到一定的水平后,瓶颈并不在技术水平上,而是在表达能力上


涉及到点击穿透的问题【web开发经典问题】(面试题)点击穿透

Snipaste_2019-08-16_13-04-59.png

用户点击事件之后,浏览器会等待300ms后,如果没有(再次的点击行为)【双击】行为,再去执行click事件

浏览器等待约 300ms 的原因是,判断用户是否是双击(double tap)行为,双击过程中就不适合触发 click 事件了 为了减少这300ms的延迟,tap事件被很多框架(如zepto)封装,来减少这延迟问题, tap事件不是原生的,所以是封装的

移动端tap与click的区别 && 点透事件

fastclick库github


节流防抖衍生知识链(面试点)

  • 阐述问题

上面说到点击问题,其实可以再扯到节流与防抖 面试准备 - JS 防抖与节流

讲到节流防抖---拉扯到回流重绘的问题

最后说到整个浏览器从输入url过程:DNS解析>tcp三次握手>tcp四次挥手>浏览器的渲染页面

  • 解决说明问题【倒序解决】

    • 输入url之后发生了什么 参考浏览器输入 URL 后发生了什么?

      • DNS解析 :( Domain Name System)是“域名系统”的英文缩写,它用于TCP/IP网络,它所提供的服务是用来将主机名和域名转换为IP地址的工作。

      • tcp三次握手:

        ss

        • 专业名词

          • SYN 表示SYN报文(在建立TCP连接的时候使用)
          • ACK 表示Acknowledgment Number字段有意义
          • FIN 表示没有数据需要发送了(在关闭TCP连接的时候使用)
        • 过程

          • 第一次握手:状态【两端都处于CLOSED关闭状态】,客户端(Client)将SYN的数据包【SYN=1,随机产生一个值seq=x】发送给服务端(Server),客户端(Client)处于SYN-SENT,等到服务端确认

          • 第二次握手:服务端(Server)接收到数据包后,得知客户端请求建立连接,服务端将SYN与ACK【SYN=1,ACK=1,ack = x + 1,随机产生一个值 seq = y】)的数据包发回给客户端(Client),服务端进入SYN-RCVD状态,操作系统为该 TCP 连接分配 TCP 缓存和变量

          • 第三次握手:客户端(Client)收到确认后,检查原先自己的seq是否等于x+1,ACK是否为1,如果没问题,将ACK数据包【ack = y + 1】丢回服务端(server),Server 检查 ack 是否为 y + 1,ACK 是否为 1,如果正确则连接建立成功。状态【两端都处于established 状态】,完成握手,随后 Client 和 Server 就可以开始传输数据。

      • TCP四次挥手

        ss
        看懂上面的然后这里简单说:

        • 第一次挥手:客户端发送FIN【FIN=1,seq=u】数据包,停止再发送数据,主动关闭 TCP 连接,进入 FIN-WAIT-1(终止等待1)状态

        • 第二次挥手:服务端接收到FIN数据包报文后,就会发出确认报文段ACK数据包【ACK = 1,确认号 ack = u + 1,序号 seq = v】,Server 进入 CLOSE-WAIT(关闭等待)状态,此时的 TCP 处于半关闭状态,Client 到 Server 的连接释放。 ----------------------------------------------------------》数据发送完毕

        • 第三次挥手:Server 已经没有要向 Client 发出的数据了,Server 发出连接释放报文段(数据包)FIN与ACK(FIN = 1,ACK = 1,序号 seq = w,确认号 ack = u + 1),Server 进入 LAST-ACK(最后确认)状态,等待 Client 的确认。

        • 第四次挥手:Client 收到 Server 的连接释放报文段后,对此发出确认报文段(ACK = 1,seq = u + 1,ack = w + 1),Client 进入 TIME-WAIT(时间等待)状态。此时 TCP 未释放掉,需要经过时间等待计时器设置的时间 2MSL 后,Client 才进入 CLOSED 状态。

        • 四次挥手的详述 Client端发起中断连接请求,也就是发送FIN报文。Server端接到FIN报文后,意思是说"我Client端没有数据要发给你了",但是如果你还有数据没有发送完成,则不必急着关闭Socket,可以继续发送数据。所以你先发送ACK,"告诉Client端,你的请求我收到了,但是我还没准备好,请继续你等我的消息"。这个时候Client端就进入FIN_WAIT状态,继续等待Server端的FIN报文。当Server端确定数据已发送完成,则向Client端发送FIN报文,"告诉Client端,好了,我这边数据发完了,准备好关闭连接了"。Client端收到FIN报文后,"就知道可以关闭连接了,但是他还是不相信网络,怕Server端不知道要关闭,所以发送ACK后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传。“,Server端收到ACK后,"就知道可以断开连接了"。Client端等待了2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,我Client端也可以关闭连接了。Ok,TCP连接就这样关闭了

      • 浏览器的渲染:【干货】十分钟读懂浏览器渲染流程 /// 浏览器渲染页面过程与页面优化

    • 回流与重绘:重点:浏览器的回流与重绘 (Reflow & Repaint) /// 你真的了解回流和重绘吗

  • 回到最初的问题:节流与防抖(这是俩种解决方案:解决的问题是:频繁的点击事件)

    • 节流:指定一段时间内只执行一次操作

                    节流函数体
                    function throttle(fn) {
                      // 4、通过闭包保存一个标记canRun
                      let canRun = true;
                      return function() {
                        // 5、在函数开头判断标志是否为 true,不为 true 则中断函数
                        //作用执行函数canRun为false,然后一段时间后才变成true
                        if(!canRun) {
                          return;
                        }
                        // 6、将 canRun 设置为 false,防止执行之前再被执行
                        canRun = false;
                        // 7、定时器
                        setTimeout( () => {
                          fn.call(this, arguments); //将不确定变量替换到函数中了
                          // 8、执行完事件(比如调用完接口)之后,重新将这个标志设置为 true
                          canRun = true;
                        }, 1000);
                      };
      
      
                         // 3、需要节流的事件
                        function sayThrottle() {
                          console.log("节流成功!");
                        }
      
      • 引用场景

    懒加载要监听计算滚动条的位置,使用节流按一定时间的频率获取。 用户点击提交按钮,假设我们知道接口大致的返回时间的情况下,我们使用节流,只允许一定时间内点击一次。

    • 防抖函数:俩次触发间隔超过指定时间间隔

        window.onload = function() {
             // 1、获取这个按钮,并绑定事件
             var myDebounce = document.getElementById("debounce");
             myDebounce.addEventListener("click", debounce(sayDebounce));
           }
      
           // 2、防抖功能函数,接受传参
           function debounce(fn) {
             // 4、创建一个标记用来存放定时器的返回值
             let timeout = null;
             return function() {
               // 5、每次当用户点击/输入的时候,把前一个定时器清除
               clearTimeout(timeout);
               // 6、然后创建一个新的 setTimeout,
               // 这样就能保证点击按钮后的 interval 间隔内
               // 如果用户还点击了的话,就不会执行 fn 函数
               timeout = setTimeout(() => {
                 fn.call(this, arguments);
               }, 1000);
             };
           }
      
           // 3、需要进行防抖的事件处理
           function sayDebounce() {
             // ... 有些需要防抖的工作,在这里执行
             console.log("防抖成功!");
           }
      

输入URL发生了什么=参考文章

DNS解析:DNS解析的过程是什么,求详细的? /// DNS解析过程详解

TCP三次握手以及TCP四次挥手 TCP三次握手与四次挥手 /// 注意!是TCP不是HTTP的3次握手与4次挥手(#...#)

浏览器的渲染:【干货】十分钟读懂浏览器渲染流程

因js部分篇幅过于长,因此分成二部分