---
title: 函数编程
date: 2018-06-09 16:29:00
updated: 2018-06-10 12:00:00
categories:
- web
tags:
- front end
---
目录
# 为什么用
# 单纯函数
# 高阶函数
# 函数收录
# 参考文献
# 同级文章
正文
# 为什么用
用这种编程思想无非是为了能更爽的写好代码. 总结一下函数式编程的优点有以下两个优点:
Less bug: 可读性高, 逻辑上更容易理解.
less Time: 更高的抽象层级, 代码高度可复用.
# 单纯函数
# 高阶函数
把函数作为变量传入别的函数, 或者一个函数的返回值是一个函数的函数.
# 函扁平化
function flattenDepth(array, depth = 1) {
let result = []
array.forEach(item => {//{1}
let d = depth
if (Array.isArray(item) && d > 0) {
result.push(...(flattenDepth(item, --d)))//{2}
} else {
result.push(item)
}
})
return result
}
console.log(flattenDepth([1, [2, [3, [4]], 5]])) // [ 1, 2, [ 3, [ 4 ] ], 5 ]
可圈可点:
①单纯函数:
②前沿技术:使用...去重数组元素。
③使用递归:({2})
④使用迭代:({1})
⑤深度支持:
# 函柯里化
function curry(func) {
var l = func.length
return function curried() {
var args = [].slice.call(arguments)
if(args.length < l) {
return function() {
var argsInner = [].slice.call(arguments)
return curried.apply(this, args.concat(argsInner))
}
} else {
return func.apply(this, args)
}
}
}
var f = function(a, b, c) {
return console.log([a, b, c])
};
var curried = curry(f)
curried(1)(2)(3) // => [1, 2, 3]
可圈可点:
①高阶函数:curry的传入值是一个函数func
,返回值是一个函数curried
。函数curried
的返回值是一个函数。
②使用闭包:curried
内访问外部变量 l
和func
。函数curried
的返回值函数在其内访问尾部变量 args
③改上下文:func.apply
④起到作用:参数够了就执行
# 函数防抖
function debounce(func, wait) {
var timer
return function() {
var context = this
var args = arguments
clearTimeout(timer)
timer = setTimeout(function() {
func.apply(context, args)
}, wait)
}
}
可圈可点:
①高阶函数:debounce的返回值是一个函数。
②使用闭包:debounce的返回值函数内访问外部变量 timer
,wait
和func
。函数curried
的返回值函数在其内访问尾部变量 args
③改上下文:func.apply
④异步机制:setTimeout
⑤回调函数:
function debounce(func, wait, leading, trailing) {
var timer, lastCall = 0, flag = true
return function() {
var context = this
var args = arguments
var now = + new Date()
if (now - lastCall < wait) {
flag = false
lastCall = now
} else {
flag = true
}
if (leading && flag) {
lastCall = now
return func.apply(context, args)
}
if (trailing) {
clearTimeout(timer)
timer = setTimeout(function() {
flag = true
func.apply(context, args)
}, wait)
}
}
}
# 函数节流
function throttle(func, wait) {
var timer
return function() {
var context = this
var args = arguments
if (!timer) {
timer = setTimeout(function () {
timer = null
func.apply(context, args)
}, wait)
}
}
}
function throttle(func, wait, leading, trailing) {
var timer, lastCall = 0, flag = true
return function() {
var context = this
var args = arguments
var now = + new Date()
flag = now - lastCall > wait
if (leading && flag) {
lastCall = now
return func.apply(context, args)
}
if (!timer && trailing && !(flag && leading)) {
timer = setTimeout(function () {
timer = null
lastCall = + new Date()
func.apply(context, args)
}, wait)
} else {
lastCall = now
}
}
}
# 函数组合
//用于组合
var compose = function(f,g) {
return function(x) {
return f(g(x));
};
};
//化纯函数
let add10 = value => value + 10;
let mult5 = value => value * 5;
let mult5AfterAdd10 = compose(mult5, add10)
mult5AfterAdd10(5)
//避层层套:
//let mult5AfterAdd10 = value => mult5(add10(value))
写出h(g(f(e(x))))
这样层层相套的代码,可读性远远高于嵌套一大堆的函数调用。
# 函允诺化
//已编函数时
function promisify (callbackBasedApi) {
return function promisified() {
const args = [].slice.call(arguments);
const _arguments = arguments; //{1}
return new Promise((resolve, reject) => {
args.push(function(err, result) { //{0}
if(err) {
return reject(err);
}
if(_arguments.length <= 1) { //{2}
resolve(result);
} else {
resolve([].slice.call(_arguments, 1)); //{3}
}
});
callbackBasedApi.apply(null, args);
});
}
};
//新编函数时
async ()=>{}
//连非允函时
(async ()=>())().then(fn).catch(handleErr)
# 化纯函数
把一些对象自带的方法转化为纯函数,不要命名转瞬即逝的中间变量
const f = str => str.toUpperCase().split('');
const toUpperCase = word => word.toUpperCase();
const split = x => (str => str.split(x));
const f = compose(split(''), toUpperCase)
f("aa vv")
//我们没有使用到str变量
# 数组求和
var foo = [1, 2, 3, 4, 5];
//不优雅
function sum(arr){
var x = 0;
for(var i = 0; i < arr.length; i++){
x += arr[i];
}
return x;
}
sum(foo) //15
//优雅
foo.reduce((a, b) => a + b) //15
# 数组过滤
var foo = [{
name: 'Stark',
age: 21
},{
name: 'Jarvis',
age: 20
},{
name: 'Pepper',
age: 16
}]
//我们希望得到结构稍微不同,age大于16的对象:
var result = [{
person: {
name: 'Stark',
age: 21
},
friends: []
},{
person: {
name: 'Jarvis',
age: 20
},
friends: []
}]
var result = foo
.filter(person => person.age > 16)
.map(person => ({
person: person,
friends: []
}))
# 避层层嵌
# 加速计算
var cacheSin =function (){
var cache = {};//{1}
return function(x){
if(cache[x]) console.log("the result is from cache!")
return cache[x] || (cache[x] = Math.sin(x));//{2}
}
}();
cacheSin(1) //第一次使用速度比较慢
cacheSin(1) //第二次使用有了cache,速度极快
//es6-简写版:
//var cacheSin = (()=>{var cache={}; return (x)=> (cache[x] || (cache[x] = Math.sin(x)))})()
可圈可点:
①高阶函数:cacheSin
的返回值是一个函数。
②使用闭包:在函数的内部访问其外部的变量cache
({2})。此处通过闭包的方式,缓存大量数学运算的结果。
③立即函数:使用了function(){}()
在写一些Canvas游戏或者其他WebGL应用的时候,经常有大量的数学运算,例如:Math.sin(1)
。Math.sin()的性能比较差,如果我们对精度要求不是太高,我们可以使用缓存。
# 函数唯一
//Create a cached version of a pure function.
function cached (fn) {
var cache = Object.create(null);//{1}
return (function cachedFn (str) {
return cache[str] || (cache[str] = fn(str))//{2}
})
}
var Sin = cached(cacheSin);//cacheSin是一个纯函数,见--加速计算--。
var task = [1, 1, 2, 2, 3, 3].map(v => Sin(v)).join("--");
console.log(task);
可圈可点:
①高阶函数:cached
的返回值是一个函数。
②使用闭包:在函数的内部({2})访问其外部的变量({1})。此处通过闭包的方式,缓存单纯函数的计算结果。
③存纯函数:在函数的内部({2})访问其外部的变量fn
。
④代码参考:vuejs源码
# 符驼峰化
var capitalize = cached(function (str) {
return str.charAt(0).toUpperCase() + str.slice(1)
});
var task = ["action", "fileNs", "fileSuffix", "className", "dirName", "fileName"].map(v => capitalize(v)).join("--");
console.log(task);
# 连驼峰符
var hyphenateRE = /\B([A-Z])/g;
var hyphenate = cached(function (str) {
return str.replace(hyphenateRE, '-$1').toLowerCase()
});
var task = ["action", "fileNs", "fileSuffix", "className", "dirName", "fileName"].map(v => hyphenate(v)).join("--");
console.log(task);
# 改上下文
function polyfillBind (fn, ctx) {//{1}
function boundFn (a) {
var l = arguments.length;
return l
? l > 1
? fn.apply(ctx, arguments)
: fn.call(ctx, a)
: fn.call(ctx)//{2}
}
boundFn._length = fn.length;
return boundFn
}
function nativeBind (fn, ctx) {
return fn.bind(ctx)
}
可圈可点:
①高阶函数:polyfillBind
的传入值fn
是一个函数,返回值boundFn
是一个函数。
②使用闭包:在函数的内部({2})访问其外部的变量({1})。此处通过闭包的方式,缓存传入的值函--数fn
。
③改上下文:在函数上下文this
的改变,至少已有这么几种方式fn.apply、fn.call、fn.bind。
④代码参考:vuejs源码
# 普通对象
# 助手函数
// these helpers produces better vm code in JS engines due to their
// explicitness and function inlining
function isUndef (v) {
return v === undefined || v === null
}
function isDef (v) {
return v !== undefined && v !== null
}
function isTrue (v) {
return v === true
}
function isFalse (v) {
return v === false
}
可圈可点
①单纯函数:
②严谨严格:在使用某一取值之前,需要十分确认拿到的值是什么,不然会引起很危险的问题。
③自定规则:js数据的几种基本类型,比如在判断真假时,会有一些内置的转换,比如undefined==》false,null==>false。为了确保永久有效以及防止对内部转换把握的不对,此处自定规则是一种推荐的做法。
# 原生类型
/**
* Check if value is primitive
*/
function isPrimitive (value) {
return (
typeof value === 'string' ||
typeof value === 'number' ||
// $flow-disable-line
typeof value === 'symbol' ||
typeof value === 'boolean'
)
}
# 输出字符
/**
* Get the raw type string of a value e.g. [object Object]
*/
var _toString = Object.prototype.toString;
# 对象检查
//是否对象类型
/**
* Quick object check - this is primarily used to tell
* Objects from primitive values when we know the value
* is a JSON-compliant type.
*/
function isObject (obj) {
return obj !== null && typeof obj === 'object'
}
//原生普通对象
/**
* Strict object type check. Only returns true
* for plain JavaScript objects.
*/
function isPlainObject (obj) {
return _toString.call(obj) === '[object Object]'
}
//获取对象类型
function toRawType (value) {
return _toString.call(value).slice(8, -1)
}
# 正则检查
function isRegExp (v) {
return _toString.call(v) === '[object RegExp]'
}
# 有效索引
/**
* Check if val is a valid array index.
*/
function isValidArrayIndex (val) {
var n = parseFloat(String(val));
return n >= 0 && Math.floor(n) === n && isFinite(val)
}
①单纯函数:
②严谨严格:在用数组索引对数据进行操作之前,先对要用的数组的索引取值,进行严格检查,避免引出一些问题。
# 转为字符
/**
* Convert a value to a string that is actually rendered.
*/
function toString (val) {
return val == null
? ''
: typeof val === 'object'
? JSON.stringify(val, null, 2)
: String(val)//{1}
}
可圈可点:
①单纯函数:
②严谨严格:
对JSON.stringify的参数含义还有些模糊,需要了解一下。
# 转为数字
/**
* Convert a input value to a number for persistence.
* If the conversion fails, return original string.
*/
function toNumber (val) {
var n = parseFloat(val);
return isNaN(n) ? val : n
}
可圈可点:
①单纯函数:
②严谨严格:当传入的字符非数字时,返回原字符,而不是NaN。避免出错或引起不必要的麻烦。
# 转为数组
/**
* Convert an Array-like object to a real Array.
*/
function toArray (list, start) {
start = start || 0;
var i = list.length - start;
var ret = new Array(i);
while (i--) {
ret[i] = list[i + start];
}
return ret
}
# 对象混合
/**
* Mix properties into target object.
*/
function extend (to, _from) {
for (var key in _from) {
to[key] = _from[key];
}
return to
}
# 对象合并
/**
* Merge an Array of Objects into a single Object.
*/
function toObject (arr) {
var res = {};
for (var i = 0; i < arr.length; i++) {
if (arr[i]) {
extend(res, arr[i]);//{1}
}
}
return res
}
# 返回某值
/**
* Return same value
*/
var identity = function (_) { return _; };
可圈可点:
①明式返回:通过统一的方式,返回传入的某一取值,明朗化。
# 生静态键
/**
* Generate a static keys string from compiler modules.
*/
function genStaticKeys (modules) {
return modules.reduce(function (keys, m) {
return keys.concat(m.staticKeys || [])
}, []).join(',')//{1}
}
可圈可点:
①高阶函数:genStaticKeys的返回值的某一操作是一个函数。
②使用迭代:modules.reduce
# 是否相等
/**
* Check if two values are loosely equal - that is,
* if they are plain objects, do they have the same shape?
*/
function looseEqual (a, b) {
}
可圈可点:
# 亲密位置
function looseIndexOf (arr, val) {
for (var i = 0; i < arr.length; i++) {
if (looseEqual(arr[i], val)) { return i }
}
return -1
}
可圈可点:
# 只执一次
/**
* Ensure a function is called only once.
*/
function once (fn) {
var called = false;
return function () {
if (!called) {
called = true;
fn.apply(this, arguments);
}
}
}
可圈可点:
①高阶函数:函数的传入值是一个函数fn
,返回值是一个函数。
②绑上下文:fn.apply(this, arguments)
③使用闭包:通过闭包缓存执行次数标识called
以及函数的传入值fn
# 受保护的
/**
* Check if a string starts with $ or _
*/
function isReserved (str) {
var c = (str + '').charCodeAt(0);
return c === 0x24 || c === 0x5F
}
可圈可点:
①单纯函数:这个函数的返回值是固定的。
②自定规则:以$
或者_
开头的是受保护的。
# 定义属性
/**
* Define a property.
*/
function def (obj, key, val, enumerable) {
Object.defineProperty(obj, key, {
value: val,
enumerable: !!enumerable,
writable: true,
configurable: true
});
}
①单纯函数:这个函数的返回值是固定的。
②前沿技术:使用es6的(es5?)Object.defineProperty
定义对象属性。
# 解析路径
/**
* Parse simple path.
*/
var bailRE = /[^\w.$]/;
function parsePath (path) {
if (bailRE.test(path)) {
return
}
var segments = path.split('.');//{1}
return function (obj) {
for (var i = 0; i < segments.length; i++) {
if (!obj) { return }
obj = obj[segments[i]];//{2}
}
return obj
}
}
可圈可点:
①高阶函数:parsePath的返回值是一个函数。
②使用闭包:通过闭包缓存外部变量 segments
({1})以及读取({2})
③使用正则:bailRE
# 环境检测
// can we use __proto__?
var hasProto = '__proto__' in {};
// Browser environment sniffing
var inBrowser = typeof window !== 'undefined';
var inWeex = typeof WXEnvironment !== 'undefined' && !!WXEnvironment.platform;
var weexPlatform = inWeex && WXEnvironment.platform.toLowerCase();
var UA = inBrowser && window.navigator.userAgent.toLowerCase();
var isIE = UA && /msie|trident/.test(UA);
var isIE9 = UA && UA.indexOf('msie 9.0') > 0;
var isEdge = UA && UA.indexOf('edge/') > 0;
var isAndroid = (UA && UA.indexOf('android') > 0) || (weexPlatform === 'android');
var isIOS = (UA && /iphone|ipad|ipod|ios/.test(UA)) || (weexPlatform === 'ios');
var isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge;
# 生成集合
/**
* Make a map and return a function for checking if a key
* is in that map.
*/
function makeMap (
str,
expectsLowerCase
) {
var map = Object.create(null);//{1}
var list = str.split(',');
for (var i = 0; i < list.length; i++) {
map[list[i]] = true;
}
return expectsLowerCase
? function (val) { return map[val.toLowerCase()]; }
: function (val) { return map[val]; }//2
}
var isBuiltInTag = makeMap('slot,component', true);
可圈可点:
①高阶函数:makeMap 的返回值是一个函数。
②使用闭包:通过闭包缓存外部变量map({1})以及读取({2})
③自定规则:
# 异步控制
# 参考文献
fronter. JavaScript 五大常见函数.2018-03-18.segmentfault
林小新.函数式编程(三).2017-07-07.segmentfault
ycczkl .Javascript函数式编程小结.2016-07-19.segmentfault
网友评论