函数柯里化,经常可能就面试的时候听说过,反正一般开发我就很少用到过(自己层次不够吧)。老规矩,先上一段代码
// function isType(value,type) {
// return Object.prototype.toString.call(value) === `[object ${type}]`;
// }
// console.log(isType([1,2,3],'Array'))
//但是如果第二个参数传成'array','Arary'等,就会判断不是
function isType(type) {
return function (value) {
return Object.prototype.toString.call(value) === `[object ${type}]`;
}
}
let isArray = isType('Array');
console.log(isArray([1,2,3]))
console.log(isArray(1,2,3))
console.log(isType('Array')([1,2,3]))
上面代码也就是判断变量的类型,但是第一种和第二种的写法不大相同的,第一种写法是一个多个参数的函数,第二个却是变成了单一的参数。其实这就是柯里化。
柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数
上面我们虽然对函数进行了柯里化,但是我们不可能在需要柯里化的时候,都像上面不断地进行函数的嵌套;所以我们通过一个柯里化函数,实现通用的柯里化方法
const currying = (fn, arr = []) => {
let len = fn.length;
// return function (...args) {
// arr = [...arr,...args]
// console.log('arr',arr)
// if(arr.length < len){
// return currying(fn, arr)
// }
// else{
// return fn(...arr)
// }
// }
return function (...args) {
let concatVal = [...arr, ...args]
// console.log('arr',concatVal)
if (concatVal.length < len) {
return currying(fn, concatVal)
}
else {
return fn(...concatVal)
}
}
}
function isType(type, value) {
return Object.prototype.toString.call(value) === `[object ${type}]`;
}
let isArray = currying(isType)('Array');
let isString = currying(isType)('String');
console.log(isArray)
console.log(isArray([1, 2, 3])) //true
console.log(isArray('aaa')) //false
console.log(isString([1, 2, 3])) //false
console.log(isString('aaa')) //true
这里就有个经典的面试题了,实现sum(1,2,3,5)的效果和sum(1,2)(3)(5),sum(1,2,3)(5)一样
function sum() {
let args = [...arguments];
let res = function () {
args.push(...arguments);
// let result = args.reduce((pre, cur) => pre + cur)
return res
}
res.toString = function(){
return args.reduce((prev,cur)=>prev+cur)
}
return res
}
console.log(sum(1, 2, 3).toString(), sum(1).toString()) //6 1
console.log(sum(1)(2,5)().toString()) // 8
用法参考了点击这里跳转
function hello(name) {
console.log('Hello, ' + name);
}
setTimeout(hello('kangkang'), 3600); //立即执行,不会在3.6s后执行
setTimeout(function() {
hello('kangkang');
}, 3600); // 3.6s 后执行
当然,在ES5里面,我们也可以使用函数的bind方法,如下所示:
setTimeout(hello.bind(this, 'kangkang'), 3600); // 3.6s 之后执行函数
这样也是非常的方便快捷,并且可以绑定函数执行的上下文.
这里我们讨论函数的柯里化,当然我们这里也可以使用函数的柯里化来达到这个效果:
setTimeout(currying (hello, 'kangkang'), 3600); // 其中currying 是上面已经提及过的
let fs = require('fs');
let person = {}
fs.readFile('./1.txt', 'utf8', function(err,data){
console.log(data);
person.name = data
})
fs.readFile('./2.txt', 'utf8', function(err,data){
console.log(data);
person.age = data
})
console.log('person',person)
上面这段代码的结果是这样的:
挺明显的,我们想要拿到的是person {name:‘qiu’,age:‘18’},但是结果却还是个空对象。js中node的readFile方法是异步的,我们的代码也是基于node来运行的。异步是在同步代码后执行的,所以这里打印的的person还是空对象。肯定promise也是可以解决这个问题的,但这里重点讲回调。
let fs = require('fs');
let person = {}
//通过回调
let index = 0
let cb = () => {
index++;
if(index == 2){
console.log('person',person) //person { name: 'qiu', age: '18' }
}
// console.log(index)
}
fs.readFile('./1.txt', 'utf8', function(err,data){
person.name = data
cb()
})
fs.readFile('./2.txt', 'utf8', function(err,data){
person.age = data
cb()
})
可以在读取文件成功后调用函数,再设置个index进行判断结束条件就可以拿到异步结束后的值了。
使用闭包,还可以让代码灵活一点
let fs = require('fs');
let person = {}
function after(index, callback) {
// index--; //闭包函数,函数的定义的作用域和函数执行的作用域 不在同一个作用域下
//闭包是定义在一个函数内部的函数,内层函数可以访问外层函数的局部变量,这些变量被内层函数引用不会被回收,
//好处是使局部变量拥有更长的生命周期可以用来封装一段逻辑,坏处是闭包常驻内存造成内存泄露。
return function () {
index--;
if (index == 0) {
callback();
}
}
}
let cb = after(2, function () {
console.log(person)
})
fs.readFile('./1.txt', 'utf8', function (err, data) {
person.name = data
cb()
})
fs.readFile('./2.txt', 'utf8', function (err, data) {
person.age = data
cb()
})
记录一下,持续修改更进