闭包和高阶函数学习笔记

2019-06-08  本文已影响0人  回调的幸福时光

一、闭包

闭包的形成与变量的作用域以及变量的生存周期密切相关。

1.1 变量的作用域

变量的作用域:指变量的有效范围。

变量的作用域

这里还涉及到“变量声明方式”、“变量提升”的影响。

1.2 变量的生存周期

二、闭包的应用场景

2.1 封装变量

把不需要暴露在全局的变量封装成“私有变量”。

用闭包实现累乘运算,并且具有缓存机制。

var mult = (function (){
  var cache = {}; // 缓存结果
  // 提炼小函数
  var calculate = function () {
    var sum = 1;
    for (var i = 0, l = arguments.length; i < l; i++) {
      sum *= arguments[i];
    }
    return sum;
  }

  return function () {
    var args = Array.prototype.join.call(arguments, ',');
    if (cache[args]) {
      return cache[args];
    }
    return cache[args] = calculate.apply(null, arguments);
  }
})();
2.2 面向对象设计

过程与数据的结合是形容面向对象中的“对象”时经常使用的表达。对象以方法的形式包含了过程,而闭包则是在过程中以环境的形式包含了数据。

var extent = function () {
  var value = 0;
  return {
    call: function () {
      value++;
      console.log(value);
    }
  }
}
2.3 闭包和内存管理

三、高阶函数

高阶函数是指至少满足下列条件之一的函数:

3.1 函数作为参数传递

把函数当作参数传递,代表我们可以抽离出一部分容易变化的业务场景,把这部分业务逻辑放在函数参数中,分离业务代码中变化与不变的部分。

场景举例:回调函数、Array.prototype.sort

3.2 函数作为返回值

判断数据类型

var isType = function (type) {
  return function (obj) {
    return Object.prototype.toString.call(obj) === `[object ${type}]`;
  }
}
var isString = isType('String');
var isArray = isType('Array');

3.3 实现 AOP

AOP (面向切面编程) :把一些跟核心业务逻辑模块无关的功能抽离出来,再通过“动态织入”的方式掺入业务逻辑模块中。
通常在 JavaScript 中实现 AOP,都是指把一个函数“动态织入”到另外一个函数之中。

Function.prototype.before = function(beforefn) {
  var __self = this; // __self 表示原函数
  return function() {
    beforefn.apply(this, arguments);
    return __self.apply(this, arguments);
  }
}
Function.prototype.after = function(afterfn) {
  var __self = this;
  return function () {
    var ret = __self.apply(this, arguments);
    afterfn.apply(this, arguments);
    return ret;
  }
}
var func = function() {
  console.log(‘something’);
}
func.before(function(){
  console.log('before');
}).after(function(){
  console.log('after');
})

3.4 柯里化(currying)

一个 currying 的函数首先会接收一些参数,但不会立即求值,而是返回另外一个函数,刚才传入的参数在函数形成的闭包中被保存起来,等到函数真正需要求值的时候,之前传入的所有参数都会被一次性用于求值。

以下是一个示例:用于实现灵活地 mult()

mult(1,2)(3)();
mult(1)(2,3)();
mult(1)(2)(3)();
var currying = function (fn) {
  var args = [];
   function next() {
    if (arguments.length === 0) {
      const res = fn.apply(this, args);
      args = []; // 计算完毕后,清空缓存的所有参数。
      return res;
    } else {
      [].push.apply(args, arguments);
      return next;
    }
   }
    return next;
}
var calculate = function () {
    var sum = 1;
    for (var i = 0, l = arguments.length; i < l; i++) {
      sum *= arguments[i];
    }
    return sum;
  }

var mult = currying(calculate);

mult(1,2)(3)();
mult(1)(2,3)();
mult(1)(2)(3)();

有的文章是用以下方式,可以使 mult(1)(2)(3)() -> mult(1)(2)(3)
但有个弊端是,toString() 的调用时机最晚,如果连续多次调用 mult() 方法,将导致参数是共享的。

next.toString = function () {
  const res = fn.apply(this, args);
  return res;
}
toString的弊端
3.5 uncurrying
Function.prototype.uncurrying = function () {
  var self = this;
  return function() {
    var obj = Array.prototype.shift.call(arguments);
    return self.apply(obj, arguments);
  }
}

另外的版本:

Function.prototype.uncurrying = function () {
  var self = this;
  return function() {
    return Function.prototype.call.apply(self, arguments);
   }
}

3.6 节流函数

var throttle = function (fn, interval) {
  var __self = fn,
      timer,
      firstTime = true;
  return function () {
    var args = arguments,
        __me = this;
    
    if (firstTime) {
      __self.apply(__me, args);
      return firstTime = false;
    }
    if (timer) {
      return false;
    }
    timer = setTimeout(function (){
      clearTimeout(timer);
      timer = null;
      __self.apply(__me, args);
    }, interval || 500);
  }
}

3.7 分时函数

var timeChunk = function (arr, fn, count) {
  var obj,
      t;
  var len = ary.length;
  var start = function (){
    for (var i = 0; i < Math.min(count || 1, ary.length); i++) {
      var obj = ary.shift();
      fn(obj);
    }
  }
  return function () {
    t = setInterval(function() {
      if (arr.length === 0) {
        return clearInterval(t);
      }
      start();
    })
  }
}

3.8 惰性加载函数

重写函数。

参考

js 闭包、常见认识误区与主要应用
十年踪迹:征服 JavaScript 面试:什么是闭包?

上一篇 下一篇

猜你喜欢

热点阅读