JS 突然想起的知识点汇总

记录一些突然想起来/容易混淆/难记的知识点

箭头函数和普通函数的区别

参考:ES6扩展 函数扩展 箭头函数”箭头函数和普通函数的区别”

  1. this指向父级作用域的this。
  2. 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。(可以拿到event函数)
  3. 没有arguments对象,如要使用可在箭头函数中用剩余参数...将传入的参数聚合成数组:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 箭头函数中调用arguments报错
    const arg=()=>{
    console.log(arguments);
    }
    arg(1,2,3);//报错:arguments is not defined at arg

    // 可以使用 剩余参数运算符 代替arguments去拿传入的参数
    const arg=(...args)=>{
    console.log(args);
    }
    arg(1,2,3);//[1, 2, 3]

箭头函数作用

  • 参考:ES6扩展 函数扩展 箭头函数”回调函数中的this”
  • 解决问题:回调函数中的this默认是指向windows
  • 解决方法
    • 方法1:在使用回调函数之前利用闭包的特性在外层函数中使用一个变量_this保存this,然后在回调函数中获取父级作用域的_this(例子)
    • 方法2:回调函数改为箭头函数,这样this取到外层的this,即我们需要的对象(例子

存储

cookie有效期

已实验过cookie如果不设置过期时间,浏览器关闭就会消失。

浏览器缓存 与 http缓存

  • 参考前端基础-HTTP HTML 浏览器(2)”说一下浏览器缓存”
  • 注意:http缓存也是在客户端缓存的
  • 前端缓存:
    • 浏览器缓存:
      • Cookie
      • SessionStorage
      • LoacalStorage
    • http缓存:
      • 强缓存:
        • Cache-Control:相对时间
        • Expires:基于服务器端的绝对时间
      • 协商缓存:
        • Last-Modified(响应头)与If-Modified-Since(请求头):资源的最后修改时间
        • ETag(响应头)与If-None-Match(请求头):资源的唯一标识(一个字符串,类似指纹),由内容生成

const定义引用类型 属性(值)可变

JS基础知识面试题(1)中

手写简易jquery+手写原型链继承(html是jquery的方法吧?)都要注意使用this

for-in与for-of

  • in 运算符:
    • 如果指定的 属性 在指定的对象或其原型链中,则in 运算符返回true。
    • 如果指定的 数组索引数组中,则in 运算符返回true。
  • for-infor-of可参考《ES6扩展 对象扩展》

浅拷贝与深拷贝

Promise的then函数返回值

字符串、数组的常用方法【待记忆】

字符串独有的

数组独有的

总结(是否修改原数组)


基础知识面试题3中“异步队列依次执行”

  • 参考 JS基础知识面试题(3)
  • 思路:队列 先进先出,Promise then,封装函数
    • 方法1: Promise.resolve() + arr.forEach + PromiseObj.then(fn)
    • 方法2: async+for-of+await PromiseObj

预加载的实现方法

  • 懒加载 可参考 JS基础知识面试题(3)预加载 慕课预加载 实现方法
  • 预加载(异步加载):提前加载图片,当用户需要查看时可直接从本地缓存中渲染。
    • 例子: loading页面显示时就是正在提前加载众多图片的时候,等待切换到下一个页面时众多图片已经加载完毕。
    • 实现方法
    1. 显示loading页面时,将需要加载的图片src放入数组中
    2. 图片的load和error事件函数中就可以放进度条的进度计算遍历图片数组加载图片(赋值给image对象的src)(error中也是正常进度,不然error就卡住永远无法加载结束)
    3. 图片数组遍历加载结束,隐藏loading页,正常显示网页,此时所有图片已加载过一次,用户查看时会直接从本地读取

JS Web API 中

通用的事件监听函数(事件代理)

可以同时监听 普通绑定 和 代理绑定 的通用事件监听函数通用的事件监听函数普通绑定时this就指向触发事件的DOM元素事件代理时就需要事件对象.target来获取触发事件的DOM元素

手写ajax

手写简易ajax
运行结果

模拟404情况,换成不存在的地址:
模拟404情况
运行结果


JS Web API AJAX中

  • 同源和跨域可以看这篇帖子,写的非常简洁易懂

同源

为什么要有同源与不同源?

  1. 出于安全考虑,浏览器不允许,页面向不同源的接口请求数据,因为如果 接口 和 网页不同源,浏览器认为是2个不同的 服务器
  2. 不同的服务器中内容是不可控的,不允许访问了
    总结说人话: 浏览器为了保护你的电脑安全
    举个栗子: 你去肯德基店里点餐,店员只允许你点肯德基的产品(炸鸡,可乐,上校鸡块),如果此时你在肯德基店里面点麦当劳的产品,浏览器会认为你是坏人,就会让保安把你赶出去
  • 同源策略:ajax请求时,浏览器要求当前网页和server必须同源(为了安全)
    • 注意:同源是浏览器规定的,在服务端是可以发起跨域攻击的,比如爬虫,因为server端并无同源策略
  • 同源协议、域名、端口,三者必须一致
    • 比如:前端http://a.com:8080/;server:https://b.com/api/xxx(默认443端口)那么这前后端是三者都不一致
  • 同源策略限制了跨域时的以下行为
    • Cookie、LocalStorage、sessionStorage 和 IndexDB 跨域无法读取,只有同源才能获取他们
    • DOM 和 JS 对象跨域无法获取
    • Ajax请求跨域时无效,可发送,但浏览器会拒绝接受响应

跨域的使用场景

  • 跨域,简单来说就是浏览器使用 ajax请求 时,如果请求的 接口地址 和 当前打开的页面 地址 不同源
  • 跨域问题浏览器对于ajax请求的一种安全限制:一个页面发起的ajax请求,只能是于当前页同源的路径,这能有效的阻止跨站攻击。
  • 出现跨域问题的前提:
    • ajax: 浏览器只有使用ajax发送请求才会出现跨域,href属性与src属性不会出现跨域
      • 接口地址: ajax请求的url
      • 打开的页面:当前页面的window.location.href
      • 不同源: 浏览器使用ajax,向不同源的接口发送请求,称之为 跨域访问
  • 注意:
    • 所有的跨域,都必须经过server端允许和配合(包括前端jsonp)
    • 未经server端允许就实现跨域,说明浏览器有漏洞,危险信号
    • 具体配合可参考[《JS Web API AJAX》]
  • 加载img CSS js可无视同源策略:
    • 比如我们经常引用的js/CSS的库<link/><script>直接使用cdn地址就是跨域的。
    • <script>可实现JSONP
    • 而图片比较特殊,如果该图片做了防盗链,则无法显示(比如百度)
    • <img/>可用于使用第三方统计服务来统计打点(比如统计访问次数),也就是把需要的参数放入img,在地址处使用第三方统计服务,这样就可以避免跨域问题
      1
      2
      3
      <img src=跨域的图片地址/>
      <link href=跨域的css地址/>
      <script src=跨域的js地址> </script>
  • 开发环境中,前后端联调使用nginx反向代理proxy反向代理 根据url派发不同的端口,把跨域变为不跨域,使Ajax请求变成同源的从而传递数据
    • 例子1:假设前端页面是3000端口,后端页面是8000端口。那么用户打开页面时打开的就是3000端口的页面,前端页面中使用ajax请求获取数据时就会去获取3000端口下的对应路径的数据。而后端页面是8000端口,所以数据其实保存在8000端口下的对应路径中。这就无法获取数据。此时使用nginx进行反向代理,遇到/api/开头的url时就分配到8000端口,遇到/开头的url时就分配到3000端口,这样就没问题了
    • 例子2:
      • 开发环境中,假设前端服务是http://localhost:8089/,后端接口是存在http://test01.7debao.com/服务上的
      • 那么前端打开页面时打开的就是http://localhost:8089/,前端页面中使用ajax请求获取数据时就会去获取http://localhost:8089/下的对应路径的数据
      • 而后端接口是存在http://test01.7debao.com/服务上的,所以数据其实保存在http://test01.7debao.com/下的对应路径中,这就导致无法获取数据
      • 此时使用proxy进行反向代理,结合axios配置给接口加d baseUrl/api,遇到以 /api 开头的URL请求会被代理分配到后端服务,其他以 / 开头的URL请求会被前端服务处理显示页面或返回404错误
      • 注意: 生产和测试环境下,我开发的项目前后端代码应该是部署在同一域名下,接口请求不存在跨域问题,所以不需要设置反向代理,具体是运维在云服务上配的,我只能看到没有处理接口,连axios的baseUrl都没设置,看起来是同源直接请求

跨域的实现方式

  • 注意:如果是协议和端口造成的跨域问题,“前端”无法解决。
  • 前提:所有的跨域,都必须经过server端允许和配合(包括前端jsonp)
  • 对于跨域请求,一般首选使用CORS(跨域资源共享),因为它是一种标准、通用且相对简单的解决方案(jsonp就复杂多了),适用于大多数跨域问题。只有在特定情况下才需要考虑使用反向代理
  • 可通过jsonp、CORS、反向代理实现跨域
    1. jsonp原理:可参考博客JS Web API AJAX
      • 曾经的跨域杀手,专治各种跨域问题。现在慢慢的淡出历史舞台

      • 由于存在一些安全风险和功能上的限制,以及现代浏览器支持其他更强大、更安全的跨域解决方案,JSONP的使用已经减少
    2. CORS(纯服务端):可参考博客JS Web API AJAX
      • 目前的主流方案,也是最简单的方案。在实际工作中由后端人员来实现,前端不需要做任何事情

    3. WebSocket
    4. nginx反向代理:可参考博客项目登录(前端联调)nginx反向代理
    5. proxy反向代理 : 可参考博客axios使用和简单封装
  • 如何选择跨域的实现方式可参考博客[JS Web API AJAX]

防抖 节流

  • 防抖:一段时间内重复触发,以最后一次触发事件为基准进行计算,n秒内未触发才执行函数,在此之前的调用都会被忽略
    • 监听一个输入框的文字变化后触发change事件,直接用keyup事件,则会频发触发change事件。如果使用防抖,则用户输入结束或暂停时,才会触发change事件。(防抖不仅限于change事件)
    • 对某个按钮进行高频操作时,防止连续点击
    • 例子
  • 节流:以第一次触发事件为准,在指定时间内最多执行一次,多余的调用都会被忽略
    • 拖拽一个元素时,要随时拿到该元素被拖拽的位置。这里直接用drag事件则会频发触发,很容易导致卡顿。而使用节流,则无论拖拽速度多快,都会每隔100ms触发一次(时间自定义)
      • 注意:如果用防抖的话,就会出现卡顿的感觉,因为只在停止的时候执行了一次,这个时候就应该用节流,在一定时间内多次执行,会流畅很多(具体原因可见上方“防抖”中“防抖与节流的区别”)
    • 轮询请求时也可用,比如批量处理大量数据时,后端需要长时间处理,前端需要动态展示处理结果,则可规定每500ms发送一次请求
      • 如果不使用节流,在请求结束后再次调用请求一直到数据全部处理完成,则会出现连续疯狂请求的情况
    • 例子

3种弹框

  • 菜鸟教程
  • 警告框:alert("sometext");
  • 确认框:confirm("sometext");
    1
    2
    3
    4
    5
    6
    7
    8
    9
    var r=confirm("按下按钮");
    if (r==true)
    {
    x="你按下了\"确定\"按钮!";
    }
    else
    {
    x="你按下了\"取消\"按钮!";
    }
  • 提示框:prompt("sometext","defaultvalue");当提示框出现后,用户需要输入某个值,然后点击确认或取消按钮才能继续操纵。
    • 如果用户点击确认,那么返回值为输入的值
    • 如果用户点击取消,那么返回值为 null
      1
      2
      3
      4
      5
      6
      var person=prompt("请输入你的名字","Harry Potter");
      if (person!=null && person!="")
      {
      x="你好 " + person + "! 今天感觉如何?";
      document.getElementById("demo").innerHTML=x;
      }

组件还是类?如何选择

  • 封装组件的情况:
    • 可重用性需求: 如果你的功能是可复用的,而且在应用程序的多个地方都可能被使用,封装成组件是一个很好的选择。Vue.js、React 和 Angular 等前端框架都鼓励组件化开发。
    • UI 组件: 当你的功能包含一组相关的 UI 元素、样式和行为时,封装成组件可以更好地组织代码。这有助于提高代码的可维护性和可重用性。
    • 独立性: 如果你的功能是独立的,有自己的状态、行为和样式,且不依赖于外部环境,那么封装成组件可以使它更加独立和可测试。
    • 框架要求: 如果你使用的前端框架或库是组件化导向的,比如 Vue.js 或 React,那么封装成组件符合框架的设计理念,并能更好地利用框架的特性。
  • 封装成的情况:
    • 面向对象编程: 如果你更喜欢使用面向对象的编程范式,或者项目中已经采用了这种方式,那么封装成类可能更符合你的编程风格。
    • 服务类或工具类: 当你的功能更偏向于服务性质,例如提供一些工具方法、服务类,而不涉及 UI 层的展示时,封装成类可能更适合。
    • 单例模式: 如果你的功能只需要一个实例,并且需要保持全局唯一性,封装成类可以更方便实现单例模式。
    • 业务逻辑层: 在某些情况下,业务逻辑可能更适合使用类来组织,尤其是当逻辑涉及多个组件或模块时。
  • 综合考虑时,你可能会在项目中同时使用组件和类,根据具体的场景选择最适合的方式。在现代前端开发中,组件化的思想更加流行,因为它有助于构建可维护和可扩展的应用程序。在大多数情况下,选择封装成组件更符合前端开发的潮流

工作场景

  • 例子代码可见《vue 全局挂载自定义函数(class)》
    • 封装class的场景:本身旺旺聊天图标就是个span元素,我还需要一个appkey属性传给span,两种不同点亮图标的方法以及前置方法,虽然我现在只用一个方法,但把其他情况的方法也放一起封装到类里,以便后续开发做其他操作
    • 为何封装class:也可以封装组件,看个人习惯吧,如果封装组件,那么span元素用于显示,appkey在外部获取后存在vuex中(不然多个页面使用,一个个获取传入太麻烦了),在合适时机通过$refs调用图标点亮的方法

,