导航
[[深刻01] 执行上下文](https://juejin.im/post/684490…
[[深刻02] 原型链](https://juejin.im/post/684490…
[[深刻03] 继承](https://juejin.im/post/684490…
[[深刻04] 事件循环](https://juejin.im/post/684490…
[[深刻05] 柯里化 偏函数 函数记忆](https://juejin.im/post/684490…
[[深刻06] 隐式转换 和 运算符](https://juejin.im/post/684490…
[[深刻07] 浏览器缓存机制(http缓存机制)](https://juejin.im/post/684490…
[[深刻08] 前端平安](https://juejin.im/post/684490…
[[深刻09] 深浅拷贝](https://juejin.im/post/684490…
[[深刻10] Debounce Throttle](https://juejin.im/post/684490…
[[深刻11] 前端路由](https://juejin.im/post/684490…
[[深刻12] 前端模块化](https://juejin.im/post/684490…
[[深刻13] 观察者模式 公布订阅模式 双向数据绑定](https://juejin.im/post/684490…
[[深刻14] canvas](https://juejin.im/post/684490…
[[深刻15] webSocket](https://juejin.im/post/684490…
[[深刻16] webpack](https://juejin.im/post/684490…
[[深刻17] http 和 https](https://juejin.im/post/684490…
[[深刻18] CSS-interview](https://juejin.im/post/684490…
[[深刻19] 手写Promise](https://juejin.im/post/684490…
[[深刻20] 手写函数](https://juejin.im/post/684490…
[[react] Hooks](https://juejin.im/post/684490…
[[部署01] Nginx](https://juejin.im/post/684490…
[[部署02] Docker 部署vue我的项目](https://juejin.im/post/684490…
[[部署03] gitlab-CI](https://juejin.im/post/684490…
[[源码-webpack01-前置常识] AST形象语法树](https://juejin.im/post/684490…
[[源码-webpack02-前置常识] Tapable](https://juejin.im/post/684490…
[[源码-webpack03] 手写webpack – compiler简略编译流程](https://juejin.im/post/684490…
[[源码] Redux React-Redux01](https://juejin.im/post/684490…
[[源码] axios ](https://juejin.im/post/684490…
[[源码] vuex ](https://juejin.im/post/684490…
[[源码-vue01] data响应式 和 初始化渲染 ](https://juejin.im/post/684490…
前置常识
函数的参数
- length:函数的legth属性,返回函数预期的参数个数(形参)
-
arguments:arguments对象,蕴含了程序运行时的所有参数(实参)
相似数组的对象转换成数组
- [].slice.call(相似数组的对象)
- [].slice.apply(相似数组的对象)
- Array.prototype.slice.call(相似数组的对象, x)
// x是绑定this后传入slice函数的参数
-
Array.from()
偏函数和柯里化的概念
-
柯里化 curry:
- 将接管多个参数的函数,转换成接管一个繁多参数的函数,并返回接管余下参数,并返回最终后果的新函数
- <font color=red>即当参数小于预期参数时,返回一个能够接管残余参数的函数,参数大于等于预期参数时,返回最终后果</font>
-
偏函数 partial application:
- 是固定一个或多个参数,产生另一个较小元的函数 n元函数 => 转换成n-x元函数
柯里化 curry
-
柯里化函数,他接管函数A作为参数,运行后可能返回一个新的函数,并且这个新的函数可能处理函数A的残余参数
1. 柯里化阶段一
- 需要:
将add(1,2,3)转化成curryAdd(1)(2)(3)
-
<font color=red>毛病:只能解决3个参数的状况,不能解决任意多个参数的状况,毫无扩展性</font>
需要: 将add(1,2,3)转化成curryAdd(1)(2)(3) 毛病:只能解决3个参数的状况,不能解决任意多个参数的状况,毫无扩展性 function curryAdd(a) { return function(b) { return function(c) { return a+b+c } } } const res = curryAdd(1)(2)(3) console.log(res, 'res1')
2. 柯里化阶段二
- 需要:
解决任意多个参数相加
-
毛病:
-
1. 解决相加逻辑的代码,只是在没有参数时才会执行,其余局部都在解决怎么收集所有参数,会多一次没有参数的调用
- 更正当的形式是通过判断函数能够接管参数的总和,来判断是否参数收集结束
2. 相加逻辑能够独自抽离
function curryAdd() { let params_arr = [] // 用于收集所有实参 function closure() { const args = Array.prototype.slice.call(arguments) // 每次调用闭包函数传入的实参,能够是多个 if (args.length) { params_arr = params_arr.concat(args) // concat返回一个拼接过后的新数组,不扭转原数组 return closure // 如果还有参数,则持续返回闭包函数,则持续持续传参调用 } return params_arr.reduce((total, current) => total + current) // 如果没有再传入参数,则相加所有传入的参数,毛病是要多一次没有参数的调用 } return closure // 第一次调用curryAdd返回的闭包 } const fn = curryAdd() const res = fn(1,2)(3)(4)() console.log(res, 'res'); // 10
-
3. 柯里化阶段三
function add(a,b,c,d,e) {
return Array.prototype.slice.call(arguments).reduce((total, current) => total + current)
// 留神:这里拿到的是实参的理论个数,即实参可能大于形参,当实参 (大于等于) 形参时,执行相加
}
function curryAdd(fn) {
let paramsArr = []
const paramsMaxLength = fn.length // function.length返回函数的形参个数,预期的参数个数为最大参数个数,即相加执行条件
function closure() {
const args = Array.prototype.slice.call(arguments)
paramsArr = paramsArr.concat(args)
if (paramsArr.length < paramsMaxLength) {
return closure
}
// 当参数个数 大于等于 最大的冀望个数,即形参的个数时,执行相加函数
return fn.apply(this, paramsArr)
}
return closure
}
const fn = curryAdd(add)
const res = fn(1,2,3)(4)(5,6)
console.log(res, 'res');
4.柯里化变通版
- 下面版本的毛病:下面的版本须要晓得add的参数length
function add() {
return Array.from(arguments).reduce((total, current) => total + current)
}
function currentAdd(fn) {
let paramsArr = []
function closure() { // 该闭包函数只负责收集参数,解决相加能够在闭包上挂载新的办法getSum
const args = Array.from(arguments)
paramsArr = paramsArr.concat(args)
return closure
}
closure.getSum = function() {
return fn.apply(this, paramsArr) // getSum负责计算,利用了闭包中的变量paramsArr
}
return closure
}
const fn = currentAdd(add)
const resAdd = fn(1)(2,3)
const res = resAdd.getSum(); // 该办法的毛病就是须要独自再调用getSum函数
console.log(res, 'res')
偏函数 partial
-
将一个或者多个参数,固定到一个函数上,并产生返回一个更小元的函数
function add (a, b) { return a + b } function partial (fn) {...} const addPartial = partial(add, 1) // ------------------ 实现固定一部分参数1 const res = addPartial(2) // 3 -------------------------- 只传一部分参数 2
偏函数实现形式1
- <font color=red>通过bind办法实现</font>
-
bind办法绑定this指向,同时也能够传入fn的局部和全副参数,并返回一个新函数,新函数能够传入参数作为fn的残余参数
function add(a,b,c,d) { return a+b+c+d } function partail() { const params = Array.prototype.slice.call(arguments) const fn = params.shift() // 删除数组第一个元素,返回该元素,扭转原数组 return fn.bind(this, ...params) // 该params执行shift后曾经扭转\ // params数组开展后的所有成员,都会作为fn的参数 // 并且bind返回的新函数还能够传参 } const fn = partail(add, 1, 2) // 固定了 1,2两个参数 const res = fn(3,4) // 除了固定的参数,剩下的参数在这里传入 console.log(res, 'res')
偏函数实现形式2
function add(a,b,c,d) {
return Array.from(arguments).reduce((total, current) => total + current)
// 相加实参
// 因为实参可能大于形参
}
function partialAdd(fn) {
let paramsFixed = Array.from(arguments).slice(1)
// 除去fn的残余参数
// 留神:该办法和curry很类似,current第一调用是不须要传fn参数的,申明的是空数组,而在partial中须要传固定的参数
const paramsMaxLength = fn.length // 形参个数
function closure() {
const args = Array.from(arguments)
paramsFixed = paramsFixed.concat(args)
if (paramsFixed.length < paramsMaxLength) {
return closure
}
return fn.apply(this, paramsFixed) // 大于等于时
}
return closure
}
const fn = partialAdd(add, 2)
const res = fn(3)(4)(5)
console.log(res, 'res') // 14
函数记忆
- 函数记忆:
指将上次的(计算结果)缓存起来,当下次调用时,如果遇到雷同的(参数),就间接返回(缓存中的数据)
-
实现原理:将参数和对应的后果保留在对象中,再次调用时,判断对象key是否存在,存在返回缓存的值
- 留神:函数是须要返回值的
function memorize(fn) { const cache = {} return function() { const key = Array.prototype.join.call(arguments, ',') if (key in cache) { return cache[key] } return cache[key] = fn.apply(this, arguments) } }
我的简书:https://www.jianshu.com/p/eb5…
尾调用
尾调用: 函数执行的最初一个步骤,是返回另一个函数的调用,叫尾调用
长处:
1. 尾调用,当里层函数被调用时,外层函数曾经执行完,出栈了,不会造成内存透露
2. 在递归中,尾调用使得栈中只有一个函数在运行,不会造成性能问题
f(x) {
return g(x)
}
// 尾调用,因为返回g(x)调用的时候,f(x)曾经执行完
f(x) {
return g(x) + 1
}
// 非尾调用,因为返回 g(x) 调用时,f(x)并未执行完,当g(x)执行完后,还有执行 g(x)+1,f(x)才执行完
// 函数只有执行完后才会出栈(执行上下文调用栈)
const a = x => x ? f() : g();
// f()和g()都是尾调用
const a = () => f() || g()
// f()非尾调用,还要接着判断
const a = () => f() && g();
// f()非尾调用
尾递归
递归 -- 尾递归和尾调用
1. 形成递归的条件
- 边界条件
- 递归后退段
- 递归返回段
- 当边界条件不满足时,递归后退
- 当边界条件满足时,递归返回
2.
Recursive:递归
factorial:阶乘
3. 尾调用和非尾调用
- 尾调用和非尾调用的区别是 执行上下文栈不一样
- 为调用:调用在函数结尾处
- 尾调用的执行上下文栈,外层函数执行完就出栈,不会一层一层嵌套,不造成内存溢出
- 尾调用本身就叫尾递归
// 尾调用
// 因为调用g(x)时,f(x)曾经执行完了,就会出栈,不会压栈,不会造成内存溢出
function f(x){
return g(x);
}
// 非尾调用
// 因为调用g(x)时,f(x)并未执行完,g(x)+1须要g(x)函数执行完,才会相加,返回后f(x)才会执行完
function f(x){
return g(x) + 1;
}
------------------------------------------------------------------------------------
+++(例1)阶乘
// recursive递归
function factorial (n) {
if (n < 2) return n
return n * factorial(n-1)
}
const res = factorial(3)
// 1. 3 => 3 * factorial(2) => 3 * 2 * factorial(1) => 3 * 2 * 1
(剖析)
1. 每次返回一个递归的函数,都会创立一个闭包
2. 所以保护这么多执行上下文栈,开销大,用以造成内存透露
3. 优化办法:尾调用
+++(例1降级)阶乘优化
function factorial(n, res) {
if (n === 1) {
return res
}
return factorial(n-1, n * res)
}
(剖析)
第一次:factorial(3, 4* 1)
第二次:factorial(2, 3* 4)
第三次:factorial(1, 2* 12)
第四次:24
+++(例1再降级)阶乘优化,多传了一个参数,能够用函数柯里化或者偏函数来实现
function factorial(res, n) {
if (n === 1) return res;
return factorial(n * res, n-1)
}
function curring (fn) {
let par_arr = Array.prototype.slice.call(arguments, 1)
const closure = function () {
par_arr = par_arr.concat(Array.prototype.slice.call(arguments))
console.log(par_arr, 'par_arr')
if (par_arr.length < fn.length) {
return closure
}
return fn.apply(null, par_arr)
}
return closure
}
const curringFactorial = curring(factorial, 1)
const res = curringFactorial(4)
console.log(res)
发表回复