JS 数据存取

140 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第23天,点击查看活动详情

计算机科学中有一个经典的问题是通过改变数据的存储位置来获取最佳的读写性能,数据存储位置关系到代码执行过程中数据的检索速度。数据的存储位置会很大成都上影响读取速度。

JS 数据

在 JS 中整个问题相对简单因为只有几种存储方案可供选择

  • 字面量
  • 本地变量
  • 数组元素
  • 对象成员

每一种数据存储的位置都有不同的速写消耗,大多数情况下从一个字面量和一个局部变量中存取数据的性能差异是微不足道的,对象和数组在代价则更高一些。

提高存取效率

管理作用域

作用域对 JS 有许多影响,从确定哪些变量可以被函数访问到确定this的赋值。JS 的作用域同样关系到性能。

每一个 JS 函数都表示为一个对象,是 Function 对象的实例。内部属性包含了一个函数被创建的作用域中对象的集合,这个集合被称为函数的作用域链,它决定哪些数据能被访问。

函数作用域中每个对象被称为一个可变对象,每个可变对象都以键值对的形式存在。

标识符解析

在函数执行过程中,每遇到一个变量都会经历一次标识符解析过程来决定从哪里获取或存储数据。该过程搜索执行环境的作用域链,查找同名的标识符。在函数执行过程中,产生搜索过程从而影响性能。

在执行环境中一个标识符所在位置越深,它的读写速度也就越慢。因此函数中读写局部变量总是最快的。

尽可能的使用局部变量,能加快 JS 执行的效率。

改变作用域链

一般来说一个执行环境的作用域链是不会改变的。但是有两个语句可以在执行时临时改变作用域链。

  1. with

    with 语句用来给对象的所有属性创建一个变量,实际上却会产生性能问题。
    当代码执行到 with 语句时,执行环境的作用域链临时被改变了。一个新的变量对象被创建,它包含参数指定对象的所有属性。这个对象被推入作用域链的首位,意味着函数的所有局部变量现在处于第二个作用域链中。

  2. try catch

    当 try 代码块中发生错误,执行过程中会自动跳转到 catch 子句,然后把异常对象推入一个变量对象并置于作用域首位。在 catch 代码块内部,函数所有局部变量将会放在第二个作用域对象中。
    通过将错误委托给函数处理可以减小 catch 子句对性能的影响

无论是 with 还是 catch 或者是包含 eval 的函数,都被认为是动态作用域。动态作用域只存在于代码执行过程中,脚本引擎必须来回切换回较慢的基于哈希表的标识符识别方式。只有确实有必要时菜推荐使用动态作用域。