函数编程

函数编程

2018-09-19  本文已影响0人  一点金光
---
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内访问外部变量 lfunc。函数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的返回值函数内访问外部变量 timerwaitfunc。函数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

# 同级文章
元式编程
对象编程

上一篇 下一篇

猜你喜欢

热点阅读