既然js函数的参数是按值传递的,那么传入很长的字符串,会否产生性能问题?

新手上路,请多包涵

我一直对JavaScript函数的“参数按值传递”的特征有些顾虑。尤其在参数为字符串,且字符串“很长”时,“按值传递参数”会不会导致性能问题呢?请大佬们帮助答疑解惑,谢谢!

例如,下面的函数会不会有性能问题?

let str = '一个很长的字符串';   //1. 内存中拥有了第一份str字符串

function checkString(str) {
    for(const ch of str) {    //5. 此时的str是内存中的第三份str字符串
        ...
    }
    return str.length < 10000 ? '' : str; 
}

function transString(str) {   //3. 此str是内存中的第二份str字符串
    if(checkString(str)) {    //4. 再次【复制】str字符串,并传入checkString函数中
         ....                 
    }
}
transString(str);             //2. 【复制】第1行的str字符串,并传入transString函数内部

我的分析是:执行transString函数需要复制两次str字符串,且复制后,在checkString函数执行时,内存中总共出现了三份str字符串。并且,由于str字符串是一个很大的字符串,所以每次复制都需要消耗很长的运行时间,并需要要多占据一份很大的内存。由此推断transString函数的性能很是低下。

这个分析纯属我瞎猜,不知道对不对。请大佬们帮助解惑,谢谢了!

阅读 3k
2 个回答

javascript里面的字符串的确是按值传递的可是别忘了他是immutable的啊,可以很轻松的基于这个特性做出很多的优化,比如string数据结构就可用用这样的数据结构描述

//该连续内存存储了一段字符,其中首地址为0xaaa
str = ['1', '2', '3']

class string {
  //字符串的头偏移量
  start = 0
  //字符串的尾偏移量
  end = 3
  //字符串首地址的指针指向的位置
  data = 0xaaa
}

这样的话字符串的赋值产生的性能问题就变得不值一提,不过多长字符串的复制都是O(1)的时间复杂度,这样设计的好处还有就是想substring,substr,slice这种mutate字符串的操作都能在O(1)的时间复杂度内完成,因为只用改下首尾偏移量然后在返回就行,相比之下在c++中如果不注意的话反而会因为字符串的mutable特性,再加上可选择引用传递或者值传递,产生更多逻辑或者性能的问题,像javascript这种高抽象的语言一般都会选择缺失一些灵活性,但是减少很多心智负担,更不容易写出有内存泄漏或者性能问题的代码

顺便附上算法4对java中字符串的描述
image.png
image.png

内存生命周期一般情况下经历一下三个步骤:

  1. 分配你所需要的内存
  2. 使用分配到的内存(读、写)
  3. 不需要时将其释放\归还

你分析的内存变大是没问题的,因为在执行代码过程中会分配内存、使用内存,但运行时间基本可以忽略不计,如果连内存都处理缓慢,估计磁盘IO、网络传输也是个问题,最后一步那就是垃圾回收,在该内存区域不在使用是,就回被垃圾回收机制自动回收,详情看这里

推荐问题