# 二.JS 高级

# 1.路由原理

路由的本质是监听 URL 的变化,然后匹配路由规则,显示相应的页面,并且无需刷新页面

  • hash 模式:通过 hashchange 事件来监听 URL 的变化,从而实现页面跳转,而服务端接受到的 URL 永远都没有 hash 后缀
  • history 模式:通过 pushState 和 replaceState 改变 URL;通过 History 模式改变 URL 同样不会引起页面的刷新,只会更新浏览器的历史记录;后退会触发 popState 事件。#### 对比
  • hash 模式只可以更改#后面的内容,history 模式可以通过 API 设置任意的同源 URL
  • history 模式可以通过 API 添加任意类型的数据到历史记录中,hash 模式只能更改哈希值,也就是字符串

# 2.ajax(异步的 javascript 和 xml)

Ajax 是一种用于创建快速动态网页的技术。Ajax 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。

传统的网页(不使用 Ajax)如果需要更新内容,必须重载整个网页页面

  • AJAX 的工作原理:

    • 0 (未初始化)还没有调用 send()方法
    • 1 (载入)已调用 send()方法,正在发送请求
    • 2 (载入完成)send()方法执行完毕
    • 3 (交互)正在解析相应的内容
    • 4 (完成)响应内容解析完成,可以在客户端调用了
  • 优点:

    • 1.减轻服务器的负担,按需取数据,最大程度的减少冗余请求
    • 2.局部刷新页面,减少用户心理和实际的等待时间,带来更好的用户体验
    • 3.基于 xml 标准化,并被广泛支持,不需安装插件等,进一步促进页面和数据的分离
  • 劣势:

    • 1.AJAX 大量的使用了 javascript 和 ajax 引擎,这些取决于浏览器的支持.在编写的时候考虑对浏览器的兼容性.
    • 2.AJAX 只是局部刷新,所以页面的后退按钮是没有用的.
    • 3.对流媒体还有移动设备的支持不是太好等
      • 怎么解决呢:通过 location.hash 值来解决 ajax 过程中导致的浏览器前进后退按钮失效问题
      • 怎么解决以前被人们常遇到的重复加载问题。主要比较前后的 hash 值,看其是否相等,再判断是否触发 ajax
    let getDate = (method, url, content) = >
    new Promise((resolve, reject) = > {
      let xhr = new XMLHttpRequest()
      xhr.open(method, url)
      xhr.send(content)
      xhr.onreadystatechange = () = > {
        if (xhr.readyState === 4 && xhr.status === 200) {
          resolve(xhr.response)
        }
      }
    })
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

# 3.异步加载和延迟加载

  • 1.异步加载的方案: 动态插入 script 标签
  • 2.通过 ajax 去获取 js 代码,然后通过 eval 执行
  • 3.script 标签上添加 defer 或者 async 属性
  • 4.创建并插入 iframe,让它异步执行 js
  • 5.延迟加载:有些 js 代码并不是页面初始化的时候就立刻需要的,而稍后的某些情况才需要的。

# 7.作用域

  • 一套管理引擎如何在当前作用域以及嵌套的子作用域中根据标识符名称(标识符就是变量或者函数名)查找变量的规则;
  • 只有全局作用域和局部作用域(es6 中加入块级作用域),作用域在他创建的时候就存在了
  • 什么是作用域链
    • 由当前环境与上一层环境的一系列变量对象组成,保证当前执行环境里有权访问的变量和函数是有序的,作用域变量只能被向上访问
    • 变量访问到 window 对象即被终止,作用域链向下访问是不被允许的
  • 改变作用方式有
    • 变量访问到 window 对象即被终止,作用域链向下访问时不允许的 1.改变作用域由 with try...中 catch 2.所有格未定义的直接赋值的变量自动声明为全局作用域。
  • 代码执行分为两个阶段:
    • 代码编译阶段:
      • 由编译器完成,将代码翻译成可执行的代码,这个阶段会被确定
    • 代码执行阶段:
      • 由 js 引擎完成,主要执行科执行的代码,这个阶段执行上下文被创建(对象被创建)
    • 执行上下文:
      • 一个看不见的对象,存在若干个属性和变量,它被调用的时候创建的。函数被调用查看 this 指向 object,object 就是上下文(只有被调用的时候创建)
  • 作用域链

