JavaScript 性能优化——惰性载入函数

原文首发于 Guanngxu 的个人博客JavaScript 性能优化——惰性载入函数

参考资料:
《JavaScript 高级程序设计(第三版)》
JavaScript专题之惰性函数
深入理解javascript函数进阶之惰性函数

因为不同厂商的浏览器相互之间存在一些行为上的差异,很多 js 代码包含了大量的if语句,将执行引导到正确的分支代码中去,比如下面的例子。

function createXHR() {
    if (typeof XMLHttpRequest != 'undefined') {
        return new XMLHttpRequest();
    } else if (typeof ActiveXObject != 'undefined') {
        if (typeof arguments.callee.activeXString != 'string') {
            var versions = ['MSXML2.XMLHttp.6.0', 'MSXML2.XMLHttp.3.0', 'MSXML2.XMLHttp'];
            var i, len;
            for (i = 0, len = versions.length; i < len; i++) {
                try {
                    new ActiveXObject(versions[i]);
                    arguments.callee.activeXString = versions[i];
                } catch (e) {
                    // skip
                }
            }
        }
        return new ActiveXObject(arguments.callee.activeXString);
    } else {
        throw new Error('No XHR object available.');
    }
}

我们可以发现,在浏览器每次调用createXHR()的时候,它都要对浏览器所支持的能力仔细检查,但是很明显当第一次检查之后,我们就应该知道浏览器是否支持我们所需要的能力,因此除第一次之外的检查都是多余的。即使只有一个if语句也肯定要比没有if语句慢,所以if语句不必每次都执行,那么代码可以运行的更快一些,惰性载入就是用来解决这种问题的技巧。

函数重写

要理解惰性载入函数的原理,我们有必要先理解一下函数重写技术,由于一个函数可以返回另一个函数,因此可以在函数内部用新的函数来覆盖旧的函数。

function sayHi() {
    console.info('Hi');
    sayHi = function() {
        console.info('Hello');
    }
}

我们第一次调用sayHi()函数时,控制台会打印出Hi,全局变量sayHi被重新定义,被赋予了新的函数,从第二次开始之后的调用都会打印出Hello。惰性载入函数的本质就是函数重写,惰性载入的意思就是函数执行的分支只会发生一次。

惰性载入

我们来看一个例子(例子来源于冴羽所写的JavaScript专题之惰性函数)。现在需要写一个foo函数,这个函数返回首次调用时的Date对象,注意是首次。

方案一
var t;
function foo() {
    if (t) return t;
    t = new Date()
    return t;
}
// 此方案存在两个问题,一是污染了全局变量
// 二是每次调用都需要进行一次判断
方案二
var foo = (function() {
    var t;
    return function() {
        if (t) return t;
        t = new Date();
        return t;
    }
})();
// 使用闭包来避免污染全局变量,
// 但是还是没有解决每次调用都需要进行一次判断的问题
方案三
function foo() {
    if (foo.t) return foo.t;
    foo.t = new Date();
    return foo.t;
}
// 函数也是一种对象,利用这个特性也可以解决
// 和方案二一样,还差一个问题没有解决
方案四
var foo = function() {
    var t = new Date();
    foo = function() {
        return t;
    };
    return foo();
};
// 利用惰性载入技巧,即重写函数

惰性载入函数有两种实现方式,第一种是在函数被调用时再处理函数。在第一次调用的过程中,该函数会被覆盖为另外一种按合适方式执行的函数,这样任何对原函数的调用都不用再经过执行分支了。

第二种实现方式是在声明函数时就指定适当的函数。这样第一次调用时就不会损失性能了,而是在代码首次加载时会损失一点性能,即是利用闭包写一个自执行的函数。

改进 createXHR

有了上面的基础,我们就可以将createXHR()改进为下列形式,这样就不用每次调用都进行判断了。

// 第一种实现方式
function createXHR() {
    if (typeof XMLHttpRequest != 'undefined') {
        createXHR = function() {
            return new XMLHttpRequest();
        }
    } else if (typeof ActiveXObject != 'undefined') {
        createXHR = function() {
            if (typeof arguments.callee.activeXString != 'string') {
                var versions = ['MSXML2.XMLHttp.6.0', 'MSXML2.XMLHttp.3.0', 'MSXML2.XMLHttp'];
                var i, len;
                for (i = 0, len = versions.length; i < len; i++) {
                    try {
                        new ActiveXObject(versions[i]);
                        arguments.callee.activeXString = versions[i];
                    } catch (e) {
                        // skip
                    }
                }
            }
            return new ActiveXObject(arguments.callee.activeXString);
        };
    } else {
        createXHR = function() {
            throw new Error('No XHR object available.');
        }
    }
}

// 第二种实现方式
function createXHR() {
    if (typeof XMLHttpRequest != 'undefined') {
        return function() {
            return new XMLHttpRequest();
        }
    } else if (typeof ActiveXObject != 'undefined') {
        return function() {
            if (typeof arguments.callee.activeXString != 'string') {
                var versions = ['MSXML2.XMLHttp.6.0', 'MSXML2.XMLHttp.3.0', 'MSXML2.XMLHttp'];
                var i, len;
                for (i = 0, len = versions.length; i < len; i++) {
                    try {
                        new ActiveXObject(versions[i]);
                        arguments.callee.activeXString = versions[i];
                    } catch (e) {
                        // skip
                    }
                }
            }
            return new ActiveXObject(arguments.callee.activeXString);
        };
    } else {
        return function() {
            throw new Error('No XHR object available.');
        }
    }
}

个人微信:Guanngxu
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Guanngxu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值