在分析这个问题之前,我们需了解什么是按值传递(call by value),什么是按引用传递(call by reference)。在计算机科学里,这个部分叫求值策略(Evaluation Strategy)。它决定变量之间、函数调用时实参和形参之间值是如何传递的。

按值传递 VS. 按引用传递

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

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

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

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

我们先看一个C的例子来了解按值和引用传递的区别:

void Modify(int p, int * q)
{
p = 27; // 按值传递 - p是实参a的副本, 只有p被修改
*q = 27; // q是b的引用,q和b都被修改
}
int main()
{
int a = 1;
int b = 1;
Modify(a, &b); // a 按值传递, b 按引用传递,
// a 未变化, b 改变了
return(0);
}

  

这里我们可以看到:

  • a => p按值传递时,修改形参p的值并不影响实参a,p只是a的副本。
  • b => q是按引用传递,修改形参q的值时也影响到了实参b的值。

探究JS值的传递方式

JS的基本类型,是按值传递的。

var a = 1;
function foo(x) {
x = 2;
}
foo(a);
console.log(a); // 仍为1, 未受x = 2赋值所影响

  

再来看对象:

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

  

说明o和obj是同一个对象,o不是obj的副本。所以不是按值传递。 但这样是否说明JS的对象是按引用传递的呢?我们再看下面的例子:

var obj = {x : 1};
function foo(o) {
o = 100;
}
foo(obj);
console.log(obj.x); // 仍然是1, obj并未被修改为100.

  

如果是按引用传递,修改形参o的,应该影响到实参才对。但这里修改o的值并未影响obj。 因此JS中的对象并不是按引用传递。那么究竟对象的值在JS中如何传递的呢?

按共享传递 call by sharing

准确的说,JS中的基本类型按值传递,对象类型按共享传递的(call by sharing,也叫按对象传递、按对象共享传递)。最早由Barbara Liskov. 在1974年的GLU语言中提出。该求值策略被用于Python、Java、Ruby、JS等多种语言。

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

var obj = {x : 1};
function foo(o) {
o = 100;
}
foo(obj);
console.log(obj.x); // 仍然是1, obj并未被修改为100.

  

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

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 obj = {x : 1};
function foo(o) {
o.x = 3;
}
foo(obj);
console.log(obj.x); // 3, 被修改了!

  

而对象就不一样了,对象是可变的。

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。

术语的不同版本