当代码在一个环境中执行时,会创建变量对象的一个作用域链

var name = "Tom"
function say() {
  alert("hi," + name)
}
say() //hi, Tom
1
2
3
4
5

函数 say()的执行环境为全局环境,所以他的变量对象为 window。当函数执行到 name 时,先查找局部环境,找到则返回局部环境的 name,否则顺着作用域查找,在全局环境中找到 name 返回,这一查找变量的有序过程的依据就是作用域。

作用域链是保证执行环境有权访问的所有变量和函数的有序访问

当代码在一个环境中执行是,会创建变量对象的一个作用域链

# 8.事件相关

js 自定义事件实现

  • createEvent,设置事件类型,是 html 事件还是鼠标事件
  • initEvent 事件初始化,事件名称,是否允许冒泡,是否阻止自定义事件
  • dispatchEvent 触发事件

前端事件流

事件流描述的是从页面中接受事件的顺序,事件捕获阶段处于目标阶段;事件冒泡阶段 addevenListener 最后这个布尔值参数如果是 true,表示在捕获阶段调用事件处理程序;如果是 false,表示在冒泡阶段调用事件处理程序。

  • 捕获阶段:

实际目标 div 在捕获阶段不会接受事件,也就是在捕获阶段,事件从 document 到 html 再到 body 就停止了

  • 目标阶段:

事件在 div 发生并处理,但是事件处理会被看成是冒泡阶段的一部分。

  • 冒泡阶段:

事件又传播回文档

阻止冒泡事件:event.stopPropagation()

function stopBubble(e) {
  if (e && e.stopPropagation) {
    e.stopPropagation()
  } else {
    //ie
    window.event.cancelBubble = true
  }
}
1
2
3
4
5
6
7
8

阻止默认行为:event.preventDefault()

function stopDefault(e) {
  if (e && e.preventDefault) {
    e.preventDefault()
  } else {
    //ie
    window.event.returnValue = false
  }
}
1
2
3
4
5
6
7
8

事件如何先冒泡后捕获

在 dom 标准事件模型中,是先捕获后冒泡。但是如果要实现先冒泡后捕获的效果,对于同一个事件,监听捕获和冒泡,分别对应相应的处理函数,监听到捕获事件,先暂缓执行,直到冒泡事件被捕获后在执行捕获事件。

那些事件不支持冒泡事件:

  • 鼠标事件:mouserleave mouseenter
  • 焦点事件:blur focus
  • UI 事件:scroll resize

事件委托(提高性能)

不在事件的(直接在 dom)上设置监听函数,而是在其父元素上设置监听函数。通过事件冒泡,父元素可以监听到子元素上事件的触发。

  • 目的

    • 通过动态绑定事件,新添加的子元素也会监听函数,减少内存消耗,提高性能
  • 写法

    • 通过判断事件发生元素 dom 的类型,来做出不同的响应。
    • 举例:最经典的就是 ul 和 li 标签的事件监听,比如我们在添加事件的时候,采用事件委托机制,不会在 li 标签上直接添加,而是在 ul 父元素上添加

输入框 change 和 input 事件

  • onchange 事件: 要在 input 失去焦点的时候才触发
  • oninput 事件: 要在用户输入时触发,他是元素之发生变化时立即触发

# 13.javascript 对象的几种创建方式

  • 工厂模式
  • 构造函数模式
  • 原型模式
  • 混合构造函数和原型模式
  • 动态原型模式
  • 寄生构造函数模式
  • 稳妥构造函数模式

# 14.js 异步加载的方式

  • 渲染引擎遇到 script 标签会停下来,等到执行完脚本,继续向下渲染
  • defer 是“渲染完再执行”,async 是“下载完就执行”,defer 如果有多个脚本,会按照在页面中出现的顺序加载,多个 async 脚本不能保证加载顺序
  • 加载 es6 模块的时候设置 type=module,异步加载不会造成阻塞浏览器,页面渲染完再执行,可以同时加上 async 属性,异步执行脚本(利用顶层的 this 等于 undefined 这个语法点,可以侦测当前代码是否在 ES6 模块之中)

