通过实例了解js函数中参数的传递


Posted in Javascript onJune 15, 2019

先看一道JS的笔试题:

var setObj=function(o){
o.name="xiaoming";
o={};
o.name="xiaohong";
}
var p={name:"xixi",age:24};
setObj(p);
console.log(p);

答案是{name:xiaoming,age24};

在JavaScript中函数参数默认为引用类型。

一.函数传递值类型:

代码实例如下:

function addNum(num){
 num+=10;
 return num;
}
var num=10;
var result=addNum(num);
console.log(num);
console.log(result);

以上代码的弹出值分别为:10和20,下面进行一下分析:

声明变量num并复制为10,这个是num是一个值类型,当为函数传递参数的时候,是将此值复制一份传递给函数,所以在函数执行之后,num本身的值并没有被改变,函数中被改变的值仅仅是一个副本而已。

二.函数传递引用类型:

function setName(obj){ 
obj.name="青岛新锐"; 
} 
var web=new Object(); 
web.name="蚂蚁部落";
 setName(web); 
console.log(web.name);

以上代码的弹出值是:“青岛新锐”,下面进行一下分析:

声明一个对象web,它是一个引用类型,当为函数传递参数的时候,是传递的web对象的引用,也就是此对象的内存地址,所以在函数中修改属性的对象就是函数外面创建的对象本身。

三.加深理解:

function setName(obj){ 
 obj.name="青岛新锐"; 
 obj=new Object(); 
 obj.name="蚂蚁部落"; 
 } 
 var web=new Object(); 
setName(web); 
console.log(web.name);

以上代码的弹出值是:青岛新锐,很多人可能会以为将会弹出“蚂蚁部落”,下面进行一下简单的分析:

在函数外面创建一个对象,并将对象的引用赋值给变量web,web中存储的是对象在内存中的存储地址,当为函数传递参数的,就是传递的在函数外面创建的对象的地址。在函数中,为外面创建的对象创建一个自定义属性name并赋值为“青岛新锐”,然后又创建一个新的对象,并将新对象的地址赋值给obj,这个时候obj指向的并不是函数外面创建的对象,所以外面对象name属性不会被改变。

这样写对于其他语言的程序员来说是很难接受的,我们在实际的开发中避免这样的写法,因为这样会造成全局作用域污染。最近在读《javascript高级程序设计》时碰到了js传递方式的问题,花费了些时间,不过总算明白了。

数据类型

在 javascript 中数据类型可以分为两类:

  • 基本类型值 primitive type,比如Undefined,Null,Boolean,Number,String。
  • 引用类型值,也就是对象类型 Object type,比如Object,Array,Function,Date等。

变量的复制

众所周知,js中变量的基本类型和引用类型保存方式是不同的,这也就导致变量复制时也就不同了。如果从一个变量向另一个变量复制基本类型的值时,会将前者的值克隆一个,然后将克隆的值赋值到后者,因此这两个值是完全独立的,只是他们的value相同而已。

var num1 = 10;
var num2 = num1;
console.log(num2);//10

上面的num1中被保存的值为10,当把num1的值赋值给num2时,num2的值也为10。但是这两个10是完全独立的,num2中的10只是被克隆出来的,相当于我写了一个word文档,把它放到了num1的文件夹中,然后我再复制这个word文档,就叫word副本吧,然后把这个副本放到num2的文件夹下,这两个word文档是完全一样的,修改任何一个都不会影响两一个。

num2 += 1;
console.log(num1); //10
console.log(num2); //11

从上面可以看出修改num2的值,num1的值未发生变化。再来看下引用类型的复制。当从一个变量向另一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份放到为新变量分配的空间中。

var obj1 = {
name : "111"
};
var obj2 = obj1;
console.log(obj2.name); //111
obj2.name = "222";
console.log(obj1.name); //222

第一次打印出的结果为“111”,这个我们很容易理解,但是第二次打印出来的是“222”,有点莫名其妙了。这就是引用类型和基本类型的不同之处了。复制对象时并不会在堆内存中新生成一个一模一样的对象,只是多了一个保存指向这个对象指针的变量罢了。将obj1的值复制给obj2,而这个值的副本实际上是一个指针,这个指针指向存储在堆中的一个对象,也就是说创建了一个新的内存地址传给了obj2,obj1和obj2两个变量同时指向了同一个Object,当去改变这个对象时,他们的值都会改变,也就是说他们中任何一个作出的改变都会反映在另一个身上。下面的简易图可能更明了些。

通过实例了解js函数中参数的传递

函数参数的传递

《js高级程序设计》上是这样叙述参数传递的:所有函数的参数都是按值传递的,也就是说把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。所以如果能理解变量的复制,那么参数的传递也就很简单了。还是先从基本类型举例说明吧。

var count = 10;
function num(num1){
num1 = 1;
return num1;
}
var result = num(count1);
console.log(result);//1
console.log(count);//10,并未变成1

这个例子很容易理解,实际就是创建了一个count的副本,然后把count的值传入参数中,因为函数中定义了参数的值,所以1就将10覆盖了,最后的result返回1,而count并未发生变化。看一个有关传递对象的例子。

var person = {
name : "Tom"
};
function obj(peo){
peo.name = "Jerry";
return peo;
}
var result = obj(person);
console.log(result.name);// Jerry
console.log(person.name);// Jerry

在上面的例子中,把person复制传入obj()中,peo和person指向了同一个对象,而在peo中修改了name属性,其实修改了它们共同指向的对象的name属性,相对应的外部person所引用的name属性也就改变了,所以打印出来的为Jerry。其实这个乍一看,感觉引用类型的参数是按照引用传递的,这就是我最初犯得错误。再来看一个例子。

