Day09 - 闭包应用1 - 制造惰性函数

3,152 阅读4分钟

365天打卡记录

🔥 创作不易、大家帮然叔 B栈 一键三连

📺 同步视频 一键三连

www.bilibili.com/video/BV1gr…

基本概念

惰性函数是函数是编程的一个重要概念。可以有效提高程序的运行效率。

惰性函数表示函数执行的分支只会在函数第一次调用的时候执行,在第一次调用过程中,该函数会被覆盖为另一个按照合适方式执行的函数,这样任何对原函数的调用就不用再经过执行的分支了。

如何实现计算缓存

假设有一个计算比如计算 平方,非常耗费时间。

需要把平方缓存起来,这样就不需要每次都计算了。

var cache = {}
function square(n) {
    if (!cache[n]) {
        cache[n] = n * n;
     }
     return cache[n];
}

使用闭包隐藏全局变量

以上代码虽然可以实现功能,但是最大的问题就是缓存需要定义一个全局变量。其实这个变量是可以隐藏的。

var square = (function () {
    var cache = {};
    return function(n) {
        if (!cache[n]) {
            cache[n] = n * n;
        }
        return cache[n];
    }
})();

惰性函数还能做什么

单例模式

单例模式是设计模式之一,它保证了一个类只有一个实例。实现方法一般是先判断实例是否存在,如果存在就直接返回,否则就创建了再返回。单例模式的好处就是避免了重复实例化带来的内存开销。

// 单例模式
function Singleton(){
  this.data = 'singleton';
}
​
Singleton.getInstance = (function () {
  var instance;
    
  return function(){
    if (instance) {
      return instance;
    } else {
      instance = new Singleton();
      return instance;
    }
  }
})();
​
var sa = Singleton.getInstance();
var sb = Singleton.getInstance();
console.log(sa === sb); // true
console.log(sa.data); // 'singleton'

单例模式前端最典型的应用场景,全局唯一消息框

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .model {
            width: 200px;
            height: 200px;
            border: 1px solid aqua;
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            text-align: center;
        }
    </style>
</head><body>
    <div id="loginBtn">点我</div>
    <script>
        var getSingle = function (fn) {
            var result; //缓存实例
            return function () {
                return result || (result = fn.apply(this, arguments))
            }
        }
        var createLoginLayer = function () {
            var oDiv = document.createElement("div");
            oDiv.innerHTML = "我是登录浮窗";
            oDiv.className = "model";
            oDiv.style.display = "none";
            document.body.appendChild(oDiv);
            return oDiv;
        }
        var createSingleLoginLayer = getSingle(createLoginLayer);
        document.getElementById("loginBtn").onclick = function () {
            //动态创建弹窗
            //新建一个弹窗实例,内部使用单例模式管理,一直只能有一个.
            var loginLayer = createSingleLoginLayer();
            loginLayer.style.display = "block"
        }
    </script>
</body>

提高浏览器兼容问题的执行效率

为了解决浏览器之间的行为差异,经常会在代码中包含了大量的 if 语句,以检查浏览器特性,解决不同浏览器的兼容问题。

显然这些if语句我们只希望在一个浏览器中只执行一遍。

function addEvent(type, element, fun) {
  if (element.addEventListener) {
    element.addEventListener(type, fun, false);
  }
  else if (element.attachEvent) {
    element.attachEvent('on' + type, fun);
  }
  else {
    element['on' + type] = fun;
  }
}

最佳的办法就是使用惰性函数将结果缓存起来。

function addEvent(type, element, fun) {
  if (element.addEventListener) {
    addEvent = function (type, element, fun) {
      element.addEventListener(type, fun, false);
    }
  }
  else if (element.attachEvent) {
    addEvent = function (type, element, fun) {
      element.attachEvent('on' + type, fun);
    }
  }
  else {
    addEvent = function (type, element, fun) {
      element['on' + type] = fun;
    }
  }
  return addEvent(type, element, fun);
}
​

第一次执行: 选择合适的api并且执行

第二次执行: 由于addEvent已经被指定为其中的一种

最后我们再看看ajax兼容性的代码

没有惰性

function createXHR(){
  var xhr = null;
  try {
    // Firefox, Opera 8.0+, Safari,IE7+
    xhr = new XMLHttpRequest();
  }
  catch (e) {
    // Internet Explorer 
    try {
      xhr = new ActiveXObject("Msxml2.XMLHTTP");
    }
    catch (e) {
      try {
        xhr = new ActiveXObject("Microsoft.XMLHTTP");
      }
      catch (e) {
        xhr = null;
      }
    }
  }
  return xhr;
}
​

惰性

function createXHR() {
  var xhr = null;
  if (typeof XMLHttpRequest != "undefined") {
    xhr = new XMLHttpRequest();
    createXHR = function () {
      return new XMLHttpRequest();
    };
  } else {
    try {
      xhr = new ActiveXObject("Msxml2.XMLHTTP");
      createXHR = function () {
        return new ActiveXObject("Msxml2.XMLHTTP");
      };
    } catch (e) {
      try {
        xhr = new ActiveXObject("Microsoft.XMLHTTP");
        createXHR = function () {
          return new ActiveXObject("Microsoft.XMLHTTP");
        };
      } catch (e) {
        createXHR = function () {
          return null;
        };
      }
    }
  }
  return xhr;
}
​

面试攻略

点评

  • 闭闭包处处都有,但是能说出经典应用又是一个难题。说Helloworld和背题没啥区别。

365天打卡记录

🔥 创作不易、大家帮然叔 B栈 一键三连