JavaScript中的值是按值传递还是按引用传递问题探讨


Posted in Javascript onJanuary 30, 2015

最近遇到个有趣的问题:“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。
Javascript 相关文章推荐
javascript demo 基本技巧
Dec 18 Javascript
基于jQuery实现的Ajax 验证用户名是否存在的实现代码
Apr 06 Javascript
jquery固定底网站底部菜单效果
Aug 13 Javascript
jQuery获得页面元素的绝对/相对位置即绝对X,Y坐标
Mar 06 Javascript
js+css实现导航效果实例
Feb 10 Javascript
jQuery延迟加载图片插件Lazy Load使用指南
Mar 25 Javascript
jQuery AJAX timeout 超时问题详解
Jun 21 Javascript
jquery 标签 隔若干行加空白或者加虚线的方法
Dec 07 Javascript
详谈commonjs模块与es6模块的区别
Oct 18 Javascript
angularjs实现分页和搜索功能
Jan 03 Javascript
JS实现获取数组中最大值或最小值功能示例
Mar 02 Javascript
从0到1学习JavaScript编写贪吃蛇游戏
Jul 28 Javascript
javascript结合fileReader 实现上传图片
Jan 30 #Javascript
使用JavaScript+canvas实现图片裁剪
Jan 30 #Javascript
js判断手机和pc端选择不同执行事件的方法
Jan 30 #Javascript
项目中常用的JS方法整理
Jan 30 #Javascript
jQuery实现带滚动线条导航效果的方法
Jan 30 #Javascript
jquery实现拖拽调整Div大小
Jan 30 #Javascript
jQuery中$.click()无效问题分析
Jan 29 #Javascript
You might like
Can't create/write to file 'C:\WINDOWS\TEMP\...MYSQL报错解决方法
2011/06/30 PHP
dedecms集成财付通支付接口
2014/12/28 PHP
Laravel第三方包报class not found的解决方法
2019/10/13 PHP
javascript-TreeView父子联动效果保持节点状态一致
2007/08/12 Javascript
jquery trim() 功能源代码
2011/02/14 Javascript
javascript开发技术大全-第1章javascript概述
2011/07/03 Javascript
javascript 基础篇2 数据类型,语句,函数
2012/03/14 Javascript
jquery 实现表单验证功能代码(简洁)
2012/07/03 Javascript
Javascript页面添加到收藏夹的简单方法
2013/08/07 Javascript
Bootstrap弹出带合法性检查的登录框实例代码【推荐】
2016/06/23 Javascript
jQuery设置单选按钮radio选中/不可用的实例代码
2016/06/24 Javascript
JS中对Cookie的操作详解
2016/08/05 Javascript
AngularJS中关于ng-class指令的几种实现方式详解
2016/09/17 Javascript
JS 60秒后重新发送验证码的实例讲解
2017/07/26 Javascript
详解如何用模块化的方式写vuejs
2017/12/16 Javascript
Vue.js 表单控件操作小结
2018/03/29 Javascript
微信小程序下拉框组件使用方法详解
2018/12/28 Javascript
基于jQuery实现可编辑的表格
2019/12/11 jQuery
微信小程序后端无法保持session的原因及解决办法问题
2020/03/20 Javascript
[00:35]DOTA2上海特级锦标赛 VP战队宣传片
2016/03/04 DOTA
Python使用Selenium+BeautifulSoup爬取淘宝搜索页
2018/02/24 Python
python 实现将文件或文件夹用相对路径打包为 tar.gz 文件的方法
2019/06/10 Python
如何使用Python实现自动化水军评论
2019/06/26 Python
Django实现微信小程序的登录验证功能并维护登录态
2019/07/04 Python
Python使用pyautocad+openpyxl处理cad文件示例
2019/07/11 Python
django的model操作汇整详解
2019/07/26 Python
使用keras框架cnn+ctc_loss识别不定长字符图片操作
2020/06/29 Python
Python logging模块异步线程写日志实现过程解析
2020/06/30 Python
pytest fixtures装饰器的使用和如何控制用例的执行顺序
2021/01/28 Python
html5 Canvas绘制线条 closePath()实例代码
2012/05/10 HTML / CSS
外贸业务员求职信范文
2013/12/12 职场文书
自我鉴定写作要点
2014/01/17 职场文书
村干部群众路线教育活动对照检查材料
2014/10/01 职场文书
经验交流材料格式
2014/12/30 职场文书
详解Python内置模块Collections
2022/03/22 Python
Java实战之课程信息管理系统的实现
2022/04/01 Java/Android