通过实例了解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 相关文章推荐
利用javascript/jquery对上传文件格式过滤的方法
Jul 25 Javascript
javascript 简练的几个函数
Aug 29 Javascript
深入认识javascript中的eval函数
Nov 02 Javascript
JS事件在IE与FF中的区别详细解析
Nov 20 Javascript
JS实现根据出生年月计算年龄
Jan 10 Javascript
js脚本实现数据去重
Nov 27 Javascript
jQuery简单自定义图片轮播插件及用法示例
Nov 21 Javascript
微信小程序 安全包括(框架、功能模块、账户使用)详解
Jan 16 Javascript
bootstrap suggest搜索建议插件使用详解
Mar 25 Javascript
通过示例彻底搞懂js闭包
Aug 10 Javascript
详解Angular结合zTree异步加载节点数据
Jan 20 Javascript
微信小程序常用简易小函数总结
Feb 01 Javascript
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
如何取得中文字符串中出现次数最多的子串
2013/08/08 PHP
yii分页组件用法实例分析
2015/12/28 PHP
jquery特效 幻灯片效果示例代码
2013/07/16 Javascript
深入理解Javascript里的依赖注入
2014/03/19 Javascript
JavaScript运动减速效果实例分析
2015/08/04 Javascript
JavaScript编程学习技巧汇总
2016/02/21 Javascript
JS对HTML表格进行增删改操作
2016/08/22 Javascript
Angularjs上传图片实例详解
2017/08/06 Javascript
详解Vue文档中几个易忽视部分的剖析
2018/03/24 Javascript
JS打印彩色菱形的实例代码
2018/08/15 Javascript
Vue.js组件高级特性实例详解
2018/12/24 Javascript
jQuery使用$.extend(true,object1, object2);实现深拷贝对象的方法分析
2019/03/06 jQuery
实现一个 Vue 吸顶锚点组件方法
2019/07/10 Javascript
Vue自定义组件双向绑定实现原理及方法详解
2020/09/03 Javascript
VUE中鼠标滚轮使div左右滚动的方法详解
2020/12/14 Vue.js
vue实现拖拽进度条
2021/03/01 Vue.js
python实现通过pil模块对图片格式进行转换的方法
2015/03/24 Python
用Python编写简单的定时器的方法
2015/05/02 Python
Python NumPy库安装使用笔记
2015/05/18 Python
使用Python写个小监控
2016/01/27 Python
python面向对象_详谈类的继承与方法的重载
2017/06/07 Python
pandas把dataframe转成Series,改变列中值的类型方法
2018/04/10 Python
TensorFlow入门使用 tf.train.Saver()保存模型
2018/04/24 Python
基于随机梯度下降的矩阵分解推荐算法(python)
2018/08/31 Python
PyTorch里面的torch.nn.Parameter()详解
2020/01/03 Python
初学者学习Python好还是Java好
2020/05/26 Python
苹果美国官方商城:Apple美国
2016/08/24 全球购物
美国的Eastbay旗下的运动款子品牌:Final-Score
2018/01/01 全球购物
李维斯法国官网:Levi’s法国
2019/07/13 全球购物
Blank NYC官网:夹克、牛仔裤等
2020/12/16 全球购物
财务方面个人工作的自我评价
2013/12/28 职场文书
公司年会晚宴演讲稿
2014/01/06 职场文书
宣传工作经验材料
2014/06/02 职场文书
文艺演出策划方案
2014/06/07 职场文书
2014年党员自我评议对照检查材料
2014/09/20 职场文书
Golang生成Excel文档的方法步骤
2021/06/09 Golang