清楚解释javascript参数传递参数传递值指的是在调用函数时向函数传递配置或运行参数的行为,包括通过call和apply传递值。在实际开发中,我们总结javascript参数传递分为基本数据类型传值(String、Numbe、Boolean、Null、undefind)和引用数据类型(Object,包括Array、Function、Data)。本文将纠正这种误解:引用类型本质上是按份额传递的。在探讨按值传递问题之前,我们先来看看javascript在声明变量时是如何分配内存的原始值的:简单的数据段存储在栈(stack)中,也就是它们的值直接存储在访问变量的位置。这是因为这些原始类型占用的空间是固定的,所以它们可以存放在一个更小的内存区域——栈中。这种存储方便快速查找变量的值。引用值:堆(heap)中存储的对象,即变量中存储的值是一个指针(point),指向存储对象的内存地址。这是因为:引用值的大小会发生变化,所以不能入栈,否则会降低变量查找的速度。相反,放置在变量的堆栈空间中的值是对象在堆上存储的地址。地址的大小是固定的,因此将其存储在堆栈上对变量性能没有负面影响。原始类型将原始值存储在堆栈上。引用类型是将数据存放在堆中,然后在栈中创建对堆地址的引用。在javascript中是不允许直接访问堆内存中存放的对象的,所以在访问一个对象时,首先要获取的是该对象在堆内存中的地址,然后根据这个地址获取值在对象中,这就是传说中的引用访问。原始类型的值是可以直接访问的。不同的内存分配机制也带来了不同的访问机制。基本类型和引用类型在变量复制时也有区别:原始值:当将一个保存有原始值的变量复制到另一个变量时,会将原始值的副本赋值给新变量,这两个变量将是完全独立,它们只是具有相同的值。引用值:当将一个保存对象内存地址的变量复制到另一个变量时,内存地址会被赋值给新的变量,也就是说两个变量都指向堆内存中的同一个对象,其中Any一个人所做的更改将反映在另一个人身上。复制不会产生新的堆内存消耗。所以我们得出结论,javascript中所有的函数参数都是按值传递的,形参都是复制到实参,只是基本数据类型复制的是原值,而引用类型复制的是堆内存的地址。引用类型的评估策略是共享调用。理论知识整理完毕。我举几个例子具体情况分析一下。基本数据类型leta=1functionfoo(x){console.log(x)}foo(a)console.log(a)//2//1将变量a的值直接复制到foo函数的实参x中,此时变量x是变量a的副本时间。它们在各自的上下文栈中独立存储值'1',互不影响。我们对a和x的读写操作对它们各自的值进行操作。在示例图中,值1分别保存在全局上下文和foo的上下文中。引用类型传值leta={abc:1}functionfoo(x){x.abc=2console.log(x.abc)}foo(a)console.log(a.abc)//2//2根据编码经验,我们得出上面的栗子是引用传递的结论是非常错误的。将对象a的引用传递给函数foo内部,函数内部变量x指向全局变量a,从而实现引用的传递,所以变量x和变量a读写同一个对象。我们用一张图来揭示:但实际上是分享传递。引用传递是我们对javascript求值策略的误解。如果是按引用传递,下面的例子会造成混淆:leta={abc:1}functionfoo(x){console.log(x)//{abc:1}x=2console.log(x)//2}foo(a)console.log(a.abc)//1foo函数执行时,第一个打印输出'{abc:1}',第二个A打印'2',console.log(a.abc)调用foo函数后的语句打印“1”。通过引用传递的单词。foo函数执行后执行表达式console.log(a.abc)。这时候a应该已经赋值2了,a.abc不存在应该打印出'undefind'。但是它打印出1,说明在foo函数内部执行x=2并没有修改外层对象a的值。为什么a和x指向同一个对象后,对x的赋值并没有改变原对象的值呢?因为这里是通过共享(共享调用)的方式将对象传递给实参的,根据引用类型变量复制的特点(上面介绍过):执行foo函数时,形参x的值就是内存地址Reference中传入的对象a的地址,即x在变量对象创建阶段保存的是一个对象的堆内存地址。此时a和x都指向同一个对象。然后在函数的执行阶段,第二行代码将原来的数据类型2赋值给了x,使得x不再保存原来的堆内存地址,而是保存了一个原始值。再次访问x时,访问的是上次赋给x的原始值。因此,对x的赋值会改变标识符x保存在上下文栈中的具体值。这时候如果使用引用传递,变量a指向的对象也应该被赋值2。但实际上,作为基本数据类型给x赋值并不能使原来的对象变成字面量value,表示引用类型不是引用传递,不遵循引用规则来处理值的写入。需要区分给对象属性赋值和直接给对象赋值的区别:leta={abc:1}functionfoo(x){x.abc=99console.log(x)//{abc:99}x=2console.log(x)//2}foo(a)console.log(a)//{abc:99}在foo函数中修改对象x的属性会导致x和a指向的对象被修改,因为它们指向相同的堆地址。参考:《javascript高级程序设计》javascript传参JS是传值还是传引用?
