`

4.1 基本类型和引用类型的值【JavaScript高级程序设计第三版】

阅读更多

ECMAScript 变量可能包含两种不同数据类型的值:基本类型值和引用类型值。基本类型值指的是简单的数据段,而引用类型值指那些可能由多个值构成的对象。
在将一个值赋给变量时,解析器必须确定这个值是基本类型值还是引用类型值。第3 章讨论了5 种基本数据类型:Undefined、Null、Boolean、Number 和String。这5 种基本数据类型是按值访问的,因为可以操作保存在变量中的实际的值。
引用类型的值是保存在内存中的对象。与其他语言不同,JavaScript 不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。为此,引用类型的值是按引用访问的①。
在很多语言中,字符串以对象的形式来表示,因此被认为是引用类型的。ECMAScript 放弃了这一传统。

4.1.1 动态的属性

定义基本类型值和引用类型值的方式是类似的:创建一个变量并为该变量赋值。但是,当这个值保存到变量中以后,对不同类型值可以执行的操作则大相径庭。对于引用类型的值,我们可以为其添加属性和方法,也可以改变和删除其属性和方法。请看下面的例子:

① 这种说法不严密,当复制保存着对象的某个变量时,操作的是对象的引用。但在为对象添加属性时,操作的是实际的对象。
var person = new Object();
person.name = "Nicholas";
alert(person.name); //"Nicholas"

运行一下
以上代码创建了一个对象并将其保存在了变量person 中。然后,我们为该对象添加了一个名为name 的属性,并将字符串值"Nicholas"赋给了这个属性。紧接着,又通过alert()函数访问了这个新属性。如果对象不被销毁或者这个属性不被删除,则这个属性将一直存在。
但是,我们不能给基本类型的值添加属性,尽管这样做不会导致任何错误。比如:

var name = "Nicholas";
name.age = 27;
alert(name.age); //undefined

运行一下
在这个例子中,我们为字符串name 定义了一个名为age 的属性,并为该属性赋值27。但在下一行访问这个属性时,发现该属性不见了。这说明只能给引用类型值动态地添加属性,以便将来使用。

4.1.2 复制变量值

除了保存的方式不同之外,在从一个变量向另一个变量复制基本类型值和引用类型值时,也存在不同。如果从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上。来看一个例子:

var num1 = 5;
var num2 = num1;

在此,num1 中保存的值是5。当使用num1 的值来初始化num2 时,num2 中也保存了值5。但num2中的5 与num1 中的5 是完全独立的,该值只是num1 中5 的一个副本。此后,这两个变量可以参与任何操作而不会相互影响。图4-1 形象地展示了复制基本类型值的过程。


 

当从一个变量向另一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份放到为新变量分配的空间中。不同的是,这个值的副本实际上是一个指针,而这个指针指向存储在堆中的一个对象。复制操作结束后,两个变量实际上将引用同一个对象。因此,改变其中一个变量,就会影响另一个变量,如下面的例子所示:

var obj1 = new Object();
var obj2 = obj1;
obj1.name = "Nicholas";
alert(obj2.name); //"Nicholas"

首先,变量obj1 保存了一个对象的新实例。然后,这个值被复制到了obj2 中;换句话说,obj1和obj2 都指向同一个对象。这样,当为obj1 添加name 属性后,可以通过obj2 来访问这个属性,因为这两个变量引用的都是同一个对象。图4-2 展示了保存在变量对象中的变量和保存在堆中的对象之间的这种关系。


 

4.1.3 传递参数

ECMAScript 中所有函数的参数都是按值传递的。也就是说,把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。基本类型值的传递如同基本类型变量的复制一样,而引用类型值的传递,则如同引用类型变量的复制一样。有不少开发人员在这一点上可能会感到困惑,因为访问变量有按值和按引用两种方式,而参数只能按值传递。

在向参数传递基本类型的值时,被传递的值会被复制给一个局部变量(即命名参数,或者用ECMAScript 的概念来说,就是arguments 对象中的一个元素)。在向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反映在函数的外部。请看下面这个例子:

 

function addTen(num) {
    num += 10;
    return num;
}
var count = 20;
var result = addTen(count);
alert(count); //20,没有变化
alert(result); //30

 

运行一下
这里的函数addTen()有一个参数num,而参数实际上是函数的局部变量。在调用这个函数时,变量count 作为参数被传递给函数,这个变量的值是20。于是,数值20 被复制给参数num 以便在addTen()中使用。在函数内部,参数num 的值被加上了10,但这一变化不会影响函数外部的count 变量。参数num 与变量count 互不相识,它们仅仅是具有相同的值。假如num 是按引用传递的话,那么变量count的值也将变成30,从而反映函数内部的修改。当然,使用数值等基本类型值来说明按值传递参数比较简单,但如果使用对象,那问题就不怎么好理解了。再举一个例子:

function setName(obj) {
    obj.name = "Nicholas";
}
var person = new Object();
setName(person);
alert(person.name); //"Nicholas"

运行一下
以上代码中创建一个对象,并将其保存在了变量person 中。然后,这个变量被传递到setName()函数中之后就被复制给了obj。在这个函数内部,obj 和person 引用的是同一个对象。换句话说,即使这个变量是按值传递的,obj 也会按引用来访问同一个对象。于是,当在函数内部为obj 添加name属性后,函数外部的person 也将有所反映;因为person 指向的对象在堆内存中只有一个,而且是全局对象。有很多开发人员错误地认为:在局部作用域中修改的对象会在全局作用域中反映出来,就说明参数是按引用传递的。为了证明对象是按值传递的,我们再看一看下面这个经过修改的例子:

function setName(obj) {
    obj.name = "Nicholas";
    obj = new Object();
    obj.name = "Greg";
}
var person = new Object();
setName(person);
alert(person.name); //"Nicholas"

这个例子与前一个例子的唯一区别,就是在setName()函数中添加了两行代码:一行代码为obj重新定义了一个对象,另一行代码为该对象定义了一个带有不同值的name 属性。在把person 传递给setName()后,其name 属性被设置为"Nicholas"。然后,又将一个新对象赋给变量obj,同时将其name属性设置为"Greg"。如果person 是按引用传递的,那么person 就会自动被修改为指向其name 属性值为"Greg"的新对象。但是,当接下来再访问person.name 时,显示的值仍然是"Nicholas"。这说明即使在函数内部修改了参数的值,但原始的引用仍然保持未变。实际上,当在函数内部重写obj 时,这个变量引用的就是一个局部对象了。而这个局部对象会在函数执行完毕后立即被销毁。

可以把ECMAScript 函数的参数想象成局部变量。

4.1.4 检测类型

要检测一个变量是不是基本数据类型?第3 章介绍的typeof 操作符是最佳的工具。说得更具体一点,typeof 操作符是确定一个变量是字符串、数值、布尔值,还是undefined 的最佳工具。如果变量的值是一个对象或null,则typeof 操作符会像下面例子中所示的那样返回"object":

var s = "Nicholas";
var b = true;
var i = 22;
var u;
var n = null;
var o = new Object();
alert(typeof s); //string
alert(typeof i); //number
alert(typeof b); //boolean
alert(typeof u); //undefined
alert(typeof n); //object
alert(typeof o); //object

运行一下
虽然在检测基本数据类型时typeof 是非常得力的助手,但在检测引用类型的值时,这个操作符的用处不大。通常,我们并不是想知道某个值是对象,而是想知道它是什么类型的对象。为此,ECMAScript提供了instanceof 操作符,其语法如下所示:

result = variable instanceof constructor

如果变量是给定引用类型(根据它的原型链来识别;第6 章将介绍原型链)的实例,那么instanceof 操作符就会返回true。请看下面的例子:

alert(person instanceof Object); // 变量person 是Object 吗?
alert(colors instanceof Array); // 变量colors 是Array 吗?
alert(pattern instanceof RegExp); // 变量pattern 是RegExp 吗?

根据规定,所有引用类型的值都是Object 的实例。因此,在检测一个引用类型值和Object 构造函数时,instanceof 操作符始终会返回true。当然,如果使用instanceof 操作符检测基本类型的值,则该操作符始终会返回false,因为基本类型不是对象。

使用typeof 操作符检测函数时,该操作符会返回"function"。在Safari 5 及之前版本和Chrome 7 及之前版本中使用typeof 检测正则表达式时,由于规范的原因,这个操作符也返回"function"。ECMA-262 规定任何在内部实现[[Call]]方法的对象都应该在应用typeof 操作符时返回"function"。由于上述浏览器中的正则表达式也实现了这个方法,因此对正则表达式应用typeof 会返回"function"。在IE 和Firefox 中,对正则表达式应用typeof 会返回"object"。
  • 大小: 5 KB
  • 大小: 10.3 KB
1
1
分享到:
评论
发表评论

文章已被作者锁定,不允许评论。

相关推荐

    [JavaScript.DOM高级程序设计](加)桑贝斯.扫描版.part1.rar

    第三部分 部分高级脚本编程资源  第9章 通过库来提高生产力   9.1 选择合适的库   9.2 增强DOM操作能力   9.2.1 连缀语法   9.2.2 通过回调函数进行过滤   9.2.3 操纵DOM文档   9.3 处理事件 ...

    ASP.NET4高级程序设计第4版 带目录PDF 分卷压缩包 part1

    ASP.NET 4高级程序设计(第4版)》【原版书为:Pro ASP.NET 4 in C# 2010】是ASP.NET领域的鸿篇巨制,全面讲解了ASP.NET4的各种特性及其背后的工作原理,并给出了许多针对如何构建复杂、可扩展的网站从实践中得出的...

    [JavaScript.DOM高级程序设计](加)桑贝斯.扫描版.part3.rar

    第三部分 部分高级脚本编程资源  第9章 通过库来提高生产力   9.1 选择合适的库   9.2 增强DOM操作能力   9.2.1 连缀语法   9.2.2 通过回调函数进行过滤   9.2.3 操纵DOM文档   9.3 处理事件 ...

    [JavaScript.DOM高级程序设计](加)桑贝斯.扫描版.part2.rar

    第三部分 部分高级脚本编程资源  第9章 通过库来提高生产力   9.1 选择合适的库   9.2 增强DOM操作能力   9.2.1 连缀语法   9.2.2 通过回调函数进行过滤   9.2.3 操纵DOM文档   9.3 处理事件 ...

    ASP.NET4高级程序设计(第4版) 3/3

     《ASP.NET 4高级程序设计(第4版)》是ASP.NET领域的鸿篇巨制,全面讲解了ASP.NET4的各种特性及其背后的工作原理,并给出了许多针对如何构建复杂、可扩展的网站从实践中得出的建议。书中还深入讲述了其他ASP.NET图书...

    extjs 4.1 beta 预览版

    6. 扩展与插件支持:EXTJS 4.1可能会强化对第三方扩展和插件的支持,让开发者可以轻松集成其他开发者创建的组件,以丰富自己的应用。 在压缩包文件"ext-4.1-pr1"中,包含了EXTJS 4.1 Beta的源码、文档、示例和其他...

    编写可维护的JavaScript(中文)

    Zakas是顶级的Web技术专家,也是《JavaScript高级程序设计》一书的作者。他曾是Yahoo!的首席前端开发工程师,在完成了从一名“独行侠”到“团队精英”的蜕变后,他站在前端工程师的角度提炼出众多的最佳编程实践,...

    JavaScript王者归来part.1 总数2

     5.6 值类型和引用类型   5.6.1 什么是值和值的引用   5.6.2 使用值和使用引用   5.6.3 值与引用的相互转换:装箱和拆箱   5.7 类型识别与类型转换   5.7.1 运行时类型识别--两个运行的类型识别的例子  ...

    django4.1官网文档 中文版

    《Django 4.1 官方文档 中文版》是一个详尽的资源,旨在帮助开发者深入了解和掌握Django 4.1这一流行的Python web框架。这个文档包含了Django的核心概念、功能特性和最佳实践,是学习和进阶Django不可或缺的参考资料...

    企微魔盒创业版 v4.1.zip

    6. **vendor**:这个目录通常包含第三方库和依赖,这些库是企微魔盒创业版运行所必需的。开发者可能选择将它们包含在压缩包中,以便用户无需单独安装就能运行软件。 7. **qvmhweb**:这可能是企微魔盒的Web应用程序...

    qooxdoo4.1

    Qooxdoo 4.1版本对响应式设计提供了很好的支持,使得应用程序能够适应不同的设备和屏幕尺寸。通过媒体查询和布局策略,开发者可以轻松创建跨平台的应用。 6. **国际化与本地化**: Qooxdoo提供了完整的国际化支持...

    spket-1.6.4.1

    其核心是一个插件架构,允许第三方开发者为其添加新的功能。Spket就是这样一个插件,它充分利用Eclipse的架构,为JavaScript开发提供了丰富的功能。 **三、Spket与EXT框架的兼容性** EXT是一个流行的JavaScript库...

    JavaScript基础教程第8版

    1.9 值和变量 8 1.9.1 操作符 8 1.9.2 赋值和比较 9 1.9.3 比较 9 1.10 编写对JavaScript友好的HTML 10 1.10.1 结构、表现和行为 10 1.10.2 div和span 10 1.10.3 class和id 11 1.11 要使用什么工具...

    Android程序设计基础

    丛书名: 图灵程序设计丛书 出版社:人民邮电出版社 ISBN:9787115215369 上架时间:2009-11-6 出版日期:2009 年11月 开本:16开 页码:196 版次:1-1 编辑推荐  你的第一本Android书.  Pragmatic系列...

    Cscms_v4.1正式版_gbk.zip_Cscms v4 正式版_cscms play.html_cscmsv4 htacc

    【Cscms_v4.1正式版_gbk.zip】是一个包含Cscms v4.1在线视频播放程序源码的压缩文件,专为构建视频网站而设计。Cscms(可能是"Content Management System"的缩写)是一个内容管理系统,允许用户方便地管理和发布视频...

    115cms综合内容管理系统 v4.1.zip

    9. **extend**:扩展模块目录,可能包含了第三方插件或自定义功能模块,增强了系统的灵活性和可扩展性。 10. **thinkphp**:ThinkPHP是一个流行的PHP框架,这个目录表明115cms是基于ThinkPHP构建的,利用了其MVC...

Global site tag (gtag.js) - Google Analytics