JavaScript Cookbook 2nd 之 Function
昨晚翻了一下,虽然都是一些旧知识,不过深入下去对照着其他资料一起看,还是能发现一些有意思的地方。
函数式编程
反正之前我是没搞懂函数式和命令式的区别,也很疑惑函数式编程中,如果出现分支怎么办,昨晚总算弄明白了。
// 我们有4个基础函数,会根据不同的业务逻辑进行组装使用
// 自动创建
function autoCreate () {}
// 自动同步
function autoSync () {}
// 流程 A
function processA () {}
// 流程 B
function processB () {}
// 流程 A 与流程 B 在业务上是互斥的
传统的命令式编程,我们会这样写业务逻辑
function service (errorHandler) {
var result;
if (!id) {
result = autoCreate();
if (result.error) {
errorHandler(result);
}
}
if (type === 'a') {
processA();
if (result.error) {
errorHandler(result);
}
}
if (type === 'b') {
result = processB();
if (result.error) {
errorHandler(result);
}
}
if (!isSync) {
result = autoSync();
if (result.error) {
errorHandler(result);
}
}
}
而函数式编程,我们则可以这样写业务逻辑。
// service 本身不是一个异步业务,所以直接使用 Promise.resolve()
var service = Promise.resolve();
// 需要对autoCreate()等四个基础函数做 Promise 改造
service.then(autoCreate)
.then(processA)
.then(processB)
.then(autoSync)
.catch(errorHandler);
这里可能会有一个疑惑,互斥的 processA 和 processB 怎么进入了同一个处理流程,这样和需求就不符合了?
在这种情况下,我们还需要在 processA 和 processB 的内部,把退出条件补上。
function processA () {
return new Promise(function(resolve, reject){
if (type !== 'A') {
resolve();
}
// 这里继续 processA 的逻辑代码
});
}
调用栈
JS 在执行的时候,有一个函数调用栈,栈里面放着一个个的函数调用帧,这些帧保存着所属函数所需的所有变量信息。
函数调用会在内存形成一个“调用记录”,又称“调用帧”(call frame),保存调用位置和内部变量等信息。如果在函数A的内部调用函数B,那么在A的调用帧上方,还会形成一个B的调用帧。等到B运行结束,将结果返回到A,B的调用帧才会消失。如果函数B内部还调用函数C,那就还有一个C的调用帧,以此类推。所有的调用帧,就形成一个“调用栈”(call stack)。
浏览器拦截 window.open
我们发现有时候执行 window.open()
,能正常打开新窗口或者新的标签页,而有时却又不行,会被浏览器拦截。
其原因是浏览器会根据当前调用栈,找到最初的caller,如果不是用户触发的,则拦截。
尾调用优化
由于函数调用的时候会生成新的调用帧,当递归调用的时候,调用栈中的调用帧增长会非常厉害,最终导致内存耗尽而触发 RangeError。
如果把函数调用放在函数块的最后一条语句,且不在使用外层函数的变量了,则外层函数所占用的调用帧已无存在意义。在进入内层函数的时候,可以直接用内层函数的调用帧替换掉外层函数的调用帧,从而大大减少内存占用。
其他
Partial Application 的模式,用来做 Library 和 SDK 都挺好的。一个可定制性高的底层接口,再通过类似 _.partial()
的方式,提供一个开箱即用的上层接口。就像 $.ajax()
和 $.get()
一样。
_.defer()
和 _.memoize()
可以用在有大运算量的业务场景。
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- java.util.concurrent.TimeoutException: 的解决!
- HTTP Status 503 - Server is shutting down or failed to initialize
- LinkedHashMap 源码剖析
- 基于SSM框架与Maven的CRUD案例
- Java 8的这个新特性,你用了吗?
- JSP+Servlet项目整合
- springboot 整合 Mybatis、JPA、Redis
- 数据库中设置列/字段自增(Oracle和Mysql)
- 数据库中日期的插入(Oracle和Mysql)
- 浅谈在进行jsp页面编程时,路径问题的解决(绝对路径与相对路径)
- 浅谈springMVC中,中文乱码的显示问题(持续更新)
- 数据库的优化方法,一般人我不告诉他(建议收藏)
- idea 远程调试
- 嘘!高效快速的刷访问量(偷偷收藏)
- 进行JDBC操作时,连接参数的配置(附DBUtil工具类)