函数的高级用法
在函数式编程中是禁用 this 的,this 只应该出现在 class 中
递归和尾递归
调用栈:
函数的执行的时候会形成一个调用记录,当函数代码执行完成之后才会销毁调用记录。也称为调用栈。
当 a 函数内部又调用了 b 函数的时候,a 函数的调用栈会保留,开启 b 函数的调用栈。当 b 结束之后 a 才会结束。
一层一层叠加下来会使得内存溢出。
javascript
function a() {
function b() {
function c() {}
c()
}
b()
}
a()
这里 a<==b<==c 先后压入栈中。cb 执行完成之后 a 才能执行完。
代码中 b/c 函数都是最后执行完成就结束了。于是可以这么写:
javascript
function a() {
function b() {
return c()
}
return b()
}
a()
将最后调用的 b 函数写成 return b()的形式。这样程序就可以判断 a 执行完成之后返回 b
那么 b 的调用栈就可以替代 a 的调用栈,同理 c 的调用栈也可以替代 b 的调用栈,节省了大量的代码内存栈空间。
比如: 求斐波拉契某一项(1,1,2,3,5,8....)
javascript
function fib(n) {
if (n == 1 || n == 2) {
return 1
}
return fib(n - 1) + fib(n - 2)
}
fib(10) //55
fib(40) // 花了一会儿时间
使用尾递归
javascript
function fib(n, a1 = 1, a2 = 1) {
if (n == 1 || n == 2) {
return a2
}
return fib(n - 1, a2, a1 + a2) //将后面的数提前,后面放和
}
fib(10) //55
fib(1000) // 秒算
柯里化(currying)
把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。比如为了延迟计算
换句话说是固定部分参数,返回一个接受剩余参数的函数
比如:
javascript
//求和,接受两个参数返回和
function sum(a, b) {
return a + b
}
sum(2, 3) //5
柯里化之后:
javascript
function sum(a) {
let temp = a
return function(b) {
return temp + b
}
}
let sum2 = sum(2)(3) // 5
//但是随着层数增加return 的层数也会增加
修改
javascript
function sum(a) {
let temp = a
function foo(b) {
temp += b
return foo
}
return foo
}
sum(1)(2)(3)(4) //1 2 3 4都加到temp中,但是不返回啊
二度修改
javascript
function sum(a) {
let o = {
value: a,
add: function(b) {
this.value += b
return this
},
minus: function(b) {
this.value -= b
return this
},
getvalue: function() {
return this.value
},
}
return o
}
sum(1).add(2).add(3).minus(10).add(4).getvalue() //10
比如类似 jQuery 里面的选择元素和设置样式的实现
javascript
;
(function(window) {
function $(str) {
let o = {
el: document.querySelector(str),
css: function(style, value) {
this.el.style[style] = value
return this
},
click: function(cb) {
this.el.onclick = cb
}, //加满就行了
}
return o
}
window.$ = $
})(window)
反柯里化
让只接受一个参数的函数变的可以接受多个参数,扩大函数的使用适用性,使本来作为特定对象所拥有的功能的函数可以被任意对象所用
形如: obj.func(arg1, arg2)
转换成func(obj, arg1, arg2)
javascript
Function.prototype.uncurring = function() {
var self = this //反柯里化的函数的主体对象
return function() {
var obj = Array.prototype.shift.call(arguments) //取接受的参数
return self.apply(obj, arguments) //将call接受的参数再用aplly接受
}
}
var push = Array.prototype.push.unCurrying(),
obj = {}
push(obj, "first", "two")
console.log(obj) //{0:"first",1:"two"}
柯里化和反柯里化一言难尽 请参考《JavaScript 高级程序设计 第三版》