当前位置: 首页 > 后端技术 > PHP

JavaScript中按值传递和按引用传递

时间:2023-03-30 03:04:19 PHP

为保证可读性,本文采用意译而非直译。另外,本文版权归原作者所有,转载仅供学习。JavaScript有5种基本数据类型,即:Boolean、null、undefined、String和Number。这些基本类型在赋值时是按值传递的。值得注意的是,还有另外三种类型:Array、Function和Object,它们是通过引用传递的。从底层技术来看,这三者都是对象。原始数据类型如果原始数据类型绑定到一个变量,我们可以认为该变量包含该原始数据类型的值。变量x=10;变量y='abc';varz=null;当我们用=将这些变量赋值给另一个变量时,实际上是复制对应的值赋值给新的变量。我们称之为按值传递。变量x=10;变量y='abc';变量a=x;变量b=y;console.log(x,y,a,b)//10,'abc',10,'abc'a和x都包含10,b和y都包含'abc',它们是完全独立的副本,不互相干扰。如果我们改变a的值,x不会受到影响。varx=10;vary='abc';vara=x;varb=y;a=5;b='def';安慰。日志(x,y,a,b);//10,'abc',5,'def'object如果一个变量绑定了一个非基本数据类型(Array,Function,Object),那么它只记录一个内存地址,里面存放的是具体的数据。注意前面提到的指向基本数据类型的变量相当于包含数据,而现在指向非基本数据类型的变量本身不包含数据。对象是在内存中创建的,当我们声明arr=[]时,我们在内存中创建了一个数组。arr记录了内存的地址。变量arr=[];//(a)arr.push(1);//(b)执行(a)后,在内存中创建一个空数组对象,其内存地址为#001,arr指向该地址。执行(b)后,数组对象中多了一个元素,但数组地址不变,arr也一样。按引用传递对象按引用传递,而不是按值传递。即变量赋值只传递地址。变种参考=[1];varrefCopy=参考;reference和refCopy指向同一个数组。如果我们更新引用,refCopy也会受到影响。参考.push(2);console.log(参考,refCopy);//[1,2],[1,2]引用重新赋值如果我们重新赋值一个已经赋值的变量,它会包含新的数据或者引用一个地址。varobj={first:'fundebug.com'};obj={second:'fundebug.cn'};obj从指向第一个对象变为指向第二个对象。如果一个对象没有被任何变量指向,比如第一个对象(地址#001),JavaScript引擎的垃圾回收机制将销毁该对象并释放内存。==和===对于引用类型的变量,==和===只会判断引用的地址是否相同,而不会判断对象的具体属性和值是否相同。因此,如果两个变量都指向同一个对象,则返回true。vararrRef=['嗨!'];vararrRef2=arrRef;console.log(arrRef===arrRef2);//true如果是不同的对象,及时包含相同的属性和值,也会返回false。vararr1=[“嗨!”];vararr2=[“嗨!”];控制台日志(arr1===arr2);//false如果要判断两个不同的对象是否真的相同,一个简单的方法就是将它们转换成字符串再判断。vararr1str=JSON.stringify(arr1);vararr2str=JSON.stringify(arr2);console.log(arr1str===arr2str);//true另一种方式是递归判断每个属性的值,直到基本类型位置,再判断是否相同。函数参数当我们将基本类型数据传入函数时,函数会将这些数据复制并赋值给函数的参数变量。varhundred=100;vartwo=2;functionmultiply(x,y){returnx*y;}vartwoHundred=multiply(hundred,two);百的值被复制到变量x,二的值被复制到变量y。纯函数是给定输入,返回唯一输出的函数。除此之外,不会对外部环境产生附带影响。我们称这个函数为纯函数。函数内定义的所有变量在函数返回后都会被垃圾回收。但是,如果函数的输入是一个对象(Array、Function、Object),那么传入的就是一个引用。对该变量的操作将影响原始对象。这样的编程技巧会产生副作用,代码的逻辑会变得复杂,可读性会降低。因此,Array.map和Array.filter等许多数组函数都是作为纯函数实现的。虽然他们的参数是一个数组变量,但是通过深拷贝赋值给一个新的变量,然后对新的数组进行操作,来避免改变原来的数组。让我们看一个例子:functionchangeAgeImpure(person){person.age=25;returnperson;}varalex={name:'Alex',age:30};varchangedAlex=changeAgeImpure(alex);console.log(alex);//{name:'Alex',age:25}console.log(changedAlex);//{name:'Alex',age:25}在非纯函数changeAgeImpure中,对象person的年龄被更新并返回。原来的alex对象也受到影响,年龄更新为25。让我们看看如何实现纯函数:functionchangeAgePure(person){varnewPersonObj=JSON.parse(JSON.stringify(person));newPersonObj.age=25;返回newPersonObj;}varalex={name:'Alex',age:30};varalexChanged=changeAgePure(alex);console.log(alex);//{name:'Alex',age:30}console.log(alexChanged);//{name:'Alex',age:25}我们通过JSON.sringify把对象变成字符串,再通过JSON.parse把字符串转回对象。此操作生成一个新对象。一道简单的面试题面试中经常会问到按值传递和按引用传递,我们试着回答一下下面的代码是如何输出的:functionchangeAgeAndReference(person){person.age=25;person={name:'John',age:50};返回人;}varpersonObj1={name:'Alex',age:30};varpersonObj2=changeAgeAndReference(personObj1);console.log(personObj1);//->?console.log(personObj2);//->?