JavaScript进阶(三) 值传递和引用传递
从C语言开始
- void swap1(int a, int b);
- void swap2(int* a, int* b)
这里swap1是不能交换两个数的值的,swap2可以。那为什么呢?有教材会说,第一个是值传递,第二个是引用传递,传递的是指针,所以第二个可以。好吧,这个解释和没说一样,那下面我就来解释一下,调用这两个函数的时候,到底发生了什么,为什么一个可以交换,另一个不可以。为了方便描述,我把这两个函数的调用代码也写出来
- int main() {
- int a = 3;
- int b = 4;
- swap1(a, b); //此时a = 3, b = 4;
- int* pa = &a;
- int* pb = &b; //为了方便解释,增加这两个临时变量,否则直接写swap2(&a, &b)的话,这行代码做的事情太多,不好解释。
- swap2(pa, pb); //此时a = 4, b = 3;
- return 0;
- }
函数的执行是在栈中,下图描述了swap1执行开始和结束的时候,栈中的情况。
回到JS
- function reverse1(array) {
- var temp = [];
- for (var i = array.length - 1; i > -1; i--) {
- temp.push(array[i]);
- }
- array = temp;
- }
- function reverse2(array) {
- var temp = [];
- for (var i = array.length - 1; i > -1; i--) {
- temp.push(array[i]);
- }
- for (var i = 0; i < array.length; i++) {
- array[i] = temp[i]
- }
- }
这两个函数都是先将一个反序完成的数组存储在temp里面,然后赋值给入参array,就是赋值的方式有所不同。这个不同的赋值方式也导致了结果的不同,结果就是reverse1无法完成工作,reverse2可以。为了解答这个问题,我先讲一下JS里面,内存中对象是如何存储的。当一行代码 var temp = [] 被运行的时候,内存中是这样的:
其中蓝色的是栈,黑框的是堆,用来动态分配内存,最右绿色的表示这段堆的起始地址。也就是说当声明一个对象的时候,栈中保存的内容只是一个指针,真正的内容在堆中。以此为基础,我们再来看一下当函数reverse1执行的时候,内存中如何实现的。为方便举例,假设传入的数组为[1,2,3];
上图为执行前,下图为执行后。当函数reverse1执行时,in作为参数传入。传入参数时,类似C语言的引用传递,将地址复制了一份,压栈传到子函数中。所以两个函数中的变量是指向同一个位置的。当reverse1执行时,temp中存储了array的反序,最后一行赋值的时候,你就看到了如下面的图表示的那样,reverse1中的array确实指向了新的反序数组,但是调用者中的局部变量in却丝毫未动。所以导致了reverse1无法完成反序功能。
那么我们再看reverse2. reverse2中的第二个循环逐个给数组的内容复制,其实它操纵的内存空间就是array指向的区域,我们又知道array和in指向了同一个区域,所以in指向的区域也被改变了。
总结一下以上所说的,
- JS中布尔,数字为基本数据类型,是值传递。无法作为引用传递。所以JS中无法实现基本数据类型的swap函数。
- 对象是引用传递。当传递对象给子函数时,传递的是地址。子函数使用这个地址来操作修改传入的对象。但是如果在子函数修改该地址指向的位置时,这个改变将无法作用于调用者。
- 引用传递其实还是值传递,只是传入的值是个地址,并且该地址指向了一段保存了对象数据的内存。这点和C中的引用传递类似。
特别说一下String
- function foo(s) {
- s = "\"" + s + "\""
- }
那么正确的函数应该怎写呢?你可能会想,应该使用String对象的函数来修改String的内容。这么想是对的,但是很不幸,JS提供的String没有任何一个可以修改String内容的函数。有人说不对,比如字符串连接函数,concat,转大小写函数toUpperCase,toLowerCase。事实上这两个函数只是返回了一个新的String对象,其原本的值兵没有改动。这个你可以去做实验看看。所以String对象被建立好之后,就再也无法改动了,所以无法用一个子函数来修改它的值。又由于String可以用 == 来判断其内容是否相等,所以它的各方面特性都很像基本数据类型。但是还有一点不一样,请看下面的例子:
- var a = 1
- var b = 1
- a == b //true
- a === b //true
- var s1 = "sdf"
- var s2 = "sdf"
- s1 == s2 //true
- s1 === s2 //true
- s3 = new String("sdf")
- s1 === s3 //false
对于数字,估计各位没有疑问吧。那么对于字符串来说,== 比较的是两个字符串的内容,这个应该也没有疑问。那么===呢?并且为什么s1===s2为true,s1===s3为false呢?
JavaScript进阶(三) 值传递和引用传递的更多相关文章
- JavaScript传递变量:值传递?引用传递?
今天在看 seajs-2.2.1/src/util-events.js源码,里面有段代码不是很理解: var events = data.events = {} // Bind event seajs ...
- JavaScript 函数参数传递到底是值传递还是引用传递
tips:这篇文章是听了四脚猫的js课程后查的,深入的理解可以参看两篇博客: JavaScript数据类型--值类型和引用类型 JavaScript数据操作--原始值和引用值的操作本质 在传统的观念里 ...
- Android For JNI(三)——C的指针,指针变量,指针常见错误,值传递,引用传递,返回多个值
Android For JNI(三)--C的指针,指针变量,指针常见错误,值传递,引用传递,返回多个值 C中比较难的这一块,大概就是指针了,所以大家还是多翻阅一下资料,当然,如果只是想了解一下,看本篇 ...
- 188W+程序员关注过的问题:Java到底是值传递还是引用传递?
在逛 Stack Overflow 的时候,发现了一些访问量像阿尔卑斯山一样高的问题,比如说这个:Java 到底是值传递还是引用传递?访问量足足有 188万+,这不得了啊!说明有很多很多的程序员被这个 ...
- [转帖]Stack Overflow上188万浏览量的提问:Java 到底是值传递还是引用传递?
Stack Overflow上188万浏览量的提问:Java 到底是值传递还是引用传递? http://www.itpub.net/2019/12/03/4567/ 在逛 Stack Overfl ...
- Java中引用类型变量,对象,值类型,值传递,引用传递 区别与定义
一.Java中什么叫做引用类型变量?引用:就是按内存地址查询 比如:String s = new String();这个其实是在栈内存里分配一块内存空间为s,在堆内存里new了一个Stri ...
- 【转载】C++ 值传递、指针传递、引用传递详解
原文链接:http://www.cnblogs.com/yanlingyin/ 值传递: 形参是实参的拷贝,改变形参的值并不会影响外部实参的值.从被调用函数的角度来说,值传递是单向的(实参->形 ...
- Java面向对象-方法的值传递和引用传递
Java面向对象-方法的值传递和引用传递 0 发布时间:『 2016-08-21 14:21』 博客类别:Java核心基础 阅读(197) 评论(0) Java面向对象-方法的值传递和引用传递 方 ...
- 一道笔试题来理顺Java中的值传递和引用传递
题目如下: private static void change(StringBuffer str11, StringBuffer str12) { str12 = str11; str11 = ...
随机推荐
- 一、cocos2dx之如何优化内存使用(高级篇)
本文由qinning199原创,转载请注明:http://www.cocos2dx.net/?p=93 一.内存优化原则 为了优化应用内存,你应该知道是什么消耗了你应用的大部分内存,答案就是Textu ...
- org.tigris.subversion.javahl.ClientException: Attempted to lock an already-locked dir svn: Working c
Eclipse插入svn提交出现:org.tigris.subversion.javahl.ClientException: Attempted to lock an already-locked d ...
- levelDB跳表实现
跳表的原理就是利用随机性建立索引,加速搜索,并且简化代码实现难度.具体的跳表原理不再赘述,主要是看了levelDB有一些实现细节的东西,凸显自己写的实现不足之处. 去除冗余的key template& ...
- ThreadLocal 和 InheritableThreadLocal (引用)
ThreadLocal:http://www.cnblogs.com/moonandstar08/p/4912673.html InheritableThreadLocal: http://www. ...
- Net FLow Template
EK Template : bool bfs(int src, int des){ memset(pre, -, sizeof(pre)); while(!que.empty()) que.pop( ...
- Flask web开发 请求拦截和预处理
我们在开发WEB应用时,往往会需要对所有的url请求进行拦截,做些预处理,比如权限处理.日志等统一处理. 本文介绍一下Flask中的处理机制.我们通过一个简单的例子来说明. 1.编写一个简单应用 ru ...
- 基于visual Studio2013解决C语言竞赛题之0602最大值函数
题目
- Git学习笔记总结和注意事项
一.Git简单介绍 Git是眼下世界上最先进的分布式版本号控制系统.其特点简单来说就是:高端大气上档次! 二.Windows上Git安装 最早Git是在Linux上开发的.非常长一段时间内.Git也仅 ...
- cocos2d-x plist文件各个属性的含义
plist文件是小图在合并成大图的时候生成的配置文件,所以须要由大图+plist文件能够全然还原出小图. 合图后的大图整体尺寸面积一般要小于原来的小图面积之和,是由于合图中会去掉小图周围的透明像素(当 ...
- Android 实现在线程中联网
其实我们要牢记的是,对数据流的操作都是阻塞的,在一般情况下,我们是不需要考虑这个问题的,但是在Android 实现联网的时候,我们必须考虑到这个问题.比如:从网络上下载一张图片: Java代码: pu ...