var person = {
name : "Tom"
}; 
function obj(peo){
peo = {
name : "Jerry"
};
return peo;
}
var result = obj(person);
console.log(result.name);// Jerry
console.log(person.name);// Tom

上面的例子中,在函数中重新定义了一个对象,也就是现在堆内存中有两个对象,外部的person指向的是老的对象,被传入参数后指向的是新定义的对象,所以调用后返回的值是新定义的对象的值。如果是参数是按引用传递的,那么person.name打印出来的结果为Jerry,从这点可以得出参数是按值传递的(有的地方叫做按共享传递)。

我们拿老罗推荐的《人类简史》把它形象化,描述的不太好。简史的第一章标题是“认知革命”,我们把它名字改为“person”根据后面的页码数可以直接找到“认知革命”的内容“也就是peoson所指向的对象”,第二章是“农业革命”,我们把它为“result”,其分目录有一节“记忆过载”(改名为“peo”),同样可以直接根据页码找到这一节内容。

现在我们把“person”复制到“peo”中,第二章中的“peo”这一节就变成了“person”,而我们根据第一章中的“peoson”找到的还是第一章的内容,这是因为它们指向了不同的内容板块,互不干扰。在这里,堆内存就是每章的内容,而第一章和第二章内容是2个不同的对象,而这两者互不相干,因此打印外部person.name时,结果仍是以前的对象的属性值。

结论

总而言之,在js中参数都是按值传递的。我写例子粗糙了些,《javascript高级程序设计》中的例子描述的更清晰一些,更易理解。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
设置下载不需要倒计时cookie(倒计时代码)
Nov 19 Javascript
字符串的replace方法应用浅析
Dec 06 Javascript
JS this作用域以及GET传输值过长的问题解决方法
Aug 06 Javascript
javascript中call和apply的用法示例分析
Apr 02 Javascript
Knockoutjs 学习系列(二)花式捆绑
Jun 07 Javascript
Bootstrap图片轮播组件使用实例解析
Jun 30 Javascript
浅谈javascript的url参数parse和build函数
Mar 04 Javascript
vue2.0开发入门笔记之.vue文件的生成和使用
Sep 19 Javascript
php 解压zip压缩包内容到指定目录的实例
Jan 23 Javascript
jquery+css3实现的经典弹出层效果示例
May 16 jQuery
微信小程序基于高德地图API实现天气组件(动态效果)
Oct 22 Javascript
jQuery实现全选按钮
Jan 01 jQuery
RxJS的入门指引和初步应用
Jun 15 #Javascript
js中let能否完全替代IIFE
Jun 15 #Javascript
回顾Javascript React基础
Jun 15 #Javascript
详解微信小程序胶囊按钮返回|首页自定义导航栏功能
Jun 14 #Javascript
微信小程序版本自动更新的方法
Jun 14 #Javascript
vue+express+jwt持久化登录的方法
Jun 14 #Javascript
深入剖析JavaScript instanceof 运算符
Jun 14 #Javascript
You might like
谈一谈收音机的高放电路
2021/03/02 无线电
动态新闻发布的实现及其技巧
2006/10/09 PHP
php开发工具之vs2005图解
2008/01/12 PHP
php下批量挂马和批量清马代码
2011/02/27 PHP
PHP静态调用非静态方法的应用分析
2013/05/02 PHP
配置php.ini实现PHP文件上传功能
2014/11/27 PHP
php实现基于PDO的预处理示例
2017/03/28 PHP
ThinkPHP中图片按比例切割的代码实例
2019/03/08 PHP
PHP扩展Swoole实现实时异步任务队列示例
2019/04/13 PHP
php实现的顺序线性表示例
2019/05/04 PHP
仅用[]()+!等符号就足以实现几乎任意Javascript代码
2010/03/01 Javascript
javascript sudoku 数独智力游戏生成代码
2010/03/27 Javascript
Prototype源码浅析 Number部分
2012/01/16 Javascript
jQuery下的动画处理总结
2013/10/10 Javascript
js实现页面跳转重定向的几种方式
2014/05/29 Javascript
javascript 小数取整简单实现方式
2014/05/30 Javascript
浅谈JavaScript正则表达式分组匹配
2015/04/10 Javascript
JavaScript实现删除,移动和复制文件的方法
2015/08/05 Javascript
jquery+CSS3实现3D拖拽相册效果
2016/07/18 Javascript
jquery中绑定事件的异同
2017/02/28 Javascript
vue3 watch和watchEffect的使用以及有哪些区别
2021/01/26 Vue.js
python文件操作相关知识点总结整理
2016/02/22 Python
Python向MySQL批量插数据的实例讲解
2018/03/31 Python
python实现K近邻回归,采用等权重和不等权重的方法
2019/01/23 Python
Python动态赋值的陷阱知识点总结
2019/03/17 Python
基于python生成器封装的协程类
2019/03/20 Python
PyTorch的深度学习入门之PyTorch安装和配置
2019/06/27 Python
CSS3制作3D立方体loading特效
2020/11/09 HTML / CSS
HTML5中的拖放实现详解
2017/08/23 HTML / CSS
伦敦香水公司:The London Perfume Company
2019/11/13 全球购物
软件工程师岗位职责
2013/11/16 职场文书
党的群众路线查摆剖析材料
2014/10/10 职场文书
实习生矿工检讨书
2014/10/13 职场文书
MySQL sql_mode的使用详解
2021/05/08 MySQL
sql server 累计求和实现代码
2022/02/28 SQL Server
如何使用注解方式实现 Redis 分布式锁
2022/07/23 Redis