ECMAScript 中所有函数的参数是按值传递的,你知道吗?

610 阅读2分钟

ECMAScript 中所有函数的参数是按值传递的!,即函数外的值会被复制到函数内部的参数中,就像从一个变量复制到另一个变量一样。

因为变量有按值访问和按引用访问,但是参数只有按值传递

参数传递方式

  • 按值传递参数时,值会被复制到一个局部变量(一个命名参数,arguments对象中的一个槽位)
  • 按引用传递参数时,值在内存中的位置会被保存在一个局部变量,这意味着对本地变量的修改会反应到函数外部。

例子

  • 传递数值形式
//按值传递的eg
function add(num) {
    num += 10
    return num
} 

let count = 20
let res = add(count)
console.log(count) //20
console.log(res)  //30

count值是20被复制到参数num上,在add内部使用。在函数内部参数的值被+10,但是并没有影响函数外部的值。参数num和变量count互不干扰!!

  • 传递引用形式--eg2
function setName(obj) {
    obj.name = "nick"
}

let person = new Object()
setName(person)
console.log(person.name, person) //nick {name: 'nick'}

创建了对象保存在person中,对象被传递给setName方法,并复制到obj中。在函数内部obj和person都指向同一对象。即使参数时按值传进参数,obj也会通过引用访问对象。当函数内部给obj设置了name属性时,函数外部对象也会反映这个变化,因为obj指向的对象保存在全局作用域的堆内存上。(很多人错误的认为,当局部作用域中修改对象而变化反映到全局时,意味着参数是按引用传递的。请看eg3)

  • 传递引用形式--eg3
function setName(obj) {
    obj.name = "nick"
    obj = new Object()
    obj.name = 'llll'
}

let person = new Object()
setName(person)
console.log(person.name, person) //nick {name: 'nick'}

这个例子比eg2多了两行代码,将obj重新定义为有着不同name的新对象。当person传入setName()时,其name属性被设置为'nick'。然后变量obj被设置为新对象,name被设置为 'llll'。如果person是按引用传递的,那么person的应该自动将指针改为指向name的'llll'对象。可是再次访问person.name时,它的值就只有'nick'。这表明函数中参数值改变后,原始的引用仍没有改变。当obj被重写时,它变成了一个指向本地对象的指针。而那个本地对象在函数执行结束时就被销毁了