ECMAScript中所有函数的参数都是按值传递的。这意味着函数外的值会被复制到函数内部的参数中,就像从一个变量复制到另一个变量一样。
如果是原始值,那么就跟原始值变量的复制
一样,如果是引用值,那么就跟引用值变量的复制
一样.对很多开发者来说,这一块可能会很不好理解,毕竟变量有按值和按引用访问,而传惨则只有按值传递。
在按值传递参数时,值会被复制到一个局部变量(即一个命名参数,或者用ECMAScript的话说,就是argument对象中的一个槽位)。在按引用传递参数时,值在内存中的位置会被保存在一个局部变量,这意味着对本地变量的修改会反映到函数外部。(这在ECMAScript中是不可能的。)
来看下面这个例子:
function addTen(num) { num += 10 return num } let count = 20 const result = addTen(count) console.log(count) // 20 没有变化 console.log(result) // 30 复制代码
这里,函数addTen()有一个参数num
,它其实是一个局部变量。在调用时,变量count
作为参数传入.count
的值是20,这个值被复制到参数num
以便在addTen()
内部使用。在函数内部,参数num
的值被加上了10,但这不会影响函数外部的原始变量count
。参数num
和变量count
互不干扰,它们只不过碰巧保存了一样的值。如果num
是按引用传递的,那么count
的值也会被修改为30.这个事实在使用数值这样的原始值时是非常明显的。但是,如果变量中传递的是对象,就没那么清楚了。
比如,再看这个例子:
function setName(obj) { obj.name = '何小生' } let person = new Object() setName(person) console.log(person.name) // '何小生' 复制代码
这一次,我们创建了一个对象,并把它保存在变量person
中。然后,这个对象被传给setName()
方法,并被复制到参数obj
中。在函数内部,obj
和person
都指向了同一个对象。结果就是,即使对象是按值传进函数的,obj
也会通过引用访问对象。当函数内部给obj
设置了name
属性时,函数外部的对象也会反映这个变化,因为obj
指向的对象保存在全局作用域的堆内存上。很多开发者错误地认为,当在局部作用域中修改而变化反映到全局时,就意味着参数是按引用传递的。为证明对象是按值传递的,我们再来看看下面这个修改后的例子。 例子:
function setName(obj) { obj.name = '何小生' obj = new Object() obj.name = '小生' } let person = new Object() setName(person) console.log(person.name) // '何小生' 复制代码
这个例子前后唯一的变化就是setName()
中多了两行代码,将obj
重新定义为一个有着不同name
的新对象。当person
传入setName()
时,其name
属性被设置为何小生
。然后变量obj
被设置为一个新对象且name
属性被设置为小生
。如果person
是按引用传递的,那么person
应该自动将指针改为指向name
为小生
的对象。可是,当我们再次访问person.name
时,它的值时何小生
。这表明函数中参数的值改变之后,原始的引用仍然没变。当obj
在函数内部被重写时,它变成了一个指向本地对象的指针。而那个本地对象在指向函数结束时就被销毁了。
注意:ECMAScript中函数的参数就是局部变量
总结小例子:
//数组 function setArray(arr) { arr[0] = 1; var arr = new Array(); //这里的地址改变了 arr[0] = 6; return arr; } var lastArr = new Array(); lastArr[0] = 0; var newArr = setArray(lastArr); console.log(lastArr[0] + '|' + newArr[0]); // 1|6 //对象 function setNameAgain(obj) { obj.name = 'aaa'; var obj = new Object(); // 如果是按引用传递的,此处传参进来obj应该被重新引用新的内存单元 obj.name = 'ccc'; return obj; } var person = new Object(); person.name = 'bbb'; var newPerson = setNameAgain(person); console.log(person.name + ' | ' + newPerson.name); //aaa | ccc 复制代码
在向参数传递基本类型的时候,被传递的值会被复制给一个局部变量(arguments对象中的一个元素)。
在向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反映在函数的外部!
重点时需要搞清楚引用类型的地址与指向地址的指针!!!