JavaScript的值传递和引用传递

  • 原文: 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/…

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/253050.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

全景摄像技术大有可为

网络摄像机发展至今,已经基本满足了“高清”、“日夜监控”、“远距离监控”的需求,但是 随着细分市场的发展,超广角摄像机需求逐渐凸显出来。主要应用在会议室、办公室、大厅/大堂、商场、仓库、车间等大面积开阔的区域,解决原来…

C#编程(五十三)----------字典DictionaryTKey,TValue

字典 关键字:Dicitionary 说明: 必须包含命名空间System.Collection.Generic Dictionary里面的每一个元素都是一个键值对(由两个元组组成:键和值). 键必须是唯一的,而值不需要唯一的. 键和值都可以是任意类型(例如:string,int,自定义类型,等等) 通过一个键读取一个值的事件是接…

setInterval只执行一次的原因

1 setInterval(arrow(),2000) 改为: 1 setInterval(arrow,2000) 原因: arrow()这是一个函数调用,函数调用就会有返回值, 而arrow()没有返回值,所以这里的arrow()是一个undefined,自然你想要的循环执行arrow…

java文件传输之文件编码和File类的使用

---恢复内容开始--- 我们知道,在用户端和服务端之间存在一个数据传输的问题,例如下载个电影、上传个照片、发一条讯息。在这里我们 就说一下文件的传输。 1.文件编码 相信大家小时候玩过积木(没玩过也看过吧),看到一个…

Android 模拟输入那点事

因工作原因,需要用到模拟输入这个东东,查阅了一些资料,实现方式有多种,我大概分为两类,命令行类和程序类。 命令行类包括自动化测试组件monkeyrunner,getevent/setevent命令,input命令 程序类包…

arm-linux-gcc:Command not found的问题

标签: ubuntulinux 2015-05-15 10:47 680人阅读 评论(0) 收藏 举报 分类: Ubuntu(23) /etc/profile gcc(9) ARM汇编指令(4) 折腾了一天,终于搞定了。 ubuntu没有roo…

[No0000111]java9环境变量配置bat

保存成bat(utf-8 无签名 编码) 右键以管理员权限运行 修改JAVAINSTALLPATH 为JAVA SDK 安装目录(默认用C:\PROGRAM FILES\JAVA\)即可; 只在 用户变量下 创建,会事先保存好用户原有的“JAVA_HOME,JRE_HOME,P…

去掉浮夸,空杯心态重新面对测试

刚开始一头扎进软件测试行业,从踏踏实实的机械化功能测试,到学会和甲方扯皮,到被鄙视的五体投地后抓紧修炼表面功夫来忽悠人,学的最多的反而是怎么与人交流。第一次面对跳槽的机会,我竟然发现自己的测试能力不升反降。…

PASTE Splay

题目描述 我们用文本处理器来处理一个特殊的文本文件,该文本文件共有N行文本,每一行文本仅包含一个自然数,第一行为1、第二行为2,以此类推至N行为自然数N。   假设对该文本文件执行一次“剪切和粘贴”操作含义如下:…

linux 用户空间通过makefile向程序传递参数

一. 用户空间 因为实际上进行预处理的只是Gcc工具&#xff0c;而make工具只是一个解决依赖关系的工具。所以问题就简化成如何通过make向gcc传递参数。通过简单的例子来说明&#xff1a;hello.c#include <stdio.h> void main(void) {#ifdef DEBUG printf("y…

Spring---基于Spring IOC的小程序

实现的功能以及各文件间的关系 IHelloMessage&#xff1a;一个接口&#xff0c;用于定义输出问候信息。 HelloWorld、HelloChina&#xff1a;接口的实现类。在这里表示人在不同的地方 Person&#xff1a;一个人物类&#xff0c;调用IHelloMessage接口&#xff0c;向用户输出问候…

Web开发者不可不知的16条原则

HTML已经走过了近20的发展历程。从HTML4到XHTML&#xff0c;再到最近十分火热的HTML5&#xff0c;它几乎见证了整个互联网的发展。但是&#xff0c;即便到现在&#xff0c;有很多基础的概念和原则依然需要开发者高度注意。下面&#xff0c;小编向大家介绍这些应该遵循的开发原则…

MIPI DSI协议介绍

原文地址&#xff1a;http://blog.csdn .NET/qq160816/article/details/19555957 一、MIPI MIPI&#xff08;移动行业处理器接口&#xff09;是Mobile Industry Processor Interface的缩写。MIPI&#xff08;移动行业处理器接口&#xff09;是MIPI联盟发起的为移动应用处理器制…

NSArray、NSDictionary、NSString存储、删改、遍历

NSString 创建一个NSString实例&#xff1a;NSString *str “this is string”;//字面量语法 常用API&#xff1a; stringWithFormat //创建动态字符串 -&#xff08;NSUInteger&#xff09;length //获取字符的数量 -isEqualToString: //判断两个字符串是否相等 -uppercaseSt…

2018.11.14成立我的博客

2018.11.14成立我的博客转载于:https://www.cnblogs.com/zengxx/p/9957509.html

130242014018-郑志良-第2次实验

一、实验目的 1&#xff0e;熟悉体系结构的风格的概念 2&#xff0e;理解和应用管道过滤器型的风格。 3、理解解释器的原理 4、理解编译器模型 二、实验环境 硬件&#xff1a; 软件&#xff1a;Python或任何一种自己喜欢的语言 三、实验内容 1、实现“四则运算”的简易翻译器。…

Hi3516A开发--挂载SD卡和U盘

一、SD卡 1、通过fdisk -l命令确认板子上的Linux系统是否识别SD卡 / # fdisk -l Disk /dev/mmcblk0: 63.8 GB, 63864569856 bytes 255 heads, 63 sectors/track, 7764 cylinders Units cylinders of 16065 * 512 8225280 bytes Device Boot Start …

【BZOJ 4170】 4170: 极光 (CDQ分治)

4170: 极光 Time Limit: 30 Sec Memory Limit: 512 MBSubmit: 121 Solved: 64Description "若是万一琪露诺&#xff08;俗称rhl&#xff09;进行攻击&#xff0c;什么都好&#xff0c;冷静地回答她的问题来吸引她。对方表现出兴趣的话&#xff0c;那就慢慢地反问。在她考…

自动生成web服务器日志解析规则

2019独角兽企业重金招聘Python工程师标准>>> 当前web服务器的多样化使得访问日志的数据清洗变得越来越复杂&#xff0c;企业需要投入专业的数据清洗人员编写数据清洗规则&#xff08;解析规则或者解析正则&#xff09;&#xff0c;或者需要关心web服务器访问日志的生…

mybatis一级缓存二级缓存

一级缓存 Mybatis对缓存提供支持&#xff0c;但是在没有配置的默认情况下&#xff0c;它只开启一级缓存&#xff0c;一级缓存只是相对于同一个SqlSession而言。所以在参数和SQL完全一样的情况下&#xff0c;我们使用同一个SqlSession对象调用一个Mapper方法&#xff0c;往往只执…