接上一回继续学习JavaScript中的作用域和作用域链。
从作用域链的结构可以看出来,在execution context的作用域链中,标识符所在的位置越深,读写速度就会越慢。如图上一回最后一图所示,
全局变量总是存在于execution context作用域链的最末端,因此在标识符解析时候,查找全局变量是最慢的。所以,在编写代码的时候应该尽量少使用全局变量,尽可能使用局部变量。一个比较好的做法就是:如果一个跨作用域的对象被引用了一次以上,则先把它存储到局部变量里再使用,例如:
这个函数引用了两次全局变量document,查找该变量必须遍历整个作用域链,直到最后在全局对象中才能找到,这段代码可以重写为:
当然了,此段代码比较简单,重写后不会显示出巨大的性能提升,但是如果程序中有大量的全局变量被反复访问,那么重写后的代码性能会显著改善。
函数在每次执行的时候对应的execution context都是唯一的,所以多次调用同一个函数会导致创建多个execution context,当函数执行完毕之后,execution context会被销毁。每一个execution context都和一个作用域链关联,一般情况下,在execution context运行的过程中,其作用域链只会被with和catch语句影响。
这里使用with语句来避免多次书写document,看上去更高效,实际上产生了性能问题。当代码运行到with语句时,execution context的作用域链临时被改变了,一个新的可变对象被创建,它包含了参数指定的对象的所有属性,这个对象将被推入到作用域链的头部,这意味着函数的所有局部变量处于了第二个作用域链对象中了,因此访问代价更高了,如图:
因此在代码中应避免使用with语句,而是将document存储在一个局部变量中就可以提升性能。另外一个会改变作用域链的是try-catch语句中的catch语句,当try代码块中发生错误时,执行过程会跳转到catch语句,然后把异常对象推入一个可变变量并置于作用域的头部。在catch代码块内部,函数的所有局部变量将会被放在第二个作用域链对象中,例如:
一旦catch语句执行完毕,作用域链就会返回到之前的状态。Try-catch语句在代码调试和异常处理中非常有用,因此不可以完全避免。但是可以通过优化代码来减少catch语句对性能的影响,例如:
优化后的代码,handleError方法是catch子句中唯一执行的代码,该函数接收异常对象作为参数,这样可以灵活和统一处理错误,由于只执行一条语句,且没有局部变量的访问,作用域链的临时改变就不会影响代码性能了。
更多信息可以微信扫描二维码,一起学习前端知识。