纯函数:
    对于相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用,也不依赖外部环境的状态。

    1. var arr = [1,2,3,4,5];
    2. //纯函数 arr.slice(0,3); //=> [1,2,3]
    3. arr.slice(0,3); //=> [1,2,3]
    4. //非纯函数
    5. arr.splice(0,3); //=> [1,2,3]
    6. arr.splice(0,3); //=> [4,5]

    定定义
    函数式编程为何排斥不纯的函数:
    非纯函数中,函数的行为需要由外部的系统环境决定。也就是说此函数行为不仅取决于输入的参数 age,还取决于一个外部的变量 timeOfLife。这种对于外部状态的依赖,是造成系统复杂性大大提高的主要原因。

    1. var timeOfLife = 20;
    2. //纯函数 function test(age){
    3. return age > 20;
    4. }
    5. //非纯函数
    6. function test(age){
    7. return age > timeOfLife;
    8. }

    函数柯里化(Currying)
    向函数传递一部分参数来调用它,让它返回一个函数去处理剩下的参数。
    事实上柯里化是一种“预加载”函数的方法,通过传递较少的参数,得到一个已经记住了这些参数的新函数,某种意义上讲,这是一种对参数的“缓存”,是一种非常高效的编写函数的方法。

    1. var timeOfLife = 20;
    2. function test(timeOfLife){
    3. return function(age){
    4. return age > timeOfLife;
    5. }
    6. }
    7. var testing = test(20);
    8. testing(18); // => false

    函数组合
    为避免写出不优雅的包菜式代码 h ( g ( f ( x ) ) ) ?我们需要用到函数组合。
    我们定义的compose就像双面胶一样,可以把任何两个纯函数结合到一起,也可以扩展出组合N个函数的N面胶。
    这种灵活的组合,让我们可以像拼积木一样优雅地组合函数式代码。

    1. //两个函数的组合
    2. var compose = function(f, g){
    3. return function(x){
    4. return f(g(x));
    5. };
    6. };
    7. var mult = function(x){
    8. return x*5;
    9. }
    10. var add = function(x){
    11. return x+1;
    12. }
    13. compose(mult, add)(2); //=> 15

    命令式代码:
    通过编写一条又一条指令,让计算机执行一些动作,其中一般会涉及许多繁杂的细节。

    声明式代码:
    通过写表达式的方式,声明我们想干什么,而不是通过一步一步的指示。
    声明式代码,是函数式编程的一个明显好处——编写、优化代码时能更专注。

    1. //命令式
    2. var rest = [];
    3. var arr = [4,9,16,25,4,16];
    4. for(var i = 0; i < arr.length; i++){
    5. if(rest.indexOf(arr[i]) === -1){
    6. rest.push(arr[i]);
    7. }
    8. }
    9. //声明式
    10. var rest = arr.map(Math.sqrt);

    函数式编程的一个明显的好处就是这种声明式的代码,对于无副作用的纯函数,我们完全可以不考虑函数内部是如何实现的,专注于编写业务代码。优化代码时,目光只需要集中在这些稳定坚固的函数内部即可。
    相反,不纯的不函数式的代码会产生副作用或者依赖外部系统环境,使用它们的时候总是要考虑这些副作用。

    总结:

    • 函数对于外部状态的依赖,是造成系统复杂性大大提高的主要原因。
    • 代码书写中让函数尽可能地纯净。
    • 函数式编程不是万能的,它与OOP 一样,只是一种编程范式。
    • 为降低软件复杂度,OOP 的方式是靠良好的封装、继承、多态以及接口定义。函数式编程则是靠纯函数以及它们的组合、柯里化等技术。

    underscore整体结构:
    通过对下面文章的学习可以对underscore有一个初步的学习和认识。
    https://www.html.cn/archives/4728

    • underscore结构
    • 面向对象风格支持

      1. 虽然underscore推崇函数式编程,但也支持面向对象风格的函数调用,仅需要通过_()来包裹对象。
    • mixin

      1. mixin(混入)模式也是增加代码复用度的一个广泛使用的设计模式。
    • 链接式调用

    1. var data = {
    2. name: 'Max',
    3. age: 21,
    4. change: function(){
    5. }
    6. };
    7. // var d1 = _.unique([21, 12, 10, 21, 'a', 'A'], function(value) {
    8. // return typeof value === 'string' ? value.toLowerCase() : value;
    9. // });
    10. // console.log(d1);
    11. var d2 = _([1,2,1,2,3,6,5,'A',6,'a']).chain().unique().map().value();
    12. console.log(d2);
    1. (function(root) {
    2. var push = Array.prototype.push;
    3. var _ = function(obj) {
    4. if(obj instanceof _) {
    5. return obj;
    6. }
    7. // 第一次调用时,this指向window,所以会执行new _(obj)
    8. // 第二次调用时,this指向当前underscore对象,所以会跳过这一步执行
    9. if(!(this instanceof _)) {
    10. return new _(obj);
    11. }
    12. // 将对象引用封装在_wrapped属性中
    13. this._wrapped = obj;
    14. };
    15. _.unique = function(arr, callback) {
    16. var ret = [];
    17. var i = 0, target;
    18. for(; i < arr.length; i++) {
    19. target = callback ? callback(arr[i]) : arr[i];
    20. if(ret.indexOf(target) === -1) {
    21. // push arr[i]比target 更好
    22. ret.push(arr[i]);
    23. }
    24. }
    25. return ret;
    26. }
    27. //开启链式调用
    28. _.chain = function(obj){
    29. var instance = _(obj);
    30. instance._chain = true;
    31. return instance;
    32. };
    33. //辅助函数
    34. var result = function(instance, obj) {
    35. return instance._chain ? _(obj).chain() : obj;
    36. };
    37. _.prototype.value = function() {
    38. return this._wrapped;
    39. }
    40. _.functions = function (obj) {
    41. var result = [];
    42. var key;
    43. for(key in obj) {
    44. result.push(key);
    45. }
    46. return result;
    47. };
    48. _.map = function(args){
    49. args.push('David');
    50. return args;
    51. };
    52. //类型检测
    53. _.isArray = function(obj){
    54. return toString.call(obj) === '[object Array]';
    55. };
    56. _.each = function(target, callback){
    57. var key,i=0;
    58. if(_.isArray(target)) {
    59. var length = target.length;
    60. for(; i < length; i++) {
    61. callback.call(target, target[i], i);
    62. }
    63. }
    64. else {
    65. for(key in target) {
    66. callback.call(target, key, target[key]);
    67. }
    68. }
    69. };
    70. _.mixin = function(obj) {
    71. _.each(_.functions(obj), function(name) {
    72. var func = obj[name];
    73. _.prototype[name] = function() {
    74. var args = [this._wrapped];
    75. push.apply(args, arguments);
    76. return result(this, func.apply(this, args));
    77. };
    78. });
    79. }
    80. _.mixin(_);
    81. root._ = _;
    82. })(this);

    undefined 的处理和 iteratee

    1. var obj = {name: 'Max', age: 21};
    2. console.log(_.map([1, 2, 3], function(value, index, object){
    3. return value * 3;
    4. }, obj));
    1. //默认迭代器
    2. _.identity = function(value) {
    3. return value;
    4. }
    5. function cb(iteratee, context) {
    6. if(!iteratee) {
    7. return _.identity;
    8. }
    9. if(_.isFunction(iteratee)) {
    10. console.log(2222);
    11. return optimizeCb(iteratee, context);
    12. }
    13. }
    14. //optimizeCb 优化迭代器
    15. var optimizeCb = function(func, context, count) {
    16. if(context == void 0) {
    17. return func;
    18. }
    19. // count 为 undefined,则默认值为3
    20. switch(count == null ? 3 : count) {
    21. case 1:
    22. return function(value) {
    23. return func.call(context, value);
    24. };
    25. case 3:
    26. return function(value, index, obj) {
    27. return func.call(context, value, index, obj);
    28. };
    29. }
    30. }
    31. _.map = function(obj, iteratee, context){
    32. //生成不同功能的迭代器
    33. var iteratee = cb(iteratee, context);
    34. // 区分数组和对象
    35. var keys = !_.isArray(obj) && Object.keys(obj);
    36. var length = (keys || obj).length;
    37. var i = 0;
    38. var result = Array(length);
    39. for(i; i< length;i++) {
    40. var currentKey = keys ? keys[i] : i;
    41. result[i] = iteratee(obj[currentKey], i, obj);
    42. }
    43. return result;
    44. };

    rest参数:

    1. function test(count1, count2, rest){
    2. console.log(count1);
    3. console.log(count2);
    4. console.log(rest);
    5. }
    6. var myTest = _.restAuguments(test);
    7. myTest(1, 2, 3, 4, 5, 6, 7);
    1. _.restAuguments = function(func) {
    2. // rest参数位置
    3. var startIndex = func.length - 1;
    4. return function() {
    5. var index = 0;
    6. var length = arguments.length - startIndex;
    7. var rest = Array(length);
    8. for(; index < length; index++) {
    9. rest[index] = arguments[index + startIndex];
    10. }
    11. var args = Array(func.length);
    12. for(index=0; index < startIndex; index++) {
    13. args[index] = arguments[index];
    14. }
    15. args[startIndex] = rest;
    16. return func.apply(this, args);
    17. };
    18. }

    Object.create **polyfill:**

    polyfill(聚酯填充物),这里可以理解为Object.create 的补丁

    1. // Object.create polyfill
    2. var baseCreate = function(prototype){
    3. if(!_.isObject(prototype)) return {};
    4. if(Object.create) {
    5. return Object.create(prototype);
    6. }
    7. Ctor.prototype = prototype;
    8. var res = new Ctor;
    9. Ctor.prototype = null;
    10. return res;
    11. };

    reduce和真值检测函数:

    1. // reduce
    2. var createReduce = function(dir){
    3. var reduce = function(obj, iteratee, memo, init) {
    4. // 判断obj为数组或对象
    5. var keys = !_.isArray(obj) && Object.keys(obj);
    6. var length = (keys || obj).length;
    7. var index = dir > 0 ? 0 : length-1;
    8. // 如果没传memo的默认值,则从第一个元素开始
    9. if(!init) {
    10. memo = obj[keys ? keys[index] : index];
    11. index += dir;
    12. }
    13. // dir为1,表示从头到尾;尾-1则表示从尾到头
    14. for(; index >= 0 && index < length; index += dir) {
    15. var currentKey = keys ? keys[index] : index;
    16. memo = iteratee(memo, obj[currentKey], currentKey, obj);
    17. }
    18. return memo;
    19. };
    20. //返回函数
    21. return function(obj, iteratee, memo, context) {
    22. var init = arguments.length >= 3;
    23. return reduce(obj, optimizeCb(iteratee, context, 4), memo, init);
    24. };
    25. };
    26. _.reduce = createReduce(1); // 1 or -1,1表示从头开始,-1表示从尾开始
    27. _.filter = _.select = function(obj, predict, context){
    28. var results = [];
    29. predict = cb(predict, context);
    30. _.each(obj, function(value, index){
    31. if(predict(value, index)) {
    32. results.push(value);
    33. }
    34. });
    35. return results;
    36. };

    index和lastIndexOf:

    1. _.sortedIndex = function(array, obj, iteratee, context) {
    2. // 如果iteratee是null, 则cb返回 function(value){ return value;}
    3. iteratee = cb(iteratee, context, 1);
    4. value = iteratee(obj);
    5. var low = 0;
    6. var high = array.length-1; // 长度减一
    7. while(low < high) {
    8. var mid = Math.floor((low+high)/2);
    9. if(array[mid] < value) {
    10. low = mid + 1;
    11. }
    12. else {
    13. high = mid;
    14. }
    15. }
    16. // 返回low或high都可
    17. return low;
    18. }
    19. // 1:正向查找,-1:反向查找
    20. function createPredicateIndexFinder(dir) {
    21. return function(array, predicate, context) {
    22. predicate = cb(predicate, context);
    23. var length = array.length;
    24. var index = dir > 0 ? 0 : length-1;
    25. for(; index>=0 && index < length; index+=dir) {
    26. if(predicate(array[index], index, array)) {
    27. return index;
    28. }
    29. }
    30. return -1;
    31. };
    32. }
    33. // 1为正向查找,-1为反向查找
    34. _.findIndex = createPredicateIndexFinder(1);
    35. _.findLastIndex = createPredicateIndexFinder(-1);
    36. function createIndexFinder(dir, predicateFind, sortedIndex) {
    37. // 调用形式:array, item, idx,idx表示是否已经为有序
    38. return function(array, item, idx){
    39. var i = 0;
    40. var length = array.length;
    41. //如果已经为正序,则调用折半查找
    42. if(idx && _.isBoolean(idx) && length) {
    43. idx = sortedIndex(array, item);
    44. return array[idx] === item ? idx : -1;
    45. }
    46. // 特殊情况,要查找的元素是NaN
    47. if(item !== item) {
    48. idx = predicateFind(slice.call(array, 0, length), _.isNaN);
    49. return idx >=0 ? idx : -1;
    50. }
    51. //正常遍历
    52. for(idx = (dir > 0 ? i : length-1); idx >= 0 && idx < length; idx +=dir) {
    53. if(array[idx] === item) {
    54. return idx;
    55. }
    56. }
    57. return -1;
    58. };
    59. }
    60. _.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex);
    61. _.lastIndexOf = createIndexFinder(-1, _.findLastIndex);

    洗牌算法:

    1. console.log(_.sample([1,2,3,4,5,6,7,8,9,10],5));
    2. console.log(_.shuffle([1,2,3,4,5,6,7,8,9,10]));
    1. _.clone = function(obj) {
    2. return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
    3. };
    4. //随机数函数
    5. _.random = function(min, max) {
    6. // 如果最大值不传,则取0到最小值之间的数
    7. if(max == null) {
    8. max = min;
    9. min = 0;
    10. }
    11. // Math.random()为取0到1之间的随机数,可以等于0,但是一定小于1
    12. // 返回的随机数可能等于最大值或最小值
    13. return min + Math.floor((Math.random() * (max - min + 1)));
    14. };
    15. _.shuffle = function(array) {
    16. return _.sample(array, Infinity);
    17. };
    18. _.sample = function(array, n) {
    19. // 判断是否是Array
    20. if(!_.isArray(array)) {
    21. return array;
    22. }
    23. //判断n的合法性
    24. if(n == null) {
    25. return array[_.random(array.length-1)];
    26. }
    27. var length = array.length;
    28. var tempArray = _.clone(array);
    29. var last = array.length -1;
    30. // 获取n和length的最小值,为什么不这样:n = n>length ? length : n;
    31. n = Math.max(Math.min(n, length),0);
    32. for(var index = 0; index < n; index++) {
    33. var ran = _.random(index, last);
    34. var temp = tempArray[index];
    35. tempArray[index] = tempArray[ran];
    36. tempArray[ran] = temp;
    37. }
    38. return tempArray.slice(0, n);
    39. };

    非纯函数中,函
    数的行为需要由外部的系统环境决定。也就是说此函数行为不仅取决于输入的参
    数 age,还取决于一个外部的变量 timeOfLife。
    ::
    对于相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用,也不依赖外部环境的状态。
    义:
    对于相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用,也不依赖外部环境的状态。