# 16.高阶函数

  • 函数作为参数传递,抽离出一部分容易变化的业务逻辑,把这部分业务逻辑放在函数参数中。这样一来可以分离业务代码中变化与不变的部分

  • 函数作为返回值传递

# 17.undefined 和 null NaN

undefined 类型只要一个,即 undefined,当声明变量还未被初始化时就是 undefined

unll 类型也只有一个值,即 null null 用来表示尚未存在的对象,常用来表示函数企图返回一个不存在的对象

NaN 与任何值都是比较结果都是 false

undefined 是一个表示"无"的原始值,转为数值时为   当声明的变量还未被初始化时,变量的默认值为 null 用来表示尚未存在的对象,常用来表示函数企图返回一个不存在的对象。

(1)变量被声明了,但没有赋值时,就等于 undefined。 (2) 调用函数时,应该提供的参数没有提供,该参数等于 undefined。 (3)对象没有赋值的属性,该属性的值为 undefined。 (4)函数没有返回值时,默认返回 undefined。

(1) 作为函数的参数,表示该函数的参数不是对象。 (2) 作为对象原型链的终点。

# 19.函数参数传递

按共享传递

var person = { a: 111 }
function change(obj) {
  obj = new Object()
  obj.a = 222
  return obj
}
1
2
3
4
5
6

# 20.函数柯里化

  • 概念:
    • 一个函数接受函数 A 作为参数,运行后返回一个新的函数,并且可以处理 A 中的参数(只接受一个参数的函数)
  • 意义:
    • 将函数完全变成了接受一个参数,返回一个参数的固定形式,便于讨论和优化

# 21.TypeScript 的优点

  • 编译时的强类型,变成了强类型语言,还是编译成 js,编译的时候可以检验
  • 更好的模块化
  • 更好的实现面向对象的编程,类、接口、模块

# 22.js 的阻塞特性

  • 所有浏览器在下载 js 的时候,会阻一切其他活动,比如其他资源的下载,内容的程序等等;直到 js 下载、解析、执行完毕后才开始继续并下载其他资源并呈现内容。为了提高用户体验,新一代浏览器都支持并行下载 js,但是 js 下载任然会阻塞其他资源的下载(图片,css)
  • css 阻塞:因为浏览器会维持 thml 中的 css 和 js 的顺序,样式表必须在嵌入的 js 执行前先加载、解析完。而嵌入的 js 会阻塞后面的资源加载,所以就会出现上面的 css 阻塞下载的情况

# 24.splice 和 slice;map 和 forEach、filter、reduce 的区别

# 1.slice(start,end):

方法可以从已有数组中返回选定的元素,返回一个新数组,包含从 start 和 end(不包含该元素)的数组方法

# 2.splice:

该方法向或者从从数组中添加或删除项目,返回被删除的项目。(该方法会改变原数组)

  • splice(index,howmany,iteml,...itemx)
  • index 参数;必须,整数规定添加或者删除的位置,使用负数,从数组尾部规定位置
  • howmany 参数:必须,要删除的数量
  • iteml..itemx:可选,向数组添加新项目

# 3.map():

会返回一个全新的数组。使用于改变数据值的时候。会分配内存存储空间数组并返回,forEach()不会返回数据

# 4.forEach():

不会返回任何有价值的东西,并且不打算改变数据,单纯的只是想用数据做一些事情,他允许 callback 更改原始数组的元素

# 5.reduce():

方法接受一个函数作为累加器,数组中的每一个值(从左到右)开始缩减,最终计算一个值,不会改变原数组的值

# 6.filter():

方法创建一个新数组,新数组中的元素是通过检查指定数组中符合条件的所有元素,它里面通过 function 去做处理

# 7. 在 JavaScript 中,用得较多的之一无疑是数组操作,这里过一遍数组的一些用法

