重点:尽量使用字面量和局部变量,减少数组和对象的使用。
函数的属性
可访问属性
内部仅供js引擎存取的内部属性
作用域链
内部属性[[scope]]
包含了一个函数被创建的作用域中的集合。它决定哪些属性能被函数访问。
函数作用域中每个对象被称为一个可变对象。每个对象都是键值对存在。
当创建一个函数后,它的作用域链会被创建此函数的作用域中的可访问的数据对象填充。
执行环境对象
执行函数时会创建一个称为执行环境的内部对象。一个执行环境定义了一个函数执行时的环境。
多次创建同一个函数会导致创建多个执行环境。当函数执行完毕,执行环境会被销毁。
活动对象
当执行环境被创建时,它的作用域链初始化为当前运行函数的[[scope]]
属性中的对象。
这些值按照他们在函数中出现的顺序被复制到执行环境的作用域链中。
这个过程完成后,一个被称为活动对象的新对象就创建好了。
活动对象是一个变量对象,它包含了所有局部变量,命令参数,参数集合,this。
执行中的变量
标识符解析过程,决定从哪里获取或存储数据。
从作用域链头部一层层查找同名标识符,这个过程影响性能。
同名变量,第一个会覆盖第二个。
全局变量在作用域的最末端,最远最慢。局部变量是最快的。优化过给js引擎没有此性能损失。
如果某个跨作用域的值在函数中被引用过一次以上,那么就把它存储到局部变量中。
其实就是多次使用某个变量,可以将它赋值给函数内部的一个临时变量,这样速度更快。
常见
// 函数内
let arr = data.slice()
...
with改变作用域链
高效写法,低性能
执行with语句时,执行环境的作用域链临时被改变,新增了一个变量对象,它包含所有参数指定对象的所有属性。
该对象被推到作用域链首页,而函数的所有局部变量处于第二个作用域对象中,导致访问层更深了。
try-catch
catch语句也有同样效果。异常对象err
会被推到作用域链首。
catch执行完毕,会恢复作用域链。
正确的使用
catch(err) {
handleError(err)
}
没有局部变量的访问,作用域链的改变不会影响性能。
动态作用域
with
,try-catch
,eval
。
无法静态分析(通过查看代码结构)检测。
优化引擎会被退化到老的查找方式,降低了效率。所以不推荐使用动态作用域。
闭包
闭包的[[scope]]
属性包含了与执行环境作用域链相同的对象的引用,所以会产生副作用。
通常函数的活动对象会随执行环境一起销毁。但闭包由于引用的存在,导致对象无法销毁。
闭包需要更多的内存开销。
在频繁访问跨作用域链的标识符时,每次访问都会带来性能的损失。
对象
方法
命名的成员引用了一个函数
属性
引用了非函数类型的成员
访问对象成员比字面量和变量还慢
原型
定义
定义并实现了一个新创建的对象所必须包含的成员列表,对象实例共享了原型对象的成员。
对象通过__proto__
内部属性绑定原型
类型
对象有两种成员类型:实例成员(own)和原型成员
实例成员存于对象实例中
原型成员从原型对象继承而来
判断
hasOwnPrototype()
:是否包含实例成员
x in obj
:包括实例和原型
原型链
(略)
嵌套成员
每个点都会执行搜索整个对象成员,越深读取越慢
苹果浏览器使用.
属性操作符会比[]
更快
缓存对象成员值
函数中将对象成员赋给临时值来减少读取。
...
let list = data.data.list
将常用的对象成员,数组元素,跨域变量保存在局部变量中来改善js性能。