用测试工具JSLitmus来告诉你几个提升JavaScript性能的小技巧

性能测试工具

JSLitmus

dom操作

测试代码

<div id="domTestWrap">
    <div class="dom-test-content">
        <span class="dom-test-text"></span>
    </div>
    <div class="dom-test-content">
        <span class="dom-test-text"></span>
    </div>
    <div class="dom-test-content">
        <span class="dom-test-text"></span>
    </div>
</div>
function domTestDepthOne () {
    document.getElementById('domTestWrap')
}
function domTestDepthOneQS () {
    document.querySelector('#domTestWrap')
}
function domTestDepthTwo () {
    document.getElementById('domTestWrap').getElementsByClassName('dom-test-content')
}
function domTestDepthTwoQSA () {
    document.querySelectorAll('#domTestWrap .dom-test-content')
}
function domTestDepthThree () {
    var domTestTextList = document.getElementById('domTestWrap').getElementsByClassName('dom-test-content')
    for (var i=0; i< domTestTextList.length; i++) {
        domTestTextList[i].innerHTML
    }
}
function domTestDepthThreeQSA () {
    var domTestTextList = document.querySelectorAll('#domTestWrap .dom-test-content')
    for (var i=0; i< domTestTextList.length; i++) {
        domTestTextList[i].innerHTML
    }
}
JSLitmus.test('dom test depth one', domTestDepthOne)
JSLitmus.test('dom test depth one query selecter', domTestDepthOneQS)
JSLitmus.test('dom test depth two', domTestDepthTwo)
JSLitmus.test('dom test depth two query selecter all', domTestDepthTwoQSA)
JSLitmus.test('dom test depth three query', domTestDepthThree)
JSLitmus.test('dom test depth three query selecter all', domTestDepthThreeQSA)

测试结果

dom操作-测试结果

PS:黄条代表每秒可执行测试函数的次数,当然越多越快。

分析总结

dom操作非常耗时,querySelector&querySelectorAll书写方便但相比

document.getElementById更加耗时些。务必减少dom操作,减少无意义的dom路径的查找。

对象操作

测试代码

var objectTest = {
    one : {
        name: 'one',
        value: 1,
        two: {
            name: 'two',
            value: 2,
            three: {
                name: 'three',
                value: 3
            }
        }
    }
}

var three = objectTest.one.two.three;
function objectTestDepthZero () {
    three.name
    three.value
}

function objectTestDepthThree () {
    objectTest.one.two.three.name
    objectTest.one.two.three.value
}


JSLitmus.test('object test depth zero', function (count) {
    while (count -- ) objectTestDepthZero()
})
JSLitmus.test('object test depth three', function (count) {
    while (count -- ) objectTestDepthThree()
})

测试结果

对象操作-测试结果

分析总结

对象属性的访问非常耗时,原因于JavaScript引擎内部存储变量的结构有关,JavaScript是弱类型语言,它不像强类型语言如C、C++那样可以通过内存地址偏移来快速访问。

可以参考,用最简单易懂的道理告诉你,为什么JavaScript在现代引擎(V8,JavaScriptCore)下,能表现出卓越性能!

务必减少对象嵌套的深度,减少无意义的对象路径的查找

作用域冒泡

测试代码

测试代码来自JSLitmus官网主页的例子

// 很多开发者不知道变量作用域对于脚本的性能有多少影响。
// 为了直观地展示这一点,我们写了下面的JSLitmus测试用例去测量对定义在不同作用域下的变量
// 执行"递增(++)"操作的表现

// 首先,测试定义在全局作用域的变量
var global_var = 1;
JSLitmus.test('global', function(count) {
  while (count--) global_var++;}
);

// 现在测试一个定义在局部函数作用域的变量
JSLitmus.test('local', function(count) {
  var local_var = 1;
  while (count--) local_var++;
});

// 尝试测试一个绑定到立即执行函数内的变量。
// 喜欢Prototype和JQuery的开发者应该觉得这样特别有意思
JSLitmus.test('closure',
  (function() {
    var closure_var = 1;
    return function(count) {while (count--) closure_var++;}
  })()
);

