2022-09-27JavaScript00

1)什么是闭包

函数执行后返回结果是一个内部函数,并被外部变量所引用,如果内部函数持有被执行函数作用域的变量,即形成了闭包。

可以在内部函数访问到外部函数作用域。使用闭包,一可以读取函数中的变量,二可以将函数中的变量存储在内存中,保护变量不被污染。而正因闭包会把函数中的变量值存储在内存中,会对内存有消耗,所以不能滥用闭包,否则会影响网页性能,造成内存泄漏。当不需要使用闭包时,要及时释放内存,可将内层函数对象的变量赋值为null。

2)闭包原理

函数执行分成两个阶段(预编译阶段和执行阶段)。

  • 在预编译阶段,如果发现内部函数使用了外部函数的变量,则会在内存中创建一个“闭包”对象并保存对应变量值,如果已存在“闭包”,则只需要增加对应属性值即可。
  • 执行完后,函数执行上下文会被销毁,函数对“闭包”对象的引用也会被销毁,但其内部函数还持用该“闭包”的引用,所以内部函数可以继续使用“外部函数”中的变量

利用了函数作用域链的特性,一个函数内部定义的函数会将包含外部函数的活动对象添加到它的作用域链中,函数执行完毕,其执行作用域链销毁,但因内部函数的作用域链仍然在引用这个活动对象,所以其活动对象不会被销毁,直到内部函数被烧毁后才被销毁。

2022-09-27安全00

1)XSS:跨站脚本攻击

就是攻击者想尽一切办法将可以执行的代码注入到网页中。

存储型(server端):
  • 场景:见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等。

  • 攻击步骤:

    • i)攻击者将恶意代码提交到目标网站的数据库中
    • ii)用户打开目标网站时,服务端将恶意代码从数据库中取出来,拼接在HTML中返回给浏览器
    • iii)用户浏览器在收到响应后解析执行,混在其中的恶意代码也同时被执行
    • iv)恶意代码窃取用户数据,并发送到指定攻击者的网站,或者冒充用户行为,调用目标网站的接口,执行恶意操作
反射型(Server端)

与存储型的区别在于,存储型的恶意代码存储在数据库中,反射型的恶意代码在URL上

  • 场景:通过 URL 传递参数的功能,如网站搜索、跳转等。

  • 攻击步骤:

    • i)攻击者构造出特殊的 URL,其中包含恶意代码。
    • ii)用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。
    • iii)用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
    • iv)恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
2022-09-27浏览器00

1)浏览器缓存策略

浏览器每次发起请求时,先在本地缓存中查找结果以及缓存标识,根据缓存标识来判断是否使用本地缓存。如果缓存有效,则使 用本地缓存;否则,则向服务器发起请求并携带缓存标识。根据是否需向服务器发起HTTP请求,将缓存过程划分为两个部分: 强制缓存和协商缓存,强缓优先于协商缓存。

  • 强缓存,服务器通知浏览器一个缓存时间,在缓存时间内,下次请求,直接用缓存,不在时间内,执行比较缓存策略。
  • 协商缓存,让客户端与服务器之间能实现缓存文件是否更新的验证、提升缓存的复用率,将缓存信息中的Etag和Last-Modified 通过请求发送给服务器,由服务器校验,返回304状态码时,浏览器直接使用缓存。

HTTP缓存都是从第二次请求开始的:

  • 第一次请求资源时,服务器返回资源,并在response header中回传资源的缓存策略;
  • 第二次请求时,浏览器判断这些请求参数,击中强缓存就直接200,否则就把请求参数加到request header头中传给服务器,看是否击中协商缓存,击中则返回304,否则服务器会返回新的资源。
2022-09-27业务场景00

1)对于正常的项目优化,一般都涉及到几个方面,开发过程中上线之后的首屏运行过程的状态

  • 来聊聊上线之后的首屏及运行状态:

    • 首屏优化一般涉及到几个指标FP、FCP、FMP;要有一个良好的体验是尽可能的把FCP提前,需要做一些工程化的处理,去优化资源的加载
    • 方式及分包策略,资源的减少是最有效的加快首屏打开的方式;
    • 对于CSR的应用,FCP的过程一般是首先加载js与css资源,js在本地执行完成,然后加载数据回来,做内容初始化渲染,这中间就有几次的网络反复请求的过程;所以CSR可以考虑使用骨架屏及预渲染(部分结构预渲染)、suspence与lazy做懒加载动态组件的方式
    • 当然还有另外一种方式就是SSR的方式,SSR对于首屏的优化有一定的优势,但是这种瓶颈一般在Node服务端的处理,建议使用stream流的方式来处理,对于体验与node端的内存管理等,都有优势;
    • 不管对于CSR或者SSR,都建议配合使用Service worker,来控制资源的调配及骨架屏秒开的体验
    • react项目上线之后,首先需要保障的是可用性,所以可以通过React.Profiler分析组件的渲染次数及耗时的一些任务,但是Profile记录的是commit阶段的数据,所以对于react的调和阶段就需要结合performance API一起分析;
    • 由于React是父级props改变之后,所有与props不相关子组件在没有添加条件控制的情况之下,也会触发render渲染,这是没有必要的,可以结合React的PureComponent以及React.memo等做浅比较处理,这中间有涉及到不可变数据的处理,当然也可以结合使用ShouldComponentUpdate做深比较处理;
    • 所有的运行状态优化,都是减少不必要的render,React.useMemo与React.useCallback也是可以做很多优化的地方;
    • 在很多应用中,都会涉及到使用redux以及使用context,这两个都可能造成许多不必要的render,所以在使用的时候,也需要谨慎的处理一些数据;
    • 最后就是保证整个应用的可用性,为组件创建错误边界,可以使用componentDidCatch来处理;
  • 实际项目中开发过程中还有很多其他的优化点:

    • 1.保证数据的不可变性
    • 2.使用唯一的键值迭代
    • 3.使用web worker做密集型的任务处理
    • 4.不在render中处理数据
    • 5.不必要的标签,使用React.Fragments
2022-09-26JavaScript00

构造函数法

用 new 关键字生成实例对象

缺点是用到了 this 和 prototype,编写复杂,可读性差

function Mobile(name, price) {
  this.name = name;
  this.price = price;
}
Mobile.prototype.sell = function () {
  alert(this.name + ",售价 $" + this.price);
};
var iPhone = new Mobile("iPhone", 1000);
iPhone.sell();

Object.create 法

Object.create() 生成实例对象。

缺点是不能实现私有属性和私有方法,实例对象之间也不能共享数据

var Person = {
  firstName: "Mark",
  lastName: "Yun",
  age: 25,
  introduce: function () {
    alert("I am " + Person.firstName + " " + Person.lastName);
  },
};

var person = Object.create(Person);
person.introduce();

// Object.create 要求IE9,低版本浏览器可以自行部署
if (!Object.create) {
  Object.create = function (o) {
    function F() {}
    F.prototype = o;
    return new F();
  };
}