实现compose函数的五种思路
- 面向过程
- 函数组合(reduce)
- 函数交织(AOP编程)
- Promise(sequence)
- Generator(yield)
从图中可以看出:
两者的优缺点:
举个栗子:
观察者模式:彩票中心里,管理员充当目标对象(被观察者),彩民充当观察者,当管理员说公布一等奖号码时,即给各个观察者发布了消息,然后彩民(观察者)就收到发布消息,进行自己的后续操作(兑奖)。
发布订阅模式:每家每户向牛奶订购中心订购了牛奶,但是各家的牛奶品牌不一样,有燕塘、蒙牛等等。当燕塘牛奶来货了,订阅中心就给订购燕塘的各家各户派发燕塘牛奶。同理,当蒙牛到货时,订阅中心发布蒙牛的牛奶。
设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢 的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。
如果从作用上来划分,JavaScript设计模式大概分为五种设计类型:
创建型设计模式 | 结构型设计模式 | 行为型设计模式 | 技巧型设计模式 | 架构型设计模式 |
---|---|---|---|---|
简单工厂模式 | 外观模式 | 模板方法模式 | 链模式 | 同步模块 |
工厂方法模式 | 适配器模式 | 观察者模式 | 委托模式 | 异步模块模式 |
抽象工厂模式 | 代理模式 | 状态模式 | 数据访问对象模式 | Widget模式 |
建造者模式 | 装饰者模式 | 策略模式 | 节流模式 | MVC模式 |
原型模式 | 桥接模式 | 职责链模式 | 简单模板模式 | MVP模式 |
单例模式 | 组合模式 | 命令模式 | 惰性模式 | MVVM模式 |
享元模式 | 访问者模式 | 参与者模式 | ||
中介者模式 | 等待者模式 | |||
备忘录模式 | ||||
迭代器模式 | ||||
解释器模式 |
事件机制的核心就是发布-订阅模式。维护一个对象,对象的 key 存的是事件 type,对应的 value 为触发相应 type 的回调函数,即 listeners,然后 trigger 时遍历通知,即 forEach 进行回调执行。
1 | class EventTarget { |
1 | // 测试 |
在JS中,我们可以使用 setTimeout
和 setIntarval
实现动画,但是 H5 的出现,让我们又多了两种实现动画的方式,分别是 CSS 动画(transition
、animation
)和 H5的canvas
实现。除此以外,H5还提供了一个专门用于请求动画的API,让 DOM 动画、canvas动画、svg动画、webGL动画等有一个专门的刷新机制。
传统的javascript 动画是通过定时器 setTimeout 或者 setInterval 实现的。但是定时器动画一直存在两个问题
- 动画的循时间环间隔不好确定(推荐的最佳循环间隔是17ms(大多数电脑的显示器刷新频率是60Hz,1000ms/60))
- 定时器第二个时间参数只是指定了多久后将动画任务添加到浏览器的UI线程队列中,如果UI线程处于忙碌状态,那么动画不会立刻执行
requestAnimationFrame
方法会告诉浏览器希望执行动画并请求浏览器在下一次重绘之前调用回调函数来更新动画。
1 | window.requestAnimationFrame(callback) |
cancelAnimationFrame()
以取消动画。在使用和实现上, requestAnimationFrame
与 setTimeout
类似。举个例子:
1 | let count = 0; |
document.hidden
属性是否可见(true),可见状态下才能继续执行以下步骤requestAnimationFrame
将回调函数追加到动画帧请求回调函数列表的末尾
注意:当执行 requestAnimationFrame(callback)的时候,不会立即调用 callback 回调函数,只是将其放入回调函数队列而已,同时注意,每个 callback回调函数都有一个 cancelled 标志符,初始值为 false,并对外不可见。requestAnimationFrame
自带函数节流功能,采用系统时间间隔,保持最佳绘制效率,不会因为间隔时间的过短,造成过度绘制,增加页面开销,也不会因为间隔时间过长,造成动画卡顿,不流程,影响页面美观。
浏览器的重绘频率一般会和显示器的刷新率保持同步。大多数采用 W3C规范,浏览器的渲染页面的标准频率也为 60 FPS(frames/per second)即每秒重绘60次,requestAnimationFrame的基本思想是 让页面重绘的频率和刷新频率保持同步,即每 1000ms / 60 = 16.7ms执行一次。
通过 requestAnimationFrame
调用回调函数引起的页面重绘或回流的时间间隔和显示器的刷新时间间隔相同。所以 requestAnimationFrame
不需要像 setTimeout
那样传递时间间隔,而是浏览器通过系统获取并使用显示器刷新频率。例如在某些高频事件(resize,scroll 等)中,使用 requestAnimationFrame
可以防止在一个刷新间隔内发生多次函数执行,这样保证了流程度,也节省了开销setTimeout
或setInterval
不准的情况(JS是单线程的,setTimeout
任务被放进异步队列中,只有当主线程上的任务执行完以后,才会去检查该队列的任务是否需要开始执行,造成时间延时)。
setTimeout
的执行只是在内存中对图像属性进行改变,这个改变必须要等到下次浏览器重绘时才会被更新到屏幕上。如果和屏幕刷新步调不一致,就可能导致中间某些帧的操作被跨越过去,直接更新下下一帧的图像。即 掉帧
使用 requestAnimationFrame
执行动画,最大优势是能保证回调函数在屏幕每一次刷新间隔中只被执行一次,这样就不会引起丢帧,动画也就不会卡顿requestAnimationFrame
执行过程,我们知道只有当页面激活的状态下,页面刷新任务才会开始,才执行 requestAnimationFrame
,当页面隐藏或最小化时,会被暂停,页面显示,会继续执行。节省了 CPU 开销。
注意:当页面被隐藏或最小化时,定时器setTimeout
仍在后台执行动画任务,此时刷新动画是完全没有意义的(实际上 FireFox/Chrome 浏览器对定时器做了优化:页面闲置时,如果时间间隔小于 1000ms,则停止定时器,与requestAnimationFrame
行为类似。如果时间间隔>=1000ms,定时器依然在后台执行)1 | // 在浏览器开发者工具的Console页执行下面代码。 |
我们可以使用 requestAnimationFrame
实现setInterval
及 setTimeout
1 | // setInterval实现 |
1 | // setTimeout 实现 |
同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
异步任务指的是,不进入主线程、而进入”任务队列”(task queue)的任务,只有”任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。只要异步任务有了运行结果,就在”任务队列”之 中放置一个事件。
任务队列中存着的是异步任务,这些异步任务一定要等到执行栈清空后才会执行。
同步就是发出一个请求后什么事都不做,一直等待请求返回后才会继续做事;异步就是发出请求后继续去做其他事,这个请求处理完成后会通知你,这时候就可以处理这个回应了。
PWA作为2018最火热的技术概念之一,对提升Web应用的安全、性能和体验有着很大的意义,非常值得我们去了解与学习。
PWA是Progressive Web App的英文缩写,也就是渐进式增强WEB应用。目的就是在移动端利用提供的标准化框架,在网页应用中实现和原生应用相近的用户体验。
一个 PWA 应用首先是一个网页, 可以通过 Web 技术编写出一个网页应用. 随后添加上 App Manifest 和 Service Worker 来实现 PWA 的
安装和离线等功能。
我们需要理解的是,PWA不是某一项技术,或者某一个新的产物;而是一系列Web技术与标准的集合与应用。通过应用这些新的技术与标准,可以从安
严格模式是ES5引入的,更好的将错误检测引入代码的方法。顾名思义,使得JS在更严格的条件下运行。
1 | 变量必须先声明,再使用 |
严格模式
的优点:
消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
消除代码运行的一些不安全之处,保证代码运行的安全;
提高编译器效率,增加运行速度;
为未来新版本的Javascript做好铺垫。
缺点:
现在网站的 JS 都会进行压缩,一些文件用了严格模式,而另一些没有。这时这些本来是严格模式的文件,被 merge 后,这个串就到了文件的中间,不仅没有指示严格模式,反而在压缩后浪费了字节。
Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与处理器对象的方法相同。Reflect不是一个函数对象,因此它是不可构造的。
Reflect这个对象在新版本的chrome是支持的, ff比较早就支持Proxy和Reflect了,要让node支持Reflect可以安装harmony-reflect;
Reflect不是构造函数, 要使用的时候直接通过Reflect.method()调用, Reflect有的方法和Proxy差不多, 而且多数Reflect方法原生的Object已经重新实现了。
与大多数全局对象不同,Reflect没有构造函数。你不能将其与一个new运算符一起使用,或者将Reflect对象作为一个函数来调用。Reflect的所有属性和方法都是静态的(就像Math对象)。