写点什么

高性能 JavaScriptの笔记(一)

用户头像
空城机
关注
发布于: 2021 年 05 月 12 日
高性能JavaScriptの笔记(一)

学习来源:《高性能JavaScript-中文版》(仅供学习使用)

数据存取

数据位置

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


  1. 字面量字面量只代表自身,不存储在特定位置。JavaScript 中的字面量有:字符串、数字、布尔值、对象、数组、函数、正则表达式,以及特殊的 null 和 undefined 值。

  2. 本地变量开发人员使用关键字 var 定义的数据存储单元。

  3. 数组元素存储在 JavaScript 数组对象内部,以数字作为索引。

  4. 对象成员存储在 JavaScript 对象内部,以字符串作为索引。


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


标识符解析

标识符解析是有代价的,事实上没有哪种计算机操作可以不产生性能开销。在执行环境的作用域链中,一个标识符所在的位置越深,它的读写速度也就越慢。因此,函数中读写局部变量总是最快的,而读写全局变量通常是最慢的(优化 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.title book.toString()


  • book.title 属于实例对象

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


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


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

book.hasOwnProperty('title')   // truebook.hasOwnProperty('toString')   //false
复制代码


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

console.log('title' in book)  // trueconsole.log('toString' in book)  // true
复制代码



原型链

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


instanceof: 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)  // trueconsole.log(b1.__proto__)console.log(books.prototype)
复制代码


b1 的__proto__与 books 的 prototype 是相同的

嵌套成员

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


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


执行location.href 总比 window.location.href要快window.location.href也比window.location.href.toString()要快

缓存对象成员值

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


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


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


例子:

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 是个全局对象。搜索该变量的过程必须遍历整个作用域链,直到最后在全局变量对象中找到。

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

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

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

  • 例子:var doc = document; var bd = doc.body; var a = doc.getElementById("a");


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


可以使用location.href来代替window.location.href

发布于: 2021 年 05 月 12 日阅读数: 42
用户头像

空城机

关注

曾经沧海难为水,只是当时已惘然 2021.03.22 加入

业余作者,在线水文 主要干前端的活,业余会学学python 欢迎各位关注,互相学习,互相进步

评论

发布
暂无评论
高性能JavaScriptの笔记(一)