在线教育网站用什么做百度网站推广费用多少
文章目录
- 基本概念
- 柯里化(Currying)是什么?
- 通用的柯里化实现
- ES5 实现
- ES6 实现
基本概念
在讲柯里化之前我们先来了解一些基本概念:
Function.length: length 属性指明函数的形参个数
function func1() {}
function func2(a, b) {}console.log(func1.length); // 0
console.log(func2.length); // 2
Arguments 对象: 是一个对应于传递给函数的参数的类数组对象
- arguments 对象是所有(非箭头)函数中都可用的局部变量
- “类数组”意味着 arguments 有长度属性,并且属性的索引是从零开始的,但是它没有 Array的内置方法,例如 forEach() 和 map()都是没有的
- arguments.length:本次函数调用时传入函数的实参数量(这个数字可以比形参数量大,也可以比形参数量小)
- arguments 可以被转换为一个真正的 Array
// arguments 转换为一个真正的 Array
var args = Array.prototype.slice.call(arguments);
var args = [].slice.call(arguments);// ES6
const args = Array.from(arguments);
const args = [...arguments];
这里小提一下: How does Array.prototype.slice.call
work?
.call()和.apply()方法允许您在函数中手动设置this的值。因此,如果我们将.slice()中的this的值设置为一个类似数组的对象,.slice()将假设它正在处理一个数组,并将执行它的任务。
剩余参数:允许我们将一个不定数量的参数表示为一个数组
如果函数的最后一个命名参数以…为前缀,则它将成为一个由剩余参数组成的真数组,其中从0(包括)到theArgs.length(排除)的元素由传递给函数的实际参数提供
function sum1(...theArgs) {console.log(theArgs)
}sum1(1, 2, 3); // [1, 2, 3]function sum2(a, ...theArgs) {console.log(theArgs)
}sum2(1, 2, 3); // [2, 3]
注:剩余参数只包含那些没有对应形参的实参,而 arguments 对象包含了传给函数的所有实参。
柯里化(Currying)是什么?
柯里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。核心思想是把多参数传入的函数拆成单参数(或部分)函数,内部再返回调用下一个单参数(或部分)函数,依次处理剩余的参数。
举个例子:
// 传统写法
function sum(a, b, c) {return a + b + c
}
console.log(sum(1,2,3)); // 6// 柯里化
function sum(a) {return function (b) {return function (c) {return a + b + c}}
}
console.log(sum(1)(2)(3)); // 6// 将 a + b + c 的操作提取成一个方法
function curry(fn) {return function (a) {return function (b) {return function (c) {return fn(a, b, c);};};}
}function add(a, b, c) {return a + b + c;
}
var sum = curry(add);
console.log(sum(1)(2)(3)); // 6
这样就对一个函数实现了柯里化。但这个 curry 函数并不通用,实际开发过程中参数数量具有不确定性:比如 sum(1)(2,3) 或 sum(1,2)(3) ,或是一个函数需要传 n 个参数,如下:
function curry(fn) {return function (a1) {return function (a2) {return function (a3) {//......return function (aN) {return fn(a1, a2, a3, ...aN);};};};};
}
我们需要实现一个通用的柯里化函数,对于参数的不确定性,我们可以通过使用 arguments 和 递归 来处理。
通用的柯里化实现
在下面实现代码中所用到的一些属性、对象、方法我们已在基本概念中介绍过,方便大家理解。
ES5 实现
// 法一 (递归调用 judge)
function curry(fn) {return function judge() {// arguments 转换为数组var _args = Array.prototype.slice.call(arguments);// 判断首次(或组合后)所传的参数数量 是否小于 fn 方法的形参个数if (_args.length < fn.length) {// 首次(或组合后)所传的参数数量 小于 fn 方法的形参个数, 内部再返回调用下一个单参数(或部分)函数return function (){// 获取当前参数数组var _args2 = Array.prototype.slice.call(arguments);// 将之前所传的参数和当前的参数组合,递归调用return judge.apply(this, _args.concat(_args2))};} else {// 首次(或组合后)所传的参数数量 等于 fn 方法的形参个数,则直接调用 fn 函数return fn.apply(this, _args);}}
}// 法二 (递归调用 curry)
function curry(fn, args) {var args = args || []; // 首次为 []return function () {var _args = args.concat([].slice.call(arguments));if (_args.length < fn.length) {return curry.call(this, fn, _args);} else {return fn.apply(this, _args);}}
}function add(a, b ,c) {return a + b + c;
}const addCurry = curry(add);console.log(addCurry(1,2)(3)) // 6console.log(addCurry(1)(2)(3)) // 6console.log(addCurry(1)(2,3)) // 6
简而言之,就是将所有实际的传参组合起来,如果实际参数个数等于 fn 方法的形参个数后便调用 fn 方法。
ES6 实现
// 法一 (递归调用 judge)
const curry = (fn) => {return function judge(...args) {if (args.length < fn.length) {return function (...args2) {return judge(...args, ...args2);};}return fn(...args);};
}// 法二 (递归调用 curry)
const curry = (fn, ...arg) => (arg.length >= fn.length ? fn(...arg) : (..._arg) => curry(fn, ...arg, ..._arg))function add(a, b ,c) {return a + b + c;
}const addCurry = curry(add);console.log(addCurry(1,2)(3)) // 6console.log(addCurry(1)(2)(3)) // 6console.log(addCurry(1)(2,3)) // 6
想要明白柯里化函数,还是建议大家动手写写,在写的过程中分析执行步骤,方便理解。