需要注意的是,求值策略中的“引用”和求值策略本身都是抽象概念,这里的引用和语言具体的引用(例如C++的&a, C#的ref参数)可以不同,请不要混淆。

由于JS在传递对象类型的值时,是按值传递引用的副本,参考Dmitry的博文(链接)目前,对JS的求值策略有两种解释:

  • JS采取的都是”按值传递”的求值策略, 其中对象类型较为特殊,实际为按值传递了引用(即传递引用的副本,而不是按引用传递引用)。从这个角度,说对象也是按值传递也是有道理的。(虽然笔者不是十分赞同).
  • 引入“按共享传递”的求值策略,它让我们精确的区分按共享传递与经典的按值传递、按引用传递的不同。在这种情形下,可以按传参类型区分:“基本类型按值传递、而对象按共享传递。”(笔者更倾向的描述方式)

结论

虽然关于JS的求值策略有诸多争议和不同版本,博主比较倾向的结论是:

“JS中基本类型是按值传递的,对象类型是按共享传递的。”

语言抽象概念并非博主创造或臆造,文中所涉理论理论均有参考,详见下面之参考文献。

另感谢博客园园友@京山游侠 @greatim的精彩讨论和补充。

参考文献

javascript之值传递与引用传递的更多相关文章

  1. JavaScript 函数参数传递到底是值传递还是引用传递

    tips:这篇文章是听了四脚猫的js课程后查的,深入的理解可以参看两篇博客: JavaScript数据类型--值类型和引用类型 JavaScript数据操作--原始值和引用值的操作本质 在传统的观念里 ...

  2. JavaScript传递变量:值传递?引用传递?

    今天在看 seajs-2.2.1/src/util-events.js源码,里面有段代码不是很理解: var events = data.events = {} // Bind event seajs ...

  3. Java中引用类型变量,对象,值类型,值传递,引用传递 区别与定义

    一.Java中什么叫做引用类型变量?引用:就是按内存地址查询       比如:String s = new String();这个其实是在栈内存里分配一块内存空间为s,在堆内存里new了一个Stri ...

  4. java中值传递和引用传递

    最近工作中使用到了值传递和引用传递,但是有点懵,现在看了下面的文章后清晰多了.一下是文章(网摘) 1:按值传递是什么 指的是在方法调用时,传递的参数是按值的拷贝传递.示例如下: public clas ...

  5. Java中的值传递和引用传递

    这几天一直再纠结这个问题,今天看了这篇文章有点思路了,这跟C++里函数参数为引用.指针还是有很大区别. 当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里 ...

  6. java的值传递和引用传递

    昨天博主在对于值传递和引用传递这里栽了一个大坑啊,导致一下午时间都浪费在这里,我们先说下值传递和引用传递java官方解释: 值传递:(形式参数类型是基本数据类型):方法调用时,实际参数把它的值传递给对 ...

  7. PHP值传递和引用传递的区别

    PHP值传递和引用传递的区别.什么时候传值什么时候传引用 (1)按值传递:函数范围内对值的任何改变在函数外部都会被忽略 (2)按引用传递:函数范围内对值的任何改变在函数外部也能反映出这些修改 (3)优 ...

  8. 【转载】C++ 值传递、指针传递、引用传递详解

    原文链接:http://www.cnblogs.com/yanlingyin/ 值传递: 形参是实参的拷贝,改变形参的值并不会影响外部实参的值.从被调用函数的角度来说,值传递是单向的(实参->形 ...

  9. java中方法的参数传递机制(值传递还是引用传递)

    看到一个java面试题: 问:当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?  答:是值传递.Java 编程语言只有值传递参 ...

  10. java 对象传递 是 值传递 还是 引用传递?

    这个问题说实话我感觉没有太大的意义. 按第一印象和c++的一些思想去理解的话对象传递是引用传递,因为传递过去的对象的值能被改变. 但是又有很多人,不知道从哪里扣出来一句,java中只有值传递,没有引用 ...

随机推荐

  1. 纯HTML页面为了避免频繁前后台Ajax交互方案

    需求: 看这么一个简单的界面. 它有很多下拉框,下拉框中的可选项并不是固定不变的. 由于页面是静态HTML页面,不能使用后台JSP动态生成. 之前的解决方案是,页面打开后使用多个Ajax请求,获取下拉 ...

  2. Apache2 添加登陆用户名和密码

    1. 修改httpd.conf, 对要做认证的目录进行设置<Directory "/usr/local/var/www">   Options Indexes Foll ...

  3. mysql中游标的使用

    BEGIN #遍历占领野地表还原军队 # and cityid=cityid) ,) ;#occupyWild表的TroopsId ,) ;#occupyWild表的CityId ,) ;#occup ...

  4. $.ajax获取不到数据问题解决

    $("#updateflow").click(function () { $.ajaxSetup({ contentType: "application/json;cha ...

  5. php中soap的使用实例以及生成WSDL文件,提供自动生成WSDL文件的类库——SoapDiscovery.class.php类

    1. web service普及: Webservice soap wsdl区别之个人见解 Web Service实现业务诉求:  Web Service是真正“办事”的那个,提供一种办事接口的统称. ...

  6. 在Windows 7上安装MongoDB 2.6.7

    sc.exe create MongoDB binPath= "C:\mongodb\bin\mongod.exe --service --config=\"C:\mongodb\ ...

  7. data-icon=""图片还可以是自定义的?

    自定义的图片怎么写啊? data-icon = "地址" 不对啊????? 不是写地址,而是设置css 比如 data-icon="mm" 那么在css中肯定有 ...

  8. java GUI之事件处理

    常见事件及相应事件源类型 事件源 用户操作 事件 JButton 点击按钮 ActionEvent JTextField 在文本域按回车键 ActionEvent JCheckBox 点击复选框 Ac ...

  9. nginx 反向代理(Reverse Proxy)与耗时记录

    反向代理服务器位于实际的服务器之前,他能够缓存服务器响应,加速访问,同时也启到了负载均衡服务器的效果.反向代理服务器解析客户端请求,根据负载均衡算法转发到不同的后台服务器上.用户和后台服务器之间不再有 ...

  10. Fedora 14配置vsftp服务步骤

    Fedora 14配置vsftp服务步骤:1:检查Fedora14是否安装了vsftp服务    用rpm -qa|grep vsftp命令检查是否安装了vsftp服务,如果安装了,会显示安装好的版本 ...