`

Javascript 按值和按引用(By Value versus By Reference)

阅读更多

像其他的语言一样,Javascript也包括下列3种重要的使用数据的方式:

1,拷贝,比如说赋予一个新的变量

2,传参,传入一个函数或者方法

3,比较,比较两个数据的值时候相等

想要深入理解一门语言,一定要理解这3种使用数据的方式。

 

在处理数据的时候,最常用的2种方式是按值按引用

在传值的方式下,值本身是最重要的数据:

  1. 用于拷贝的时候,总是重新拷贝一份新的值存放在新的变量或者对象属性或者数组元素中,新值和旧值是完全独立且分开存储的;
  2. 用于传参的时候,总是拷贝一份新的值传入函数,在函数里无论对该拷贝做任何操作都不会影响原始值;
  3. 用于比较的时候,两个截然不同的值需要按字节比较以保证他们是完全相等的。

在传引用的方式下,值只有一份拷贝,操作的总是指向该值的引用。变量不会直接持有该值,而是持有引用。不论是在做赋值,入参还是比较操作的时候,操作的对象总是引用。

  1. 用于拷贝的时候,只是分配指向该值的引用,而不是值的拷贝或者值本身。赋值之后,新的变量指向的和原始变量指向的是同一个值。可以通过任何一个变量来操作原始值,比如说改变了新的变量的值,那么原始变量的值也会随着改变。
  2. 用于传参的时候,函数可以通过该应用修改原始值,并且在函数外面可见。
  3. 用于比较的时候,比较两个变量指向的是不是同一个原始值,指向不同的两个值的两个变量总是不等的。

下面的列表是传值和传引用的对比:

传值(By value) 传引用(By reference)
拷贝(Copy) 拷贝一份新值,他们是两个完全不同且独立的拷贝 只拷贝了引用,如果新的变量值改变了,那么原始的引用也会随之改变
传参(Pass) 一份新的拷贝作为参数传入函数,在函数内改变只是该拷贝,不会影响原始值 引用作为参数传入函数,在函数内改变该引用会造成原始值的改变
比较(Compare) 一般都是按字节比较,以保证两个不同的值是相等的 只要判断两个引用是否指向同一个值,即使字节码相等,两个指向不同的的值的引用依然不等

 

基本类型和引用类型

Javascript处理数据的一个基本原则是,基本类型按值处理,引用类型,顾名思义,按引用来操作。Number和Boolean是基本类型,他们可以容易的被javascript解释器转换成字节码来处理。Object是引用类型,Array和Function是特定类型的Object,他们都是引用类型。这些引用类型可以包含任意的属性或者元素,所以他们无法像基本类型一样转换成字节码来处理。而且对象和数组都可以变得很大,按值操作对他们来说毫无意义,因为如果进行拷贝或者比较操作会占用大量内存。

 

String类型如何处理?

一个字符串可以是任意长度,看起来他们应该按引用来处理。事实上,由于他们不是对象,Javascript经常简单的把他们当成基本类型来处理,但是String不能适配按值或者按引用模型,我们将在后面讨论。

 

下面就让我们用一系列例子来说明按值和按引用的区别。

按值拷贝,传参,比较

// 按值拷贝
var n = 1;  // 变量n持有值1
var m = n;  // 拷贝值: 变量m持有不同的值1

// 按值传参
// 你会发现,函数并没有按我们的要求工作
function add_to_total(total, x)
{
    total = total + x;  // 只是内部的拷贝,total变化只在函数内可见
}

//调用函数,按值传入Number类型n,m
add_to_total(n, m);

//验证结果,发现n并没有变化
if (n == 1) m = 2;  // 把m的值改成2


 按引用拷贝,传参,比较

//创建一个对象类型,xmas是一个指向Date的引用
var xmas = new Date(2007, 11, 25);
// 当拷贝引用的时候,新的变量还是指向原始对象
var solstice = xmas;  // 两个变量指向同一个原始对象

// 通过新的变量改变原始对象
solstice.setDate(21);

// 该改变对于老的引用依然可见
xmas.getDate( );  // 返回21, 而不是原始值25

// 对象和数组同理
// 下面的函数演示给数组每一个元素增加一个值
// 指向数组的引用被当成参数传入函数,而不是数组的拷贝
// 在函数里的改变对于函数外部同样可见
function add_to_totals(totals, x)
{
    totals[0] = totals[0] + x;
    totals[1] = totals[1] + x;
    totals[2] = totals[2] + x;
}

// 按引用比较
// 由于两个引用指向的是同一个对象,所以他们相等
(xmas == solstice)  // 返回true

// 重新创建两个引用,指向同一天
var xmas = new Date(2007, 11, 25);
var solstice_plus_4 = new Date(2007, 11, 25);

//但是按照"按引用比较"原则,他们是不等的!
(xmas != solstice_plus_4)  // 返回 true

 

”按引用传参“的含义:

按引用传参的目的是为了在函数内部对传入的参数进行修改,比如对传入的对象属性或者数组元素进行修改,这个对于函数外部依然是可见的,但是如果开发人员在函数内部用另一个指向对象或者数组的引用重写了传入的引用,那么这个改变在函数外部依然是不可见的。所以有的开发人员就认为,”按引用传参“其实就是“按值传参”,只不过传入的值是引用本身,而不是引用指向的原始对象。

 

下面的例子演示了这个问题:

//这是函数add_to_totals( )的另一个版本,由于在函数内部,开发人员试图直接修改引用,所以该函数并不能正常工作
function add_to_totals2(totals, x)
{
    newtotals = new Array(3);
    newtotals[0] = totals[0] + x;
    newtotals[1] = totals[1] + x;
    newtotals[2] = totals[2] + x;
    totals = newtotals;  // totals在函数外部并没有任何变化
}
 

字符串的拷贝和传参

本文之前提过,字符串无法用按值和按引用的二分法来严格定义。由于字符串不是对象,我们可以很自然的把它当成基本类型,按照基本类型的处理原则,他们应该是按值来处理。但是字符串又可以是任意长度,如果在拷贝,传参和比较操作用都是基于字节码来操作的话,是非常低效的,所以我们又很自然的认为字符串应该按照引用类型来处理。

 

让我们停止无谓的猜测,写段代码了实验吧。如果String是按引用来拷贝,传参的话,我们应该可以通过引用来修改字符串的原始值。

 

在写代码之前,有一个问题困扰着我们,我们没有办法去修改String。如charAt()方法返回传入的字符所在的位置,但是我们找不到相应的setCharAt()方法。这不是设计的缺陷,而是有意为之。在Javascript中,String被故意设计成不可变对象,没有任何javascript语法,函数,或者属性允许你修改字符串。

 

由于String的不可变行,讨论之前的问题已经毫无意义:没有办法判断字符串是按值还是按引用处理。我们可以这么认为,为了提高效率,字符串是按引用处理的,但是这个对于我们写代码并不重要。

 

字符串比较

尽管我们无法判断字符串到底是按值还是按引用进行拷贝和传参,但是我们可以写一段测试代码来判断String在比较操作中是按值还是按引用处理的。

 

字符串是按值还是按引用比较的?

// 判断字符串是按值还是按引用比较是很容易的。
// 我们可以比较含有相同字符串的两个变量,
//如果是按值比较的话,他们就是相等的;如果是按引用比较的话,他们是不等的
var s1 = "hello";
var s2 = "hell" + "o";
if (s1 == s2) document.write("Strings compared by value");


 上面这段实验代码显示在Javascript中,字符串是按值比较的,这可能令你大吃一惊,因为在其他主流语言c,c++,java中,字符串都是按引用比较的。如果你想比较两个不同的字符串,你必须调用特定的函数或者方法。作为一个相对高级语言,Javascript认为大部分情况下,当你比较两个字符串时,你是要比较他们的值是否相同。因此,不管事实如何,我们认为,为了效率考虑,Javascript在做字符串拷贝和传参的操作时,是按引用处理的,在做字符串比较操作时,是按值处理的。

 

 

总结

                                             JavaScript数据类型处理方式

类型(Type) 拷贝(Copy by) 传参(Pass by) 比较(Compare by)
数字(number) 值(Value) 值(Value) 值(Value)
布尔(boolean) 值(Value) 值(Value) 值(Value)
字符串(string) 不可变(Immutable) 不可变(Immutable) 值(Value)
对象(object) 引用(Reference) 引用(Reference) 引用(Reference)

 

 

参考文献:

JavaScript: The Definitive Guide, 5th Edition

 

 

分享到:
评论

相关推荐

    Versus AI Duel Game in JavaScript Free Source Code.zip

    本篇文章将深入探讨一个基于JavaScript实现的“Versus AI Duel Game”,通过分析源代码,我们将揭示其中的关键技术和设计思路,为JavaScript游戏开发者提供宝贵的参考。 1. **基础架构** 这款游戏的实现离不开...

    Induction versus permanent magnet motors

    1. **功率密度**:永磁电机能够提供更高的功率密度,即单位质量和体积内产生的功率更大。这对于许多工业应用而言至关重要,特别是在纸张制造等需要紧凑型设计的领域。 2. **功率因数**:永磁电机拥有更优的功率因数...

    Java 9 Programming By Example

    Java 9 Programming By Example by Peter Verhas English | 26 Apr. 2017 | ASIN: B01KOG6SWI | 504 Pages | AZW3 | 4.1 MB Key Features We bridge the gap between “learning” and “doing” by providing ...

    miss rate versus false positives per image(FPPI)绘制代码

    本文件用于绘制miss rate versus false positives per image(FPPI)曲线 使用方法参考:https://blog.csdn.net/weixin_38705903/article/details/109654157

    Vs Soccer Game using JavaScript with Free Source Code.zip

    这个项目还提供免费源代码,这为学习者和开发者提供了深入了解游戏开发和JavaScript编程的机会。 【描述分析】 "JavaScript" 是唯一提供的描述,它是一种广泛用于网页和网络应用的编程语言。JavaScript通常用于实现...

    javascript语言精粹(中英文版)

    The function Statement Versus the function Expression Section B.10. Typed Wrappers Section B.11. new Section B.12. void Appendix C. JSLint Section C.1. Undefined Variables and Functions Section...

    versus:使用指定为字符串的比较运算符比较两个变量

    与.js 使用指定为字符串的比较运算符比较两个变量。 (这实际上抽象成可重用模块 。) 用法 versus ( 1 , '==' , 2 ) ;... Versus 使用进行==和!=比较。 所以我们可以这样做: versus ( { foo : 1 } ,

    Squanderville versus Thriftville,

    总的来说,"Squanderville versus Thriftville"不仅是对个人理财的教诲,也是对国家经济政策的深刻反思。通过理解巴菲特的寓言,我们可以更好地理解消费、储蓄、债务和经济增长之间的复杂关系,从而做出更明智的决策...

    Science versus naturePPT教案.pptx

    Science versus naturePPT教案.pptx

    ZeroC Ice3.7 官方手册英文版加注中文书签

    - **值传递与引用传递的区别(Pass-by-Value Versus Pass-by-Reference)**:对比分析了值传递与引用传递的不同之处。 - **按值传递接口(Passing Interfaces by Value)**:介绍了按值传递接口的具体应用场景。 ...

    Graph-Cuts versus Level-Sets,ECCV06_tutorial_partI-partIV

    Graph-Cuts与Level-Sets是计算机视觉和图像处理领域中的两种关键算法,它们在解决分割问题上具有重要的应用。在ECCV06(欧洲计算机视觉会议2006)的教程中,这部分内容深入探讨了这两种技术的原理、优势和局限性。...

    Pi calculus versus Petri nets

    标题与描述:“Pi calculus versus Petri nets”,这一主题聚焦于两种在信息技术领域内,特别是流程感知信息系统(Process-Aware Information Systems, PAISs)中应用广泛的建模工具之间的比较:Pi calculus(π演算...

    Spring-Reference_zh_CN(Spring中文参考手册)

    5.4.1. 设置和获取属性值以及嵌套属性 5.4.2. 内建的PropertyEditor实现 5.4.2.1. 注册用户自定义的PropertyEditor 6. 使用Spring进行面向切面编程(AOP) 6.1. 简介 6.1.1. AOP概念 6.1.2. Spring AOP的功能和目标 ...

    Opengl Reference Manual (Bluebook)

    ### OpenGL Reference Manual (Bluebook) 关键知识点解析 #### 标题解读 - **OpenGL Reference Manual**:指的是一本详尽介绍了OpenGL编程接口(Application Programming Interface, API)的参考手册。 - **...

    reformulation versus cutting planes for robust optimization.pdf

    BertsimasD,DunningI,LubinM.Reformulationsvs.cuttingplanesforrobust optimization: acomputationalandmachinelearningperspective.unpub- lished workingpaperavailableonlineatoptimization-online.org.Apr2014

    Microsoft SharePoint 2010 Developer Reference

    ### Microsoft SharePoint 2010 Developer Reference Key Points #### Introduction to Microsoft SharePoint 2010 **What is SharePoint?** Microsoft SharePoint 2010 is a web-based collaborative platform ...

    modus_versus

    另一方面,如果`modus_versus`是一个教学项目,它可能包含了关于CSS的教程、示例代码和练习,旨在帮助初学者理解CSS的工作原理和最佳实践。通过学习这个项目,开发者可以深入理解如何使用选择器、层叠规则、媒体查询...

    Oral dabigatran versus enoxaparin for thromboprophylaxisafter pr

    Oral dabigatran versus enoxaparin for thromboprophylaxisafter primary total hip

    使用HTML,CSS,JavaScript开发Android应用程序教程

    ##### Web Apps Versus Native Apps **Web Apps** - **定义**:Web应用程序是指可以在任何现代浏览器上运行的应用程序。 - **优点**: - 跨平台兼容性好,只需要编写一次代码即可在多种设备上运行。 - 更新方便...

    debs_kafka_versus_rabbitmq.zip

    标题中的"debs_kafka_versus_rabbitmq"表明这是一个关于比较Apache Kafka与RabbitMQ的讨论。这两个都是流行的消息中间件,主要用于在分布式系统中处理和传递消息。在这个压缩包中,我们有一个名为"debs_kafka_versus...

Global site tag (gtag.js) - Google Analytics