纯函数、柯里化、组合、with、eval

125 阅读4分钟

纯函数、柯里化、组合、with、eval

学习coderwhy课程笔记

js纯函数

  • 确定的输入,一定会产生确定的输出 (传入相同的参数,总会返回相同的结果)
  • 函数在执行过程中,不会产生任何副作用

副作用:

  1. 在执行一个函数时,除了返回函数值之外,还对调用函数产生了附加的影响,比如修改了全局变量,修改参数或者改变外部的存储
  2. 纯函数在执行的过程中就是不能产生这样的副作用,副作用往往是产生bug的“温床”。

例如: slice就是一个纯函数; splice就不是一个纯函数(splice会修改原数组)

优势:

  1. 安心的编写和使用
  2. 单纯实现自己的业务逻辑即可,不需要关心传入的内容是如何获得的或者依赖其他的外部变量是否已经发生了修改;
  3. 输入内容不会被任意篡改,并且自己确定的输入,一定会有确定的输出;

js柯里化

  • 只传递给函数一部分参数来调用它,让它返回一个函数去处理剩余的参数,这个过程就称之为柯里化; 具体如下:
function add(x, y, z) {
    return x + y + z
}

var result = add(10, 20, 30)
console.log(result)

// add函数转换成sum函数的过程,叫做柯里化的过程
function sum(x) {
    // 这里可以写一些复用的逻辑
    return function (y) {
        return function (z) { return x + y + z }
    }
}
var result1 = sum(10)(20)(30)
console.log(result1)


// 简化柯里化的代码
var sum2 = x => y => z => x + y + z
console.log(sum2(10)(20)(30))

优势:

  1. 一个函数处理单一的问题,而不是将一大堆处理过程交给一个函数来处理
  2. 对某些逻辑进行复用

柯里化函数的实现

补充知识:JS 中函数的 length 属性

function add(x, y, z) {
    return x + y + z
}

function add1(x, y = 1, z) {
    return x + y + z
}

console.log(add.length, add1.length) // 3,1

函数的length由2个因素决定:

  1. 函数有多少个必须要传入的参数,即 形参的个数
  2. 第一个具有默认值之前的参数个数

所以上述代码的打印结果是3,1

柯里化函数实现代码如下:

function add(x, y, z) {
    return x + y + z
}

// 柯里化函数的实现
function myCurrying(fn) {
    function curried(...args) {
        // 判断当前已经接收到的参数个数 和 fn函数本身需要接收的参数是否已经一致
        // 1、当前传入的参数 大于等于 需要的参数时,就执行函数
        if (args.length >= fn.length) {
            return fn.apply(this, args)
        } else {
            // 没有达到个数时,需要返回一个新的函数,继续来接收的参数
            function curried2(...args2) {
                return curried.apply(this, args.concat(args2))
            }
            return curried2
        }
    }
    return curried
}

var curryAdd = myCurrying(add)
console.log(curryAdd(10)(20)(30))
console.log(curryAdd(10, 20)(30))
console.log(curryAdd(10, 20, 30))

组合函数

具体代码如下:

function double(num) {
    return num * 2
}

function square(num) {
    return num ** 2
}

var count = 10
var result = square(double(count))
console.log(result)

// 上述2个函数整合为下面所示函数,即为组合函数
function composeFn(m, n) {
    return n(m(count))
}

var newFn = composeFn(double, square)
console.log(newFn(10))

传入多个函数的组合函数的实现:

function myCompose(...fns) {
    // 先校验当前参数是函数
    var length = fns.length;
    for (var i = 0; i < length; i++) {
        if (typeof fns[i] !== 'function') {
            throw new TypeError('要求参数都为函数')
        }
    }


    function compose(...args) {
        var index = 0
        var result = length ? fns[index].apply(this, args) : args
        while (++index < length) {
            result = fns[index].call(this, result)
        }
        return result
    }

    return compose
}

function double(m) {
    return m * 2
}
function square(m) {
    return m ** 2
}
var newFn = myCompose(double, square);

with语句

with语句有自己的作用域

var message = 'Hello World'
var obj = {name: 'why',age: '18',message:"hello"}
with (obj) {
    console.log(message) // hello
}

此时打印出来的message为obj中的message,不建议在代码中使用,严格模式下禁止使用,了解即可

eval

eval是一个特殊的函数,它可以将传入的字符串当做JavaScript代码来运行。

var jsstring = 'var message ="Hello world"; console.log(message);'
eval(jsstring) // Hello world

不建议在开发中使用eval :

  • eval代码的可读性非常的差(代码的可读性是高质量代码的重要原则);
  • eval是一个字符串,那么有可能在执行的过程中被刻意篡改,就可能会造成被攻击的风险;
  • eval的执行必须经过JS解释器,不能被JS引擎优化;