• 原文: Explaining Value vs. Reference in Javascript
  • 译者: Fundebug

为了保证可读性,本文采用意译而非直译。另外,本文版权归原作者所有,翻译仅用于学习。

JavaScript有5种基本的数据类型,分别是:布尔、null、undefined、String和Number。这些基本类型在赋值的时候是通过值传递的方式。值得注意的是还有另外三种类型: Array、Function和Object,它们通过引用来传递。从底层技术上看,它们三都是对象。

基本数据类型

如果一个基本的数据类型绑定到某个变量,我们可以认为该变量包含这个基本数据类型的值。

var x = 10;
var y = 'abc';
var z = null;
复制代码

当我们使用=将这些变量赋值到另外的变量,实际上是将对应的值拷贝了一份,然后赋值给新的变量。我们把它称作值传递

var x = 10;
var y = 'abc';var a = x;
var b = y;console.log(x, y, a, b) // 10, 'abc', 10, 'abc'
复制代码

ax都包含10,by都包含'abc',并且它们是完全独立的拷贝,互不干涉。如果我们将a的值改变,x不会受到影响。

var x = 10;
var y = 'abc';
var a = x;
var b = y;
a = 5;
b = 'def';
console.log(x, y, a, b); // 10, 'abc', 5, 'def'
复制代码

对象

如果一个变量绑定到一个非基本数据类型(Array, Function, Object),那么它只记录了一个内存地址,该地址存放了具体的数据。注意之前提到指向基本数据类型的变量相当于包含了数据,而现在指向非基本数据类型的变量本身是不包含数据的。

对象在内存中被创建,当我们声明arr = [],我们在内存中创建了一个数组。arr记录的是该内存的地址。

var arr = []; // (a)
arr.push(1); // (b)
复制代码

当执行完(a)之后,内存中创建了一个空的数组对象,其内存地址为#001arr指向该地址。

变量 地址 对象
arr #001 []

当执行完(b)之后,数组对象中多了一个元素,但是数组的地址依然没有变,arr也没有变。

变量 地址 对象
arr #001 [1]

引用传递

对象是通过引用传递,而不是值传递。也就是说,变量赋值只会将地址传递过去。

var reference = [1];
var refCopy = reference;
复制代码
变量 地址 对象
reference #001 [1]
refCopy #001

referencerefCopy指向同一个数组。 如果我们更新referencerefCopy也会受到影响。

reference.push(2);
console.log(reference, refCopy); // [1, 2], [1, 2]
复制代码
变量 地址 对象
reference #001 [1, 2]
refCopy #001

引用重新赋值

如果我们将一个已经赋值的变量重新赋值,那么它将包含新的数据或则引用地址。

var obj = { first: 'fundebug.com'};
obj = { second: 'fundebug.cn'};
复制代码

obj从指向第一个对象变为指向第二个对象。

变量 地址 对象
obj #001 {first: 'fundebug.com'}
#002 {second: 'fundebug.cn'}

