按值传递与按引用传递

原创
2015/08/01 12:33
阅读数 310

讨论这个问题,我们首先应该明确一点:

什么是值传递?什么是引用传递?

值传递:传递过程中,将变量的值拷贝一个副本,用这个副本值来对新变量完成初始化;

                传递结果:两个变量的值相同,但互相独立。

引用传递:传递过程中,只传递原变量的指针副本,新变量接受这个指针副本;

                传递结果:两个变量指向同一个对象

由以上可知:值传递与引用传递最大的区别在于:传递过程中是否发生了对象的拷贝

在一些java和javascript的书中,对于参数传递的过程都强调:只有值传递。这种理解或许存在一定的误区,或者说这种认识是比较模糊的,不能算错但让人难以理解,因为这两种语言中非基本类型对象很明显传递的是一个引用,可为什么非要说是按值传递?

经过分析可以想象,这种说法的内涵是:对于非基本类型的数据,变量在栈中存储的内容本身就是一个指针地址,而发生传递时,传递的并不是引用本身,而是引用的副本(拷贝)这是在描述栈上的变化,即:无论传递的是一个基本类型的值还是其他类型的引用副本,这当中本身就发生了拷贝操作】。

var person = new Object();
person.name = 'sunf';

function change(obj){
    obj.name = 'fanS';
    obj.age = 18;
}

change(person);
console.log(person.name);

在上面这个例子的过程:

1:创建一个对象person。

2:给person添加一个name属性,并赋值sunf。

3:调用函数change(),将person的引用副本传递给函数change()的局部变量,此时obj指向与外部的person相同的对象。

4:操作局部变量obj,相当于直接操作位于堆中的对象。于是输出时候person.name被改变。

var person = new Object();
person.name = 'sunf';

function change(obj){
    obj = new Object();
    obj.name = 'fanS';
}

alert(person.name);

在上面这个调用例子用:change中的obj变量在函数开始运行的时,将原先的引用替换成了一个对新对象的引用,此时obj与外部的person没有任何关系,所以在函数中改变obj的属性,person不会有任何变化。

class Person{
    String name;
    String age;
    
    public static void main(String[] args){
        Person person = new Person();
        person.name = "sun";
        person.age = 18;
        
        change(person);
    }
    
    public static void change(Person methodPerson){
        //由于外部的person与方法内部methodPerson指向同一个对象
        //所以修改methodPerson相当于修改person
        methodPerson.name = "fan";
        //methodPerson指向新的对象,与原来的关系断开。
        methodPerson = new Person();
        //外部person不会变化
        methodPerson.name = 'hhhh'; 
    }
}

在非基本类型参数传递时,java与javascrpt采用的手段完全相同,都是通过引用副本操作堆中的对象

由以上例子可得出结论:发生参数传递时引用的副本可以被改变,但是引用本身不会被改变(其实根本拿不到引用本身,它只存在于最初的那个变量)。(换句话说就是:方法体内对引用副本的改变与外部变量的引用本身无关)

从这些例子看来,两种语言都是值传递这个话,虽然不能算错,但表达的并不完整,所以我更倾向于“基本类型按值传递,非基本类型按引用传递”这样的理解。因为这样更容易理解参数在传递过程中发生的变化。

另外在java语言中,String有一个很有意思的现象。首先String是引用类型,我们从以上结论知道,引用类型是按引用传递。

class Person{
    
    public static void main(String[] args){
        String name = "sunf";
        change(name);
        System.out.println("name--->" + name);
        
    }
    
    public static void change(String str){
        str = "hello " + str;
        System.out.println("str--->"+ str);
    }
}

//打印结果:
//str---> hello sunf
//name---> sunf

从以上结果的运行看来,String类型仿佛是按值传递的,但是不要忘了,String类是final的他不可变化,任何对String类型对象做的修改,都会创建一个新的对象。所以在函数change中第一行,str实际上是将其指针指向了一个新的对象,所以str与外部的name无关。

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
6 收藏
0
分享
返回顶部
顶部