Skip to content

函数的高级用法

在函数式编程中是禁用 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 高级程序设计 第三版》