// 还是绑定在立即执行函数中的变量,不过这次套了两层
JSLitmus.test('multi-closure',
  (function() {
    var multi_var = 1;
    return (function() {
      return function(count) {while (count--) multi_var++;}
    })()
  })()
);

// 测试一个空的函数调用,这可以作为一个参照点
JSLitmus.test('empty function call', function(count) {
  var f = function() {};
  while (count--) f();
});

测试结果

作用域冒泡-测试结果

分析总结

每次访问变量或者函数都会进行一次作用域冒泡的过程,所以本地作用域如函数作用域是最快的,全局作用域最慢。
应该要尽量减少这个冒泡的层数,对于要经常访问的全局变量,应该在本地作用域做一个缓存。

其他测试

测试代码

这些测试代码来自,JSLitmus官网的例子

// 这是一个空的非循环测试。它的结果应该是一秒可以做无限次,或者非常接近无限的一个值
JSLitmus.test('empty test (non-looping)', function() {});

// 这是一个空的循环测试,对于这种执行非常快的代码,需要执行更多次才能得到比较准确的结果
// 所以经常使用循环测试
// 它的结果应该是一秒可以做无限次,或者非常接近无限的一个值
JSLitmus.test('empty test', function(count) {
  while (count--);
});

// 测试调用一个定义在全局作用域的函数的消耗
var emptyFunction = function() {};
JSLitmus.test('function overhead (non-looping)', function() {
  emptyFunction();
});

// 循环测试调用一个定义在本地作用域函数的消耗。
// 这个应该比前者更快,一个原因是使用循环测试更准确,另一个原因是函数定义在本地作用域了
JSLitmus.test('function overhead', function(count) {
  var f = emptyFunction;
  while (count--) f();
});

// 测试Array.join()方法,然后我们可以看看它和字符串"+"操作的对比
JSLitmus.test('"hello" + "world" (non-looping)', function() {
  var a = "hello", b = "world", x;
  x = a+b;
});

// 循环测试Array.join()方法,然后我们可以看看它和字符串"+"操作的对比
JSLitmus.test('"hello" + "world"', function(count) {
  var a = "hello", b = "world", x;
  while(count--) x = a+b;
});

// 循环测试Array.join()方法,然后我们可以看看它和字符串"+"操作的对比
JSLitmus.test('string join()', function(count) {
  var a = ['hello', 'world'];
  while (count--) a.join();
});

// Math.random()是快还是慢呢?
JSLitmus.test('Math.random()', function(count) {
  while (count--) Math.random();
});

// 正则表达测试有多快呢?让我们来一探究竟
JSLitmus.test('RegExp.test()', function(count) {
  while (count--) /rl/.test('hello world');
});

// 呵呵,如果在循环的外面定义正则表达式对象会有帮助吗?
JSLitmus.test('cached RegExp.test()', function(count) {
  var re = /rl/;
  while (count--) re.test('hello world');
});

// 新建Date对象的速度怎样?是快还是慢?
JSLitmus.test('new Date()', function(count) {
  while (count--) new Date();
});

// 如果我们把Dete对象设置到元素的innerHTML中的速度如何?
// 因为这个操作一定很慢,所以就不需要循环测试了
JSLitmus.test('set Element.innerHTML', function() {
  document.getElementById('test_element').innerHTML = new Date();
});

// 测试一个数组的创建,这个可以作为一个参照物
JSLitmus.test('new Array()', function(count) {
  while (count--) {var a = [];}
});

测试结果

其他测试-测试结果

分析总结

图中,Infinity的测试代表非常快,其中,空函数的调用,字符串的"+"的拼接都非常快。
Math.random()与新建一个数组也比较快。
正则表达式测试,数组的join,新建日期,稍微慢了些。
最后,设置innerHTML的dom操作最慢。

更多

web性能优化--高性能javascript

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值