如果一个对象没有被任何变量指向,就如第一个对象(地址为#001),JavaScript引擎的垃圾回收机制会将该对象销毁并释放内存。

== 和 ===

对于引用类型的变量,=====只会判断引用的地址是否相同,而不会判断对象具体里属性以及值是否相同。因此,如果两个变量指向相同的对象,则返回true

var arrRef = ['Hi!'];
var arrRef2 = arrRef;console.log(arrRef === arrRef2); // true
复制代码

如果是不同的对象,及时包含相同的属性和值,也会返回false

var arr1 = ["Hi!"];
var arr2 = ["Hi!"];console.log(arr1 === arr2); // false
复制代码

如果想判断两个不同的对象是否真的相同,一个简单的方法就是将它们转换为字符串然后判断。

var arr1str = JSON.stringify(arr1);
var arr2str = JSON.stringify(arr2);console.log(arr1str === arr2str); // true
复制代码

另一个方法就是递归地判断每一个属性的值,直到基本类型位置,然后判断是否相同。

函数参数

当我们将基本类型数据传入函数,函数会将这些数据拷贝赋值给函数的参数变量。

var hundred = 100;
var two = 2;
function multiply(x, y) {return x * y;
}
var twoHundred = multiply(hundred, two);
复制代码

hundred的值拷贝给变量xtwo的值拷贝给变量y

纯函数

对于一个函数,给定一个输入,返回一个唯一的输出。除此之外,不会对外部环境产生任何附带影响。我们机会称该函数为纯函数。所有函数内部定义的变量在函数返回之后都被垃圾回收掉。

但是,如果函数的输入是对象(Array, Function, Object),那么传入的是一个引用。对该变量的操作将会影响到原本的对象。这样的编程手法将产生附带影响,是的代码的逻辑复杂和可读性变低。

因此,很多数组函数,比如Array.mapArray.filter是以纯函数的形式实现。虽然它们的参数是一个数组变量,但是通过深度拷贝并赋值给一个新的变量,然后在新的数组上操作,来防止原始数组被更改。

我们来看一个例子:

function changeAgeImpure(person) {person.age = 25;return person;
}
var alex = {name: 'Alex',age: 30
};
var changedAlex = changeAgeImpure(alex);
console.log(alex); // { name: 'Alex', age: 25 }
console.log(changedAlex); // { name: 'Alex', age: 25 }
复制代码

在非纯函数changeAgeImpure中,将对象personage更新并返回。原始的alex对象也被影响,age更新为25。

让我们来看如何实现一个纯函数:

function changeAgePure(person) {var newPersonObj = JSON.parse(JSON.stringify(person));newPersonObj.age = 25;return newPersonObj;
}
var alex = {name: 'Alex',age: 30
};
var alexChanged = changeAgePure(alex);
console.log(alex); // { name: 'Alex', age: 30 }
console.log(alexChanged); // { name: 'Alex', age: 25 }
复制代码

我们通过JSON.sringify将对象变为一个字符串,然后再通过JSON.parse将字符串变回对象。通过该操作会生成一个新的对象。

一道简单的面试题

值传递和引用传递经常在面试中被问到,来尝试回答一下如下代码如何输出:

function changeAgeAndReference(person) {person.age = 25;person = {name: 'John',age: 50};return person;
}
var personObj1 = {name: 'Alex',age: 30
};
var personObj2 = changeAgeAndReference(personObj1);
console.log(personObj1); // -> ?
console.log(personObj2); // -> ?
复制代码

关于Fundebug

Fundebug专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了7亿+错误事件,得到了Google、360、金山软件、百姓网等众多知名用户的认可。欢迎免费试用!

版权声明

转载时请注明作者Fundebug以及本文地址:
blog.fundebug.com/2017/08/09/…

JavaScript的值传递和引用传递相关推荐

  1. JavaScript强化教程——对象的值传递和引用传递

    2019独角兽企业重金招聘Python工程师标准>>> 本文为 H5EDU 机构官方 HTML5培训 教程,主要介绍:JavaScript强化教程--对象的值传递和引用传递 func ...

  2. javascript之值传递与引用传递

    javascript之值传递与引用传递 在分析这个问题之前,我们需了解什么是按值传递(call by value),什么是按引用传递(call by reference).在计算机科学里,这个部分叫求 ...

  3. Javascript 之《函数传参到底是值传递还是引用传递》

    前言 这个问题其实困惑了我好久,但是在实际使用中总是得过且过,不想去深究.由于这种态度,在学习 Javascript 过程中,水平一直都是出于半桶水状态,很多概念和原理似懂非懂,模糊不清. 所以,写了 ...

  4. 三分钟让你掌握JavaScript中值传递和引用传递的区别

    值传递:传递的是实际参数的一个副本.基本数据类型Undefined,Null,Boolean,Number.String都是值传递. 引用传递:传递的是实际参数的地址.引用数据类型Object,Arr ...

  5. javaScript中值传递和引用传递

    案例1 var str1 = "江西赣州于都县"; var number1 = 526.97; var b = true; var roles = ["建宁公主" ...

  6. 基础回顾(正则、循环、和、 int和Integer、String /StringBuffer、Array和ArrayList、值传递和引用传递、 Lamda、java8)

    1. 正则表达式 1.1 Java中是如何支持正则表达式操作的? Java中的String类提供了支持正则表达式操作的方法,包括:matches().replaceAll().replaceFirst ...

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

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

  8. Java值传递与引用传递

    Java面试题: 当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递? 答案基本上是:值传递 说明:得出这种结论的前提必须是&qu ...

  9. python参数传递方法_深入理解python中函数传递参数是值传递还是引用传递

    python 的 深入理解python中函数传递参数是值传递还是引用传递 目前网络上大部分博客的结论都是这样的: Python不允许程序员选择采用传值还是传 引用.Python参数传递采用的肯定是&q ...

最新文章

  1. “AI”战疫在行动,一文盘点百度大脑增援疫情防控的AI操作
  2. Jquery通过Ajax方式来提交Form表单
  3. 第四周实践项目2 算法库——单链表
  4. Keepalived+Nginx 实现双机热备
  5. java远程桌面连接不上_Java远程桌面调用失败
  6. 常用的织梦(dedecms)调用标签
  7. seajs的使用--主要了解模块化
  8. 基于PHP的个人博客网站系统
  9. 三、用python实现平稳时间序列的建模
  10. Java面试题大全(整理版)1000+面试题附答案详解,最全面详细,看完稳了
  11. 使用ConfuserEx加密混淆程序以及如何脱壳反编译
  12. 读研究生的目的之我见
  13. Linux RHEL/Ubuntu安装教程
  14. iPhone播放声音文件的例子
  15. 为什么发动机需要吸入大量的空气?
  16. 字符图片分割研究小结(搬运、整理、分类、汇总)(自用,持续更新中)
  17. 理解SVM ——入门SVM和代码实现
  18. 各种OJ网站,刷题必备
  19. 微信开发者工具报错 系统错误,错误码-1,undefined 问题解决
  20. table中英文值显示其对应中文

热门文章

  1. 5错误怎么办_“选择不对,一生白费”:如果选错了,该怎么办?记住这两句话...
  2. 基于matlab的信号合成与分解,基于matlab的信号合成与分解
  3. Winform-图片上传
  4. Java8-如何构建一个Stream
  5. 发现一个小坑的地方,unity的协程,想要停止,必须以字符串启动
  6. iOS8 【xcode6中添加pch全局引用文件】
  7. (周日赛)Sort the Array
  8. 锅巴H264播放器地址和说明
  9. 收藏一些自己认为好的网站或博客
  10. 循环链表解决约瑟夫环问题