深入理解JavaScript的值传递和引用传递


Posted in Javascript onOctober 24, 2018

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'

a和x都包含10,b和y都包含'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)之后,内存中创建了一个空的数组对象,其内存地址为#001,arr指向该地址。

变量 地址 对象
arr #001 []

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

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

引用传递

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

var reference = [1];
var refCopy = reference;

变量 地址 对象
reference #001 [1]
refCopy #001

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

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的值拷贝给变量x,two的值拷贝给变量y。

纯函数

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

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

因此,很多数组函数,比如Array.map和Array.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中,将对象person的age更新并返回。原始的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); // -> ?

总结

以上所述是小编给大家介绍的JavaScript的值传递和引用传递,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
封装了一个支持匿名函数的Javascript事件监听器
Jun 05 Javascript
教你使用javascript简单写一个页面模板引擎
May 05 Javascript
学习使用AngularJS文件上传控件
Feb 16 Javascript
jQuery简单实现仿京东分类导航层效果
Jun 07 Javascript
JS实现自动阅读单词(有道单词本添加功能)
Nov 14 Javascript
JavaScript之map reduce_动力节点Java学院整理
Jun 29 Javascript
微信小程序事件对象中e.target和e.currentTarget的区别详解
May 08 Javascript
JS实现可切换图片的幻灯切换效果示例
May 24 Javascript
VUE DEMO之模拟登录个人中心页面之间数据传值实例
Oct 31 Javascript
Vue初始化中的选项合并之initInternalComponent详解
Jun 11 Javascript
如何利用 JS 脚本实现网页全自动秒杀抢购功能
Oct 12 Javascript
关于JavaScript回调函数的深入理解
Jun 27 Javascript
详解JavaScript中typeof与instanceof用法
Oct 24 #Javascript
使用electron实现百度网盘悬浮窗口功能的示例代码
Oct 24 #Javascript
JavaScript根据json生成html表格的示例代码
Oct 24 #Javascript
vue项目引入Iconfont图标库的教程图解
Oct 24 #Javascript
vue中的router-view组件的使用教程
Oct 23 #Javascript
jQuery pagination分页示例详解
Oct 23 #jQuery
jquery.pagination.js分页使用教程
Oct 23 #jQuery
You might like
用PHP实现的随机广告显示代码
2007/06/14 PHP
php的POSIX 函数以及进程测试的深入分析
2013/06/03 PHP
golang 调用 php7详解及实例
2017/01/04 PHP
PHP守护进程化在C和PHP环境下的实现
2017/11/21 PHP
Mootools 1.2 手风琴(Accordion)教程
2009/09/15 Javascript
关于火狐(firefox)及ie下event获取的两种方法
2012/12/27 Javascript
JavaScript splice()方法详解
2020/09/22 Javascript
让input框实现类似百度的搜索提示(基于jquery事件监听)
2014/01/31 Javascript
原生javascript实现的分页插件pagenav
2014/08/28 Javascript
Juery解决tablesorter中文排序和字符范围的方法
2015/05/06 Javascript
jQuery实现仿新浪微博浮动的消息提示框(可智能定位)
2015/10/10 Javascript
js实现ctrl+v粘贴上传图片(兼容chrome、firefox、ie11)
2016/03/09 Javascript
微信小程序 教程之wxapp 视图容器 view
2016/10/19 Javascript
NPM 安装cordova时警告:npm WARN deprecated minimatch@2.0.10: Please update to minimatch 3.0.2 or higher to
2016/12/20 Javascript
JavaScript输入分钟、秒倒计时技巧总结(附代码)
2017/08/17 Javascript
再谈Angular4 脏值检测(性能优化)
2018/04/23 Javascript
webpack4.x开发环境配置详解
2018/08/04 Javascript
前端防止用户重复提交js实现代码示例
2018/09/07 Javascript
jQuery实现经典的网页3D轮播图封装功能【附源码下载】
2019/02/15 jQuery
vue组件中实现嵌套子组件案例
2020/08/31 Javascript
[04:10]2018年度CS GO玩家最喜爱的主播-完美盛典
2018/12/16 DOTA
在Python中使用模块的教程
2015/04/27 Python
Flask的图形化管理界面搭建框架Flask-Admin的使用教程
2016/06/13 Python
python在Windows下安装setuptools(easy_install工具)步骤详解
2016/07/01 Python
使用PyTorch训练一个图像分类器实例
2020/01/08 Python
Python基于pandas绘制散点图矩阵代码实例
2020/06/04 Python
微软开源最强Python自动化神器Playwright(不用写一行代码)
2021/01/05 Python
德国宠物用品、宠物食品及水族馆网上商店:ZooRoyal
2017/07/09 全球购物
新闻报道策划方案
2014/06/11 职场文书
大学生预备党员自我评价
2015/03/04 职场文书
人事行政主管岗位职责
2015/04/09 职场文书
中学总务处工作总结
2015/08/12 职场文书
fastdfs+nginx集群搭建的实现
2021/03/31 Servers
Golang 入门 之url 包
2022/05/04 Golang
MySQL中JOIN连接的基本用法实例
2022/06/05 MySQL
Nginx开源可视化配置工具NginxConfig使用教程
2022/06/21 Servers