JavaScript是通过引用还是值传递

时间:2017-10-19
今天又回顾了一下js基础,发现自己很渣,后来看了一下js函数的传递,那么js到底是按值传递还是按引用传递呢?本文章向大家详细介绍JS中的值是按值传递,还是按引用传递,需要的朋友可以参考一下。

在 javascript 中数据类型可以分为两类:

  • 原始数据类型值 primitive type,比如Undefined,Null,Boolean,Number,String。
  • 引用类型值,也就是对象类型 Object type,比如Object,Array,Function,Date等。

声明变量时不同的内存分配

  • 原始值:存储在栈(stack)中的简单数据段,也就是说,它们的值直接存储在变量访问的位置。这是因为这些原始类型占据的空间是固定的,所以可将他们存储在较小的内存区域 – 栈中。这样存储便于迅速查寻变量的值。
  • 引用值:存储在堆(heap)中的对象,也就是说,存储在变量处的值是一个指针(point),指向存储对象的内存地址。这是因为:引用值的大小会改变,所以不能把它放在栈中,否则会降低变量查寻的速度。相反,放在变量的栈空间中的值是该对象存储在堆中的地址。地址的大小是固定的,所以把它存储在栈中对变量性能无任何负面影响。

在分析这个问题之前,我们需了解什么是按值传递(call by value),什么是按引用传递(call by reference)。

  • 按值传递(call by value)是最常用的求值策略:函数的形参是被调用时所传实参的副本。修改形参的值并不会影响实参。
  • 按引用传递(call by reference)时,函数的形参接收实参的隐式引用,而不再是副本。这意味着函数形参的值如果被修改,实参也会被修改。同时两者指向相同的值。

按引用传递会使函数调用的追踪更加困难,有时也会引起一些微妙的BUG。

按值传递由于每次都需要克隆副本,对一些复杂类型,性能较低。两种传值方式都有各自的问题。

1.如果是基本类型,则是按值传递

var str = 'one';
function f(string) {
    string = 'two';
}
f(str);
console.log(str); // 仍为one, 未受string = 'two'赋值所影响

2.如果是引用类型,则是按共享传递 call by sharing,并不是按引用传递,也不是按值传递。叫按对象传递、按对象共享传递.最早由Barbara Liskov. 在1974年的GLU语言中提出。该求值策略被用于Python、Java、Ruby、JS等多种语言。

var obj = {};
function f(o) {
    o.name = 'li';
}
f(obj);
console.log(obj.name); //li  被修改了



var obj = {};
function f(o) {
 //   o = 'li';

   o = [];
}
f(obj);
console.log(obj); //{}  没有修改

重点:调用函数传参时,函数接受对象实参引用的副本(既不是按值传递的对象副本,也不是按引用传递的隐式引用)。 它和按引用传递的不同在于:在共享传递中对函数形参的赋值,不会影响实参的值。如下面例子中,不可以通过修改形参o的值,来修改obj的值

再给大家看一个比较常见的,让人误解为引用传递的。

function changeObj(o){
    o.name = "changeobj";
}
var p = {};
changeObj(p);
console.log(p.name);

看到这里大家肯定会认为这是按引用传递的啊,因为在函数内部添加了对象的属性,在外边也添加了。那么接下来再看这个例子。

var o = {
    name : 'tt'
}
             
var b = new Object();
b.name = 'b';
o = b;
console.log(o.name);

上述代码先创建了一个对象,然后将其赋值非变量o,接着又用构造器创建了一个对象,并修改其属性,然后将其复制给变量对象o,然后访问o的时候name被修改为b了。这里o的引用指向了新对象b;

 再看下面这个例子大家就可以明白了

function quoteFn(obj){
    var o = new Object();
    obj = o;
    obj.name = "tony";
    obj.age = 24;
    obj.sex = "Men";
}
 
var person = {
    name : "miracle",
    age : 12
}
 
 
console.log(person.name);
console.log(person.age);
console.log(person.sex);

在这个例子中先创建了person对象,并设定其name 属性值为"miracle",age属性值为 12。接着调用quoteFn()函数,并将person对象的值(这里说的是值,这个值是一个指向person对象的指针)传递给了参数obj。

然后在quoteFn函数中穿件了一个局部对象o,并将其复制给了参数obj,然后添加了新的几个属性。最后我们打印person.name 发现属性没有改变。如果是按引用传递,那么person应该指向新的对象啊。

所以综上所述,JS参数的传递是按值传递的,引用类型传递是一个指针的副本。