最近遇到个有趣的问题:“JS中的值是按值传递,还是按引用传递呢?”

在分析这个问题之前,我们需了解什么是按值传递(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 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。

术语的不同版本

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

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

  1. JS采取的都是”按值传递”的求值策略, 其中对象类型较为特殊,实际为按值传递了引用(即传递引用的副本,而不是按引用传递引用)。从这个角度,说对象也是按值传递也是有道理的。(虽然笔者不是十分赞同).

  2. 引入“按共享传递”的求值策略,它让我们精确的区分按共享传递与经典的按值传递、按引用传递的不同。在这种情形下,可以按传参类型区分:“基本类型按值传递、而对象按共享传递。”(笔者更倾向的描述方式)

结论

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

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

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

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

参考文献
  • Wikipedia.org-Evaluation strategy
  • http://dmitrysoshnikov.com/-Evaluation Strategy

作者:Bosn
微博:@Bosn
主页:bosn.me
简介:工程师、音乐人

原文地址:http://www.cnblogs.com/bosnma/p/4256108.html

JS是按值传递还是按引用传递?相关推荐

  1. c#_按值传递与按引用传递的区别

    一.引言 C#中参数的传递方式可以分为两类,按值传递和按引用传递.如果再根据参数的类型进行细分,大致可以分为如下四种: 值类型的按值传递 引用类型的按值传递 值类型的按引用传递 引用类型的按引用传递 ...

  2. 关于按值传递和按引用传递(送给这个方面的新手——包括我自己)

    我记得刚上大学我们学院就给我们开了第一门专业课--高级语言程序设计.在函数一节里面,首先要学到形式参数和实际参数,然后就学了按值传递和按引用传递,当时学得那叫一个云里雾里啊,再加上后面还学了个指针,弄 ...

  3. Java:按值传递还是按引用传递详细解说

     Java:按值传递还是按引用传递详细解说 2012-11-14 23:02 44567人阅读 评论(17) 收藏 举报 本文章已收录于: 分类: java(48) 作者同类文章X 变量(6) 作 ...

  4. 编程细节 —— 按值传递、按引用传递(final、const)

    System.out,out 是 System 类内定义的静态 final PrinterStream 变量: public final class System {...public final s ...

  5. JS中的函数参数传递到底是按值传递还是按引用传递

    首先我们知道JS中的数据类型大致可以分为简单数据类型和复杂数据类型: 当我们声明一个变量并给它赋值时,可以赋给其简单值和复杂值(以下堆内存和栈内存的地址表示均随意取的,只是为了区分,不代表真实的内存地 ...

  6. 按值传递和按引用传递的区别_c++按值、地址、引用传递参数

    在现实生活中,"地址"指的是我们居住在某条街上的某个小区的某栋某楼某室: 而在计算机中,"地址"指的是一个.一些数据在内存中储存的位置.比如我们之前讲到的指针, ...

  7. 按值传递和按引用传递

    实参给形参传递数据的两种方式: 1.按值传递:实参传递给形参的是值,实参和形参在内存上是两个独立的变量,修改形参不会对实参产生影响.原理如下图: 2.按引用传递:实参传递给形参的是引用地址,实参和形参 ...

  8. Java 必知必会——什么是对象引用?按值传递 OR 按引用传递?

    1. 什么是对象? 任何事物都可以作为一个对象,一台电脑.一部手机等等等等. 在面向对象程序设计的 Java 中,程序是由对象组成的,对象包含了描述其状态的数据(属性)和可以执行的操作(方法),因此, ...

  9. 一道JS面试题所引发的血案,透过现象寻本质,再从本质看现象

    觉得本人写的不算很烂的话,可以登录关注一下我的GitHub博客,新手写东西写的不好之处,还望见谅,毕竟水平有限,写东西只为交流提高,一起学习,还望大神多加指点,指出纰漏,和提出宝贵的意见,博客会坚持写 ...

最新文章

  1. SKU表管理之查询获取sku表列表数据
  2. Unity 2D游戏开发教程之使用脚本实现游戏逻辑
  3. Tomexam在线考试系统 2.1
  4. QT 使用全局变量的方法
  5. java 列表框简单案例
  6. 【NOIP2013模拟】粉刷匠 题解代码
  7. thinkphp5.0解决控制器驼峰命名时提示找不到类名
  8. python计算召回率代码_序列标注的准确率和召回率计算
  9. 新版ubuntu16.04安装旧版商店与flash
  10. 【环境安装】Ubuntu20.04 安装yasm-1.3.0
  11. 1980-2018年中国及世界各国实际利率数据
  12. 斗魔之残龙纹 第一章
  13. java-net-php-python-jsp学生社团信息演示录像2019计算机毕业设计程序
  14. java时间差的百分之二十,java计算时间差及某个时间段数据
  15. 局域网中的每台计算机主机扩展槽,计算机导论选择题
  16. 等保三级核心-主机安全
  17. 澳洲技术移民评分标准
  18. 揭秘国漫《武动乾坤》制作幕后 动作戏竟然真人演
  19. 如何装python软件_Visual Studio 中的 Python 教程步骤 5,安装包 | Microsoft Docs
  20. 用户与组的创建、删除

热门文章

  1. Hive:用Java代码通过JDBC连接Hiveserver
  2. 简单的Net::FTP模块的简单应用
  3. package.json 入门
  4. 百度定位---适配8.0限制后台定位
  5. GNU make manual 翻译(三十八)
  6. php之include的使用
  7. 《基于ArcGIS的Python编程秘笈(第2版)》——第1章 面向ArcGIS的Python语言基础
  8. struts2 action中result参数详解
  9. Microsoft Office Communications Server 2007 R2 RTM 简体中文企业版部署速成篇之一
  10. css 设置overflow:scroll 滚动条的样式