数据类型 | 深刻理解所有函数的参数都是按值传递的
这部分有一句很重要但是比较难理解的话:
ECMAScript 中所有函数的参数都是按值传递的
一看很懵,红宝书紧接着就做出了解释:
这意味着函数外的值会被复制到函数内部的参数中,就像从一个变量复制到另一个变量一样
如果是原始值,那么就跟原始值变量的复制一样,如果是引用值,那么就跟引用值变量的复制一样
大概理解了,但是还是云里雾里,那么我们首先要清楚,按值传递和按引用传递参数会造成什么样的结果?
按值传递参数:值会被复制到一个局部变量(可以理解为
arguments
的一个槽位【补充:agruments
是一个类数组对象,包含调用函数时传入的所有参数】)按引用传递参数:在按引用传递参数时,值在内存中的位置会被保存在一个局部变量,这意味着对本地变量的修改会反映到函数外部(在ECMAScript中显得不科学)
- 看一下这个例子:
1 | function addTen(num) { |
原始值的例子很容易理解,num
是一个局部变量,在调用时,count
作为参数传入,被复制到num
一边在addTen
内部使用
在函数内部,参数num
的值被加上了10,但这不会影响函数外部的原始变量count
,参数num
和变量count
互不干扰,如果num
是按引用传递的,那么count
的值也会被修改为20
- 再看一下一个引用数据类型的例子:(比较不容易理解)
1 | function setName(obj) { |
这次创建了一个对象并保存在person
中,然后这个对象被传给setName
方法,并且复制给obj
在函数内部,obj
和person
是指向同一个对象的,这样就会导致,即使对象是按值传进函数的,obj
也会通过引用访问对象,这样的话,当函数内部修改了obj
的name
属性的值的时候,函数外部的对象也会反映这个变化,因为,obj
指向的对象保存在全局作用域的堆内存上
上面的过程会给人一种错觉:你在函数局部作用域中修改对象而导致反映到全局,不就意味着参数是按引用传递的吗?(一开始我也这样以为的!!!)
那么请看下面这个例子:
1 | function setName(obj) { |
这个例子比上面的例子就多了一行,也就是在函数内部将obj重新定义为了一个有着不同name的新对象
分析整个过程:
创建了一个对象并保存在
person
中,然后这个对象被传给setName
方法,并且复制给obj
在函数内部内部修改了
obj
的name
属性的值,并且又作妖地把obj
设置为name
为'Jack'
的一个新对象但是,当我们访问
person.name
的时候,打印的结果是Jenny
而不是Jack
这表明函数中参数的值改变之后,原始的引用仍然没变,当
obj
在函数内部被重写时,它变成了一个指向本地对象的指针,而那个本地对象在函数执行结束时就被销毁了