Bootstrap

高性能JavaScriptの笔记(一)

学习来源:

数据存取

数据位置

计算机科学中有一个经典问题是通过改变数据的存储位置来获得最佳的读写性能,数据存储的位置关系到代码执行过程中数据的检索速度。在JavaScript 中,这个问题相对简单,因为只有几种存储方案可供选择。不过,和其他编程语言一样,数据的存储位置会很大程度上影响其读取速度。JavaScript中有下面四种基本的数据存取位置。

在不同浏览器中,访问不同存储位置的数据需要消耗的时间也是不同的。由下图可以知道,读取字面量和本地变量消耗的时间最少,性能最高

标识符解析

标识符解析是有代价的,事实上没有哪种计算机操作可以不产生性能开销。在执行环境的作用域链中,一个标识符所在的位置越深,它的读写速度也就越慢。因此,函数中读写局部变量总是最快的,而读写全局变量通常是最慢的(优化JavaScript 引擎在某些情况下能有所改善)。

全局变量总是存在于执行环境作用域链的最末端,因此它也是最远的。

JavaScript 中对象是基于原型的,原型是其他对象的基础

  • 访问字面量和局部变量的速度最快,相反,访问数组元素和对象成员相对较慢。由于局部变量存在于作用域链的起始位置,因此访问局部变量比访问跨作用域变量更快。变量在作用域链中的位置越深,访问所需时间就越长。由于全局变量总处在作用域链的最末端,因此访问速度也是最慢的。

  • 避免使用with语句,因为它会改变执行环境作用域链。同样,try-catch语句中的catch子句也有同样的影响,因此也要小心使用。

  • 嵌套的对象成员会明显影响性能,尽量少用。

  • 属性或方法在原型链中的位置越深,访问它的速度也越慢。

  • 通常来说,你可以通过把常用的对象成员、数组元素、跨域变量保存在局部变量中来改善JavaScript性能,因为局部变量访问速度更快。

集合对象

访问集合的效率比访问数组更低可以考虑将集合变为数组

function toArray() {
  var len = coll.length, a = [];
  for (var i = 0; i < len; i++) {
    a[i] = coll[i]
  }
}

原型

  • JavaScript对象是基于原型的

  • 原型是其他对象的基础

对象可以有两种成员类型:

1. 实例成员(也称为own成员) 
2.原型成员

例子:

 var book = {
    title: 'hello',
    na: 'world'
 }

book 是一个对象,在book当中存在book.titlebook.toString()

  • book.title属于实例对象

  • book.toString() 属于继承的原型对象

对象成员解析时会先从实例对象中找,如果没有找到会从继承的原型对象寻找

hasOwnProperty()方法可以判断对象是否包含特定的实例对象

book.hasOwnProperty('title')   // true
book.hasOwnProperty('toString')   //false

如果要确定对象是否包含特定的属性,可以使用in操作符,in操作符既可以搜索实例也可以搜索原型:

console.log('title' in book)  // true
console.log('toString' in book)  // true

原型链

默认情况下,所有的对象都是Object的实例

: instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上

例子:

function books(title, na){
    this.title = title;
    this.na = na;
}
books.prototype.say = function() {
    console.info(this.title, this.na)
}
var b1 = new books('111111', '22');
console.log(b1 instanceof books)  // true
console.log(b1.__proto__)
console.log(books.prototype)

b1的__proto__与books的prototype是相同的

嵌套成员

由于对象成员可能包含其他成员,每次遇到点操作符,也就是book.title这类,嵌套成员会导致JavaScript引擎搜索所有对象成员

对象成员嵌套得越深,读取速度就越慢

执行总比 要快也比要快

缓存对象成员值

由于所有类似的性能问题都与对象成员有关,因此应该尽可能避免使用它们。

在同一个函数中没有必要多此读取同一个对象成员

可以将值保存在局部变量当中减少查找次数

例子:

function hasEitherClass(element, className1, className2) {
  for(var i = 0; i < 1000; i++) {
  var k = element.className;
  }
  return element.className == className1 || element.className == className2;
} 

function hasEitherClass2(element, className1, className2) {
  var currentClassName = element.className;
  for(var i = 0; i < 1000; i++) {
  var k = currentClassName;
  }                                
  return currentClassName == className1 || currentClassName == className2;
}

使用console.time测试后,hasEitherClass2的运行速度比hasEitherClass更快

小节

平时写代码时可以优化的点①:

document是个全局对象。搜索该变量的过程必须遍历整个作用域链,直到最后在全局变量对象中找到。

可以通过以下方法减少对性能的影响:

  • 先将全局变量的引用存储在一个局部变量中

  • 然后使用这个局部变量代替全局变量。这样访问全局变量的次数就减少了,因为局部变量访问更快。

  • 例子:

平时写代码时可以优化的点②:

可以使用来代替