标签:
对象是Javascript的基本数据类型。对象是一种复合值:它将很多值集合在一起,可通过名字访问这些值。同时,对象也可以看做是属性的无序集合,每个属性都是一个名/值对。属性名是字符串,因此我们可以把对象看成是从字符串到值的映射。
一、创建对象
(1)使用对象直接量
基本语法:
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 Definitive Guide", "for" : "all audiences", // 如果属性名字里含有禁用字符(比如中间有空格,连字符、名字本身是关键字等),必须使用引号,也就是将它转化成字符串表示。 author:{ //对象的属性,也可以是一个对象。 firstname : "David", surname : "Flanagan", } }
(2)通过new创建对象
基本语法:
var p = new object(); //创建一个空对象,和{}一样 var a = new Array(); //创建一个空数组,和[]一样 var d = new Date(); //创建一个表示当前时间的Date对象 var r = new RegExp("js"); //创建一个可以进行模式匹配的EegExp对象
PS:除了这些内置构造函数,用自定义构造函数来初始化新对象也是很常见的。
(***)原型链
每一个Javascript对象(null除外)都和另一个对象相关联,“另一个”就是原型,每一个对象都从原型继承属性。
所有通过对象直接量创建的对象都具有同一个原型对象,并可以通过Javascript代码object.prototype获得对原型对象的引用。通过关键字new和构造函数调用创建的对象的原型就是构造函数的prototype属性的值。因此,同使用{}创建对象一样,通过new object()创建的对象也继承自object.prototype。同样,通过 new Array()创建的对象,原型就是Array.prototype,以此类推。
没有原型的对象为数不多,object.prototype就是其中之一。它不继承任何属性,其他原型对象都是普通对象,普通对象都具有原型。所有的内置构造函数(以及大部分自定义的构造函数)都具有一个继承自object.prototype的原型。例如,Date.prototype的属性继承自object.prototype,因此由new Date()创建的Date对象同时继承自Date.prototype和object.prototype。这一系列链接的原型对象就是“原型链”。
(3)object.create()
ECMAScript 5 定义了一个名为Object.create()的方法,它创建一个新对象,其中第一个参数就是这个对象的原型。object.create()提供第二个可选参数,用以对对象的属性进行进一步描述。
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是一个继承了object.prototype属性的空对象
它可以通过任意原型创建新对象(换句话说,****可以使任意对象可继承****),这是一个强大的特性!
// inherit() 返回了一个继承自原型对象p的属性的新对象 // 这里使用ECMAScript 5 中的object.create()函数(如果它存在的话) // 如果不存在Object.create(),则退化使用其他方法 function inherit(p){ if(p == null) throw TypeError(); //p是一个对象,但不能是null if(Object.create) //如果Object.create()存在 return Objevt.create(p); //直接使用它 var t = typeof p; //否则进行下一步检测 if(t !=="object" && t !=="function") throw TypeError(); function f() {}; //定义一个空构造函数 f.prototype = p; //将其原型属性设置为p return new f(); //使用f()创建p的继承对象 }
在明白了构造好熟的内容后,上述例子会更容易理解,现在只要知道它返回的新对象继承了参数对象的属性就可以了。需要注意的是,inherit()并不能完全代替object.create(),它不能通过传入null原型来创建对象,而且不能接收可选的第二个参数。
inherit()函数的其中一个用途就是防止库函数无意间(非恶意地)修改那些不受你控制的对象。不是将对象直接作为参数传入函数,而是将它的继承对象传入函数。当函数读取继承对象的属性时,实际上读取的是继承来的值。如果给函数对象的属性赋值,则这些属性只会影响这个继承对象自身,而不是原始对象。
var o = { x :"don‘t change this value"} library_function(inherit(o)); // 防止对o的意外修改
(4)属性的查询和设置
大家应该都知道,想要获取某个属性的值,只需通过"."或者"[]"运算符来获取,运算符左侧是一个表达式,它返回一个对象。对于点来说,右侧必须是一个以属性名称命名的简单标识符。对于方括号来说,方括号内必须是一个计算结果为字符串的表达式,这个字符串就是属性的名字。
比如:
var el = book.el; //变量el得到book的"el"属性 var el2 = book["main title"] //变量el2得到book的"main title"属性
和查询属性值的写法一样,通过点和方括号也可以创建属性或给属性赋值,但需要将它们放在赋值表达式的左侧:
book.chen = 6; //给book创建一个名为"chen"的属性,赋值为6 book["main title"] = "ECMAScript"; //给"main title"属性赋值
在ECMAScript 3中,点运算符后的标识符不能是保留字,比如,o.for或o.class是非法的,因为for是关键字,class是保留字。如果一个对象的属性名是保留字,则必须使用方括号的形式访问它们,比如o["for"]和o["class"]。然而ECMAScript 5对此放宽了限制,可以在点运算符后直接使用保留字。
当使用方括号时,我们说方括号内的表达式必须返回字符串,其实更严格的来说,表达式必须返回字符串或返回一个可以转换为字符串的值。
(5)作为关联数组的对象
上文提到,"var el = book.el"和"var el = book["el"]"的值是相同的。
第一种语法使用点运算符和一个标识符,这第二种语法使用方括号和一个字符串,看起来有点像数组,只是这个数组元素是通过字符串索引而不是数字索引。这种数组就是关联数组,也称作散列、映射或字典。Javascript的所有对象都是关联数组。
在C、C++和Java和一些强类型语言中,对象只能拥有固定数目的属性,并且这些属性名称必须提前定义好。由于Javascript是弱类型语言,因此不必遵循这条规定,在任何对象中程序都可以创建任意数量的属性。但当通过点运算符访问对象的属性时,属性名用一个标识符来表示。标识符必须直接出现在Javascript程序中,它们不是数据类型,因此程序无法修改它们。
反过来讲,当通过[]来访问对象的属性时,属性名通过字符串来表示。字符串是Javascript的数据类型,在程序运行时可以修改和创建它们。因此在Javasctript中使用下面这种代码:
var addr = ""; for(i = 0; i<4; i++){ addr += customer["address" + i] + ‘\n‘; }
这段代码读取customer对象的address0、address1、address2和address3属性,并将它们连接起来。
这个例子主要说明了使用数组写法和用字符串表达式来访问对象属性的灵活性。这段代码也可以通过点运算符来重写,但是很多场景只能使用数组写法来完成。假设你正在写一个程序,这个程序利用网络资源计算当前用户股票市场投资的金额。程序允许用户输入每只股票的名称和购股份额。该程序使用名为portfolio的对象来存储这些信息。每只股票在这个对象中都有对应的属性,属性名称就是股票名称,属性值就是购股数量,例如用户持有IBM的50股,那么portfolio.ibm属性的值就为50.
下面是程序的部分代码,这个函数用来给portifolio添加新的股票:
function addstock(portfolio,stockname,shares){ portfolio[stockname] = shares; }
由于用户是在程序运行时输入股票名称,因此在此之前无法得知这些股票的名称是什么。而由于在写程序的时候不知道属性名称,因此无法通过点运算符来访问对象portfolio的属性。但可以使用[]运算符,因为它使用字符串值(字符串值是动态的,可以在运行时更改)而不是标识符(标识符是静态的,必须写在死程序中)作为索引对属性进行访问。
当使用for/in循环遍历关联数组时,就可以清晰地体会到for/in的强大之处。下面是一个利用for/in计算portfolio的总计值:
function getvalue(portfolio){ var total = o.o; for (stock in portfolio){ //遍历portfolio中的每只股票 var shares = portfolio[stock]; //得到每只股票的份额 var price = getquote(stock); //查找股票价格 total += shares * price; //将结果累加至total中 } return total; //返回total的值 }
标签:
原文地址:http://my.oschina.net/u/2352178/blog/476341