js参数传递方式

js究竟是按值传递还是按引用传递

按值传递(call by value)

是最常用的求值策略:函数的形参是被调用时所传实参的副本。修改形参的值并不会影响实参。

按引用传递(call by reference)

函数的形参接收实参的隐式引用,而不再是副本。这意味着函数形参的值如果被修改,实参也会被修改。同时两者指向相同的值。

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

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

在红宝书中有这样一段

在书里举的这个例子里,它传递的的确是引用,但实际上他应用的策略却是 call by sharing,通俗的说就是,它并不是把引用直接传递进去,而是把引用的拷贝传递进去存储在函数内部的AO对象里。因此,对这个引用进行第二次赋值的时候,实际上把这份引用指向了另外一个对象,所以之后对这个对象的操作不会影响到外部的对象

js中所有的参数传递都是传递值,不是传递引用,基本类型很好理解,那么对象类型呢.

var obj = {x : 1};
function foo(o) {
    o = 100;
}
foo(obj);
console.log(obj.x); //1

可以看到,obj的值并没有改变,所以不是按照引用传递, 那么这种传递方式究竟是什么呢?

###按共享传递 call by sharing

如果大多数开发人员所讨论的前两种策略都是众所周知的,那么下面的策略(更确切地说是它的讨论术语)并未被广泛使用。然而,正如我们很快就会看到的那样,它在ECMAScript中传递参数策略方面起着关键作用。

这种策略的替代名称是“按对象传递”或“通过对象共享传递”。

“共享”策略是在1974年由Barbara Liskov首先提出的,用于CLU编程语言。

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

然而,虽然引用是副本,引用的对象是相同的。它们共享相同的对象,所以修改形参对象的属性值,也会影响到实参的属性值。

var obj = {x : 1};
function foo(o) {
    o.x = 3;
}
foo(obj);
console.log(obj.x); // 3, 被修改了!

对于对象类型,由于对象是可变(mutable)的,修改对象本身会影响到共享这个对象的引用和引用副本。而对于基本类型,由于它们都是不可变的(immutable),按共享传递与按值传递(call by value)没有任何区别,所以说JS基本类型既符合按值传递,也符合按共享传递。

var a = 1; // 1是number类型,不可变 var b = a; b = 6;

据按共享传递的求值策略,a和b是两个不同的引用(b是a的引用副本),但引用相同的值。由于这里的基本类型数字1不可变,所以这里说按值传递、按共享传递没有任何区别。

###基本类型的不可变(immutable)性质

基本类型是不可变的(immutable),只有对象是可变的(mutable). 例如数字值100, 布尔值true, false,修改这些值(例如把1变成3, 把true变成100)并没有什么意义。比较容易误解的,是JS中的string。有时我们会尝试“改变”字符串的内容,但在JS中,任何看似对string值的”修改”操作,实际都创建新的string值。

var str = "abc";
str[0]; // "a"
str[0] = "d";

str; // 仍然是”abc”;赋值是无效的。没有任何办法修改字符串的内容 而对象就不一样了,对象是可变的。

var obj = {x : 1};
obj.x = 100;
var o = obj;
o.x = 1;
obj.x; // 1, 被修改
o = true;
obj.x; // 1, 不会因o = true改变

这里定义变量obj,值是object,然后设置obj.x属性的值为100。而后定义另一个变量o,值仍然是这个object对象,此时obj和o两个变量的值指向同一个对象(共享同一个对象的引用)。所以修改对象的内容,对obj和o都有影响。但对象并非按引用传递,通过o = true修改了o的值,不会影响obj。