为了性能优化, 应该用临时变量保存对象的属性以避免多次访问, 那是否也应该用一个变量保存外层作用域的变量呢?

will
  • 6

很多教材都说了, 访问属性速度很慢, 所以如果访问一个属性超过一次, 就该用一个临时变量保存该属性, 以避免多次进行属性查找。但是少有教材提到, 访问外层变量的速度如何。
是因为访问外层作用域的速度衰减可以忽略不计, 以至于即便是访问全局作用域的变量, 也比访问一个当前作用域对象的属性还要快, 所以教材才没有对此进行说明吗?
我个人认为应该不是这样的, 访问外层作用域的变量, 其复杂度也是随着作用域的增加而增加的, 所以其复杂度应该也是O(n), 跟访问对象的属性复杂度应该是相同的才对, 既然提到了要用变量保存对象的属性, 那理应也提一提用变量保存外部变量吧。
除非我的推测是错误的。请教大神讲解一下访问外层变量的详细机制。

回复
阅读 813
3 个回答
西索酱
  • 253

专业的回答我没有,但是我开发中会这样处理:


let a = pts.c.d.e.qqq
let b = pts.c.d.e.fff

当我有这样的访问时,我就会改成下面的

let pts_e = pts.c.d.e

let a = pts_e.qqq
let b = pts_e.fff

我并不知道这样能提高效率,我甚至担心会增加内存使用。但这样确实让代码更容易读,更容易改bug
这个处理思路在小学叫提取公因数

_usw
  • 3.7k

无责任瞎猜,具体实现不详。
我们抽象每个函数都有一个隐藏的属性,由于“持有”自身执行所需的上下文。那么假设有如下场景:

function a() {
    let aa = 'aa'
    return function b() {
        let bb = 'bb'
        return function c(cc) {
            console.log(aa + bb + cc)
        }
    }
}

从的角度来看,aa是a的aa,bb是a的b的bb,看起来有点儿嵌套的意思了。c有一个隐藏的属性,就当是这样的:

context = {
    aa: a.aa,
    bb: a.b.bb,
    cc: cc
}

不严谨也不准确哈

那么当c执行的时候,有没有可能函数体内的aa和bb其实就是a.aa和a.b.bb的替身,也就是一个访问器的临时变量。

这只是一种满足“避免多次访问”的自洽的说法。并不是真实的情况。

这个问题,其实就是等价于问,访问多级的活动记录表,和访问多级的运行时标准库Object谁更快,虽然二者的一般实现都是散列表,但是活动记录表一般会拥有更多的信息且都是在静态编译时就能确定的,也就是说不管一个外层变量跨了几个层级,它属于哪个层级的活动记录表完全是确定的,如果你想写一个高性能的vm在已有的信息下完全可以把外层变量的访问时间缩短到一个散列表的访问(如果追求极致的性能甚至用一个数组都行,不过就要求把符号离散化为数字标识),而运行时的Object实现是为了业务实现的,提供给普通用户使用的,他甚至还实现了原型这种充满动态和未知的东西,这就意味着他能优化的手段有限可能是无法达到活动记录表的优化程度和速度的

宣传栏