对象是Javascript的基本数据类型。对象是一种复合值。它将很多值(原始值 或者其他对象)聚合在一起。可通过名字访问这些值。对象也可以看做是属性的无序集合,每个属性都有一个名/值。属性名是字符串,因此我们可以把对象看成是从字符串到值的映射。这种基本数据结构还有很多叫法,有些我们已经非常熟悉,比如“散列”(hash)、“散列表”(hashtable)、“字典”(dictionary)、“关联数组”(assciativeArray)。然而对象不仅仅是字符串到值的映射,除了可以保持的自有的属性,javascript对象还可以从一个陈为原型的对象继承属性。对象的方法通常是继承的属性。这种“原型式继承”(PRotypal inheritance)是javascript的核心特征。
javascript对象是动态的--可以新增属性也可以删除属性。--但他们常来模拟静态对象以及静态类型语言中的“结构体”(struct)。有时他们也用做字符串的集合(忽略名/值对应中的值)。除了字符串、数字、true、false、null、undefined之外,javascript值都是对象。尽管字符串、数字、和布尔值不是对象。但他们的行为和不可变对象(3.6)非常相似。
3.7节已经讲到,对象是可变的,我们通过引用而非值来操作对象。如果变量x是指向一个对象的引用,那么指向代码var y =x;变量y也是指向同一个对象的引用。而非这个对象的副本,通过修改y的值也会对变量x造成影响。
对象最常见的用法是创建(create)、设置(set)、查找(query)、删除(delete)、检测(test)、和枚举(enumerate)它的属性。我们在开始几节讲述这些基础的操作。后续几节讲述高级的内容。其中一部分内容来自ECMAScrit5。
属性包含名字和值,属性名可以是包含空字符串在内的任意字符串,但对象中不能存在两个同名的属性。值可以是任意javascript值。或者在(ECMAScript5中)可以是一个getter或者setter函数的讲解。除了名字和值之外,每个属性还有一些与之相关的值,称为“属性特性”(property attribute)
在ECMAScript5之前,通过代码给对象创建的所有属性都是可写可枚举的的。在ECMAScript5之后则可以对这些特性加以配置。6.7讲述如何操作。
除了基本的属性之外,每个对象还有三个相关的对象特性(object attribute):
对象最常见的用法是创建(create)、设置(set)、查找
对象最常见的用法是创建(create)、设置(set)、查找(query)、删除(delete)、检测(test)、和枚举(enumerate)它的属性。我们在开始几节讲述这些基础的操作。后续几节讲述高级的内容。其中一部分内容来自ECMAScrit5。
1.iii和2.ii会有关原型和属性继承的讲述,6.8节会进一步讲述着三个特性。最后,我们用下面这些属于对三类javascript对象和两类属性做区分
1.创建对象
可以通过对象直接量、关键字new和(ECMAscript5中的)Object.create()函数来创建对象。
i.对象的直接量创建对象最简单的方式就在是javascript代码中使用对象的直接量。对象直接量是由若干名/值组成的映射表,名/值对象中间用用冒号分隔,名/值之间 用逗号分隔,整个映射表用花括号括起来。属性名可以是javascript标识符也路是字符串直接量(包括空字符串)。属性的值可以是任意类型的javascript表达式,表达式的值(可以是原始值),就是这个属性的值。
var empty ={};//一个空对象 var point ={x:0,y:0}; //两个属性 var point2 ={x:point.x,y:point.y+1}; var book ={ "main title":"javascript", //属性名字里有空格,必须用字符串表示 'sub-title':"the defintive guide", //属性里有连接字符,因此需要使用双引号 "for":"all adiences", //"for是保留字,因此需要双引号。 author:{ //这个属性的值是一个对象 firstName:"dabid", //这里属性的值也是一个对象 surname:"flangan" //这里的属性名都没有引号 } };
对象的直接量是一个表达式,这个表达式的每次运算都创建并初始化一个新的对象。每次计算对象直接量的时候,也都会计算它的每个属性的值。也就是说,如果在一个重复的调用函数中循环体内使用了对象的直接量。它将创建很多新对象。并且每次创建的对象的属性值也有可能不同。
字面量写法举例:
function displayInfo(args){ var output = "" ; if(typeof args.name == "string"){ output += "Name:" + args.name + "/n"; } if (typeof args.age == "number"){ output += "Age:" + args.age + "/n"; } console.log(output) } displayInfo({name:"w",age:10})
ii.通过new创建对象
通过new创建并初始化一个对象,关键字new后跟随一个函数调用。这里的函数称作构造函数(constructor),构造函数用以初始化一个新创建的对象。javascript语言核心中的原始类型都包含内置构造函数。例如:
var o =new Object();创建一个空对象,和{}一样 var a =new Array(); var d =new Date(); var r =new RegExp("js");
除了这些内置构造函数,用自定义构造函数来初始化新对象也是非常常见的。第9章将详细描述其中的细节
iii.原型
在讲述第三章对象创建技术之前,我们应该首先解释一下原型。每一个javascript对象(null除外)都和令一个对象相关联。“另一个”对象就是我们熟知的原型,每一个对象都从原型继承属性。
通过对象直接量创建的对象都有同一个原型函数,并可以通过javascript代码Object.prototype获得对原先对象的引用。通过关键字new和构造函数调用创建的对象的原型就是构造函数prototype属性的值。因此,同使用{}创建对象一样,通过new Object()创建的对象也继承Object.prototype.同样,通过new arrary创建的对象原型就是Arrary.prototype,通过new Array()对象创建的对象的原型就是Date.prototype.
没有原型的对象为数不多,object.prototype就是其中之一。它不继承任何属性。其它原型的对象都是普通对象,普通对象都具有原型。所有的内置构造函数(以及大部分自定义的构造函数)都具有 一个继承自Object.prototype的原型。
例如Date.prototype的属性继承自Object.protype,因此,new Date()创建的date对象的属性同时继承Date.prototype和Object.prototype。这一系列链接的原型对象就是所谓的“原型链”(prototype chain)。
6.2.ii会讲述继承的工作机制。6.8.i会讲解如何获取对象的原型。第9章将会更详细的讨论原型和构造函数,包括如何用过编写构造函数定于对象“类”,以及给构造函数的protype属性值可以让其“实例”直接使用上这个原型的属性和方法。
iiii.Object.create()
ECMAScript5定义了一个名为Object.create()的方法,它创建一个新的对象,其中第一个参数就是这个对象的原型。Object.create()提供第二个可选参数,用以对对象的属性进行进一步描述。(6.7会详细讲解第二个参数)
Object.create()是一个静态函数,而不是提供给某个对象的调用方法。使用它的方法很简单,只需传入所需的原型对象即可。
var o1 = Object.create({ x: 1, y: 2 }); //o1继承了属性x和y
可以通过传入参数null来创建一个没有原型的新对象,但通过这样方式创建的对象不会继承任何东西,甚至不包括基础方法。比如toString(),也就是说,它不能和“+”运算符一起工作。
var o2 = Object.create(null); //o2不继承任何属性和方法
如果要创建一个普通的空对象,(比如通过{}或new Object()创建的对象),需要传入Object.prototype:
var o3 =Object.create(Object.prototype); //o3和{}和new Object一样
可以通过任何原型创建新对象(换句话说,可以使任意对象可继承),这是一个强大的特性。在ECMACscript3中可以用类型下面的代码模拟属性继承。
//inherit()返回了一个继承自原型对象p属性的新对象 //这里是有ECMAScript5中的Object.create()函数(如果存在的话) //如果不存在Object.create,则使用其他方法 function inherit(p) { if (p == null) throw TypeError(); //p是一个对象,不能是null if (Object.create) //如果Object.create存在 return Object.create(p); //直接使用它 var t = typeof p; //否则进一步检测 if (t !== "object" && t !== "function") throw TypeError; function f() {}; //定义一个空构造函数 f.prototype = p; //将其原型属性设置p return new f(); //将f()创建p的继承对象 }
在看完第9章关于构造函数的内容后,上面的inherit()函数更容易理解。现在只要知道它返回的新对象继承了参数对象的属性就可以了。注意:inherit()并不能完全代替Object.create(),它不能通过传入null原型来创建对象,而且不能接收可选的第二个参数。不过我们任辉在本章和第9章示例代码中多次用到inherit()
inherit()函数的其中一个用途就是防止库函数无意间(非恶意地)修改那行不受你控制的对象。不是将对象直接作为参数传入函数,而是将它的继承对象传入函数。当函数读取继承对象的属性时,实际上读取的是继承来的值。如果给继承对象的属性赋值,则泽祥属性只会影响到这个继承对象自身。而不是原始对象:
var o = {x: "donot change this balue"}; library_function(inherit(o));//防止对o的意外修改
了解其工作原理,需要首先了解javascript中的属性的查询和设置机制。接下来会讲到。
2.属性的查询和设置我们可以通过点.和方括号[]运算符来获取属性的值。运算符的左侧应当是一个表达式,它返回一个对象。对于.来说,右侧必须是一个属性名称命名的简单标识符。对于方括号来说[],内必须是一个计算结果为字符串的表达式,这个字符串就是属性的名字:
var book ={ "main title":"javascript", //属性名字里有空格,必须用字符串表示 'sub-title':"the defintive guide", //属性里有连接字符,因此需要使用双引号 "for":"all adiences", //"for是保留字,因此需要双引号。 author:{ //这个属性的值是一个对象 firstName:"dabid", //这里属性的值也是一个对象 surname:"flangan" //这里的属性名都没有引号 } }; var oAuthor = book.author; //得到book的“author”属性 var oName = oAuthor.surname //得到author的“surname”的属性 var oTitle =book["main title"] //得到book的main title属性 book.edition = 6;// 给book添加一个名为edition的属性 book["main title"] ="ECMAscript" //给"main title"属性赋值
和查询属性值的写法一样,通过点和方括号也可以创建属性或给属性赋值,但需要将他们放在属性表达式的左侧
在ECMAScript3中 ,点运算符后的标识符不能是保留字,比如o.for或o.class是非法的,因为for是javascript关键字,class是保留字。如果一个对象的属性名是保留字,则必须使用方括号的形式访问他们。比如o["for"]或o["classs"].ECMAScript5对此放宽了限制。可以在点运算符后直接使用保留字。
当使用方方括号时,我们说方括号内的表达式必须返回字符串。其实更严格的讲,表达式必须返回字符串或返回一个科可转换的字符串的值、在第七章第一个 例子里使用看方括号而非数组。这样的情况很常见。
i.作为关联数组的对象上文提到,下面的两个表达式的值相同:
object.propertyobject["property"]
第一种语法使用点运算符和一个标记符,这和C和Java中访问一个结构体或对象的静态字段非常类似。第二种语法使用方括号和一个字符串。看起来更像数组,只是这个数组元素是通过字符串索引而不是数字索引。这种数组就是我们所说的关联数组(associative array),也称作散列,映射或字典。javascript对象都是关联数组,本节讨论其重要性。
在c c++和java一些强类型(strong typed)语言中,对象只能拥有固定数目的属性,并且这些属性的名称需要提前定义好。由于javascript是弱类型语言,因此不必遵守这条规定,在任何程序对象中都可以创建任意数量的属性。但当通过.运算符访问对象的属性时,属性名用一个标识符来表示。标识符必须出现在javascript程序中,它们不是数据类型,因此程序无法修
新闻热点
疑难解答