函数节流
函数节流的目的
从字面上就可以理解,函数节流就是用来节流函数从而一定程度上优化性能的。例如,DOM 操作比起非 DOM 交互需要更多的内存和 CPU 时间。连续尝试进行过多的 DOM 相关操作可能会导致浏览器挂起,有时候甚至会崩溃。尤其在 IE 中使用 onresize 事件处理程序的时候容易发生,当调整浏览器大小的时候,该事件会连续触发。在 onresize 事件处理程序内部如果尝试进行 DOM 操作,其高频率的更改可能会让浏览器崩溃。又例如,我们常见的一个搜索的功能,我们一般是绑定 keyup 事件,每按下一次键盘就搜索一次。但是我们的目的主要是每输入一些内容搜索一次而已。为了解决这些问题,就可以使用定时器对函数进行节流。
函数节流的原理
某些代码不可以在没有间断的情况连续重复执行。第一次调用函数,创建一个定时器,在指定的时间间隔之后运行代码。当第二次调用该函数时,它会清除前一次的定时器并设置另一个。如果前一个定时器已经执行过了,这个操作就没有任何意义。然而,如果前一个定时器尚未执行,其实就是将其替换为一个新的定时器。目的是只有在执行函数的请求停止了一段时间之后才执行。
Promise
现在回顾下 Promise 的实现过程,其主要使用了设计模式中的观察者模式:
通过 Promise.prototype.then 和 Promise.prototype.catch 方法将观察者方法注册到被观察者 Promise 对象中,同时返回一个新的 Promise 对象,以便可以链式调用。
被观察者管理内部 pending、fulfilled 和 rejected 的状态转变,同时通过构造函数中传递的 resolve 和 reject 方法以主动触发状态转变和通知观察者。
深拷贝和浅拷贝的定义
浅拷贝:
创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
深拷贝:
将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象
CSRF
什么是 CSRF:
CSRF(Cross-site request forgery)跨站请求伪造:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
一个典型的 CSRF 攻击有着如下的流程:
1. 受害者登录a.com,并保留了登录凭证(Cookie)。
2. 攻击者引诱受害者访问了b.com。
3. b.com 向 a.com 发送了一个请求:a.com/act=xx。浏览器会默认携带a.com的Cookie。
4. a.com接收到请求后,对请求进行验证,并确认是受害者的凭证,误以为是受害者自己发送的请求。
5. a.com以受害者的名义执行了act=xx。
6. 攻击完成,攻击者在受害者不知情的情况下,冒充受害者,让a.com执行了自己定义的操作。
CSRF 的特点:
1. 攻击一般发起在第三方网站,而不是被攻击的网站。被攻击的网站无法防止攻击发生。
2. 攻击利用受害者在被攻击网站的登录凭证,冒充受害者提交操作;而不是直接窃取数据。
3. 整个过程攻击者并不能获取到受害者的登录凭证,仅仅是“冒用”。
4. 跨站请求可以用各种方式:图片URL、超链接、CORS、Form提交等等。部分请求方式可以直接嵌入在第三方论坛、文章中,难以进行追踪。
CSRF 通常是跨域的,因为外域通常更容易被攻击者掌控。但是如果本域下有容易被利用的功能,比如可以发图和链接的论坛和评论区,攻击可以直接在本域下进行,而且这种攻击更加危险。
防护策略:
CSRF 通常从第三方网站发起,被攻击的网站无法防止攻击发生,只能通过增强自己网站针对 CSRF 的防护能力来提升安全性。
上文中讲了CSRF的两个特点:
1. CSRF(通常)发生在第三方域名。
2. CSRF攻击者不能获取到Cookie等信息,只是使用。
针对这两点,我们可以专门制定防护策略,如下:
1. 阻止不明外域的访问
- 同源检测
- Samesite Cookie
2. 提交时要求附加本域才能获取的信息
- CSRF Token
- 双重Cookie验证
JavaScript 设计模式
设计模式是解决某个特定场景下对某种问题的解决方案。因此,当我们遇到合适的场景时,我们可能会条件反射一样自然而然想到符合这种场景的设计模式。
1、单例模式
单例模式的定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。实现的方法为先判断实例存在与否,如果存在则直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。
适用场景:一个单一对象。比如:弹窗,无论点击多少次,弹窗只应该被创建一次。
2、策略模式
策略模式的定义:定义一系列的算法,把他们一个个封装起来,并且使他们可以相互替换。
策略模式的目的就是将算法的使用算法的实现分离开来。
一个基于策略模式的程序至少由两部分组成。第一个部分是一组策略类(可变),策略类封装了具体的算法,并负责具体的计算过程。第二个部分是环境类 Context(不变),Context 接受客户的请求,随后将请求委托给某一个策略类。要做到这一点,说明 Context 中要维持对某个策略对象的引用。
3、代理模式
代理模式的定义:为一个对象提供一个代用品或占位符,以便控制对它的访问。
常用的虚拟代理形式:某一个花销很大的操作,可以通过虚拟代理的方式延迟到这种需要它的时候才去创建(例:使用虚拟代理实现图片懒加载)
图片懒加载的方式:先通过一张 loading 图占位,然后通过异步的方式加载图片,等图片加载好了再把完成的图片加载到 img 标签里面。
使用代理模式实现图片懒加载的优点还有符合单一职责原则。减少一个类或方法的粒度和耦合度。
4、中介者模式
中介者模式的定义:通过一个中介者对象,其他所有的相关对象都通过该中介者对象来通信,而不是相互引用,当其中的一个对象发生改变时,只需要通知中介者对象即可。通过中介者模式可以解除对象与对象之间的紧耦合关系。
例如:现实生活中,航线上的飞机只需要和机场的塔台通信就能确定航线和飞行状态,而不需要和所有飞机通信。同时塔台作为中介者,知道每架飞机的飞行状态,所以可以安排所有飞机的起降和航线安排。
中介者模式适用的场景:例如购物车需求,存在商品选择表单、颜色选择表单、购买数量表单等等,都会触发 change 事件,那么可以通过中介者来转发处理这些事件,实现各个事件间的解耦,仅仅维护中介者对象即可。
5、装饰者模式
装饰者模式的定义:在不改变对象自身的基础上,在程序运行期间给对象动态地添加方法。
例如:现有 4 种型号的自行车分别被定义成一个单独的类,如果给每辆自行车都加上前灯、尾灯、铃铛这 3 个配件,如果用类继承的方式,需要创建 4*3=12 个子类。但如果通过装饰者模式,只需要创建 3 个类。
装饰者模式适用的场景:原有方法维持不变,在原有方法上再挂载其他方法来满足现有需求;函数的解耦,将函数拆分成多个可复用的函数,再将拆分出来的函数挂载到某个函数上,实现相同的效果但增强了复用性。
例:用 AOP 装饰函数实现装饰者模式
微信小程序与 h5 的区别
HTML 英文全称为 Hyper Text Markup Language,即超文本标记语言,H5 是一种技术,依附的外壳是是浏览器,而小程序是基于微信的一种不需要下载安装即可使用的应用。
从「前端开发」的视角来看,微信小程序和 H5 也存在着多方面的不同。概括来说有以下四个方面的区别
一、运行环境的不同
小程序只能在微信中使用。H5 可以在任何浏览器使用
H5 的运行环境是浏览器,小程序的运行环境是微信开发团队基于浏览器内核完全重构的一个内置解析器
二、开发成本的不同
H5 的开发,涉及开发工具(vscode、Atom 等)、前端框架(Angular、react 等)、模块管理工具(Webpack 、Browserify 等)、任务管理工具(Grunt、Gulp 等),还有 UI 库选择、接口调用工具(ajax、Fetch Api 等)、浏览器兼容性等等。
开发一个微信小程序,由于微信团队提供了开发者工具,以及 api,并且规范了开发标准。在使用这些 API 时,不用考虑浏览器兼容性,不用担心出现 BUG。
小程序是 XML,不是 HTML。小程序用的是 WXML 和 WXSS,标准由微信小程序自己制定。HTML 和 WXML 有交集、CSS 和 WXSS 有交集,但他们是不同的。
显而易见微信小程序的开发成本相对低很多。
三、获取系统级权限的不同
微信小程序相对于 H5 能获得更多的系统权限,比如网络通信状态、数据缓存能力等,这些系统级权限都可以和微信小程序无缝衔接。这也是 H5 的大多应用场景被定位在业务逻辑简单、功能单一的原因。
四、运行流畅度的不同
最容易区分小程序与 H5 的一点,打开 H5,实际上是打开一个网页,而网页需要在浏览器中渲染,面对复杂的业务逻辑或者丰富的页面交互时页面会卡顿。
而微信小程序,直接在微信上运行,省去了通过浏览器渲染的步骤,因此,在微信中使用小程序,才会比 H5 流畅很多。除了首次打开需要几秒的加载时间外,小程序各个页面的切换、跳转等体验已经媲美原生 App,非常顺畅。小程序不能跳转外部链接,H5 没有限制。
概括来说、小程序相对 H5 有着开发成本低、功能更丰富、用户体验更佳的优点。(缺点:微信做了很多限制,很多东西不能自定义)
VUE 响应式系统实现
Vue.js 是一款 MVVM 框架,数据模型仅仅是普通的 JavaScript 对象,但是对这些对象进行操作时,却能影响对应视图,它的核心实现就是「响应式系统」。
vue 2.0 中,是基于 Object.defineProperty 实现的「响应式系统」。vue3 中是基于 Proxy/Reflect 来实现的,vue3 的详细解析有时间再写了,本文讲的是 vue2 的实现。
主要涉及属性:
1. enumerable,属性是否可枚举,默认 false。
2. configurable,属性是否可以被修改或者删除,默认 false。
3. get,获取属性的方法。
4. set,设置属性的方法。
响应式基本原理就是,在 Vue 的构造函数中,对 options 的 data 进行处理。即在初始化 vue 实例的时候,对 data、props 等对象的每一个属性都通过 Object.defineProperty 定义一次,在数据被 set 的时候,做一些操作,改变相应的视图。