map: 遍历数组,返回回调返回值组成的新数组
forEach: 无法break,可以用try/catch中throw new Error来停止
filter: 过滤
some: 有一项返回true,则整体为true
every: 有一项返回false,则整体为false
join: 通过指定连接符生成字符串
push / pop: 末尾推入和弹出,改变原数组, 返回推入/弹出项【有误】
unshift / shift: 头部推入和弹出,改变原数组,返回操作项【有误】
sort(fn) / reverse: 排序与反转,改变原数组
concat: 连接数组,不影响原数组, 浅拷贝
slice(start, end): 返回截断后的新数组,不改变原数组
splice(start, number, value...): 返回删除元素组成的数组,value 为插入项,改变原数组
indexOf / lastIndexOf(value, fromIndex): 查找数组项,返回对应的下标
reduce / reduceRight(fn(prev, cur), defaultPrev): 两两执行,prev 为上次化简函数的return值,cur 为当前值(从第二项开始)
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 25.移动端 300ms 延迟

  • 由来:300 毫秒延迟解决的是双击缩放
    • 双击缩放,手指在屏幕快速点击两次,safari 浏览器就会将网页缩放原始比列
    • 由于用户可以双击缩放或者是滚动操作,当用户点击屏幕一次之后,浏览器并不会判断用户确实要打开这个链接,还是想要进行双击操作
    • 因此,safair 浏览器就会等待 300ms,来判断用户是否在次点击屏幕
  • 解决方案
    • 1.禁止缩放,设置 meta 标签 user-scalable=no
    • 2.fastclick.js
      • 原理:FastClick 的实现原理是在检查到 touchend 事件的时候,会通过 dom 自定义事件立即发出 click 事件,并把浏览器在 300ms 之后真正 click 事件阻止掉
      • fastclick.js 还可以解决穿透问题

# 26.介绍下 Set、Map、WeakSet 和 WeakMap 的区别

  • Set
    • 成员的值都是唯一的,没有重复的值
    • 两个属性 constructor,size
    • 可以遍历
    • 四个方法:add,delete,has,clear
  • weakSet
    • 不重复的值的集合
    • 成员只能是对象,而不能是其他类型的值
    • 没有 size 属性,不能遍历
    • 三个方法 add,delete,has
  • Map
    • javascript 的对象,本质上是键值对的集合
    • 但是传统上只能用字符串当做键
    • 可以遍历,方法很多,可以干根各种数据格式转换
  • weakMap
    • 只接受对象作为键名
    • 键名指向的对象,不计入垃圾回收机制
    • 不能遍历,方法同 get,set,has,delete

# 27.document load / ready

  • 共同点:
    • 这两种事件都代表的是页面文档加载时触发。
  • 异同点:
    • ready 事件的触发,表示文档结构已经加载完成(不包含图片等非文字媒体文件)。
    • onload 事件的触发,表示页面包含图片等文件在内的所有元素都加载完成。

# 28.模块化

  • 基本概念:
    • 1.在 js 中,一个模块就是实现特定功能的文件(js)文件
    • 2.遵循模块的机制,想要什么就加载什么模块
    • 3.模块化开发需要遵循规范
  • js 实现模块化规范
    • 1.AMD 浏览器 requirejs 模块被异步加载,模块加载不影响后面语句的运行,默认使用 baseURL+paths 的路径解析方式
    • 2.CommonJS nodejs
    • 3.ES6 的 import/export
    • 4.CMD 浏览器端
  • 解决的问题:
    • 1.命名冲突
    • 2.文件依赖
    • 3.模块的复用
    • 4.统一规范和开发方式

# 29.DOM 操作

(1)创建新节点

  createDocumentFragment()    //创建一个DOM片段
  createElement()   //创建一个具体的元素
  createTextNode()   //创建一个文本节点

(2)添加、移除、替换、插入

  appendChild()
  removeChild()
  replaceChild()
  insertBefore() //在已有的子节点前插入一个新的子节点

(3)查找

  getElementsByTagName()    //通过标签名称
  getElementsByName()    //通过元素的Name属性的值(IE容错能力较强,会得到一个数组,其中包括id等于name值的)
  getElementById()    //通过元素Id,唯一性

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。 它是基于 JavaScript 的一个子集。数据格式简单, 易于读写, 占用带宽小 {'age':'12', 'name':'back'}

# 4.为什么 0.1+0.2!=0.3

# 5.垃圾回收机制

# 6.新生代算法

# 7.老生代算法

上次更新: 2022/6/29 上午12:09:44