第3章 对象(Object)
3.1 对象字面量(Object Literals)
3.2 检索(Retrieval)
3.3 更新(update)
3.4 引用(Reference)
3.5 原型(Prototype)
3.6 反射(Reflection)
3.7 枚举(Enumeration)
3.8 删除(Delete)
3.9 减少全局变量的污染(Global Abatement)
JS的简单类型包括:数字、字符串、布尔值、null值和undefined值。其他所有的值都是对象。数字、字符串和布尔值“貌似”对象,因为它们拥有方法,但它们是不可变的。
JS中的对象是可变的键控集合。在JS中,数组是对象,函数是对象,正则表达式是对象,当然,对象自然是对象。对象是属性的容器,其中每个属性都拥有名字和值。属性的名字可以是包括空字符串在内的任意字符串,属性值可以是除undefined值之外的任意值。
JS中对象是无类别的。对象适合用于收集和管理数据。对象可以包含其他对象,所以它们可以很容易地表示成树形或图形结构。JS包括一个原型链特性,允许对象继承另一个对象的属性。正确地使用它能减少对象初始化的时间和内存消耗。
3.1 对象字面量(Object Literals)
(1)对象字面量提供了一种非常方便地创建新对象值的表示法。一个对象字面量就是包围在一对花括号中零或多个“名/值”对。
var empty_object= {};
var stooge= {
"first-name":"Jerome",
"last-name":"Howard"
};
(2)属性名可以包括空字符串在内的任意字符串。在对象字面量中,如果属性名是一个合法的JS标识符且不是保留字,并不强制要求用""括住属性名。所以用引号括住"first-name"是必须的,但是否括住first_name则是可选的。逗号用来分隔多个“名/值”对。
属性的值可以从包括另一个对象字面量在内的任意表达式中获得。对象是可以嵌套的:
var flight={
airline:"Oceanic",
number:815,
departure: {
IATA:"SYD", //IATA:国际航空运输协会
time:"2004-09-22 14:55",
city:"Sydney"
},
arrival: {
IATA:"LAX",
time:"2004-09-23 10:42",
city:"Los Angeles"
}
};
3.2 检索(Retrieval)
(1)要检索对象中包含的值,可以采用[]后缀中括住一个字符串表达式的方式。如果字符串表达式是一个常数,而且是一个合法的JS标识符,那么也可以用.表示法来代替。优先考虑使用.表示法。
stooge["first-name"] //"Jerome"
flight.depature.IATA //"SYD"
(2)如果尝试去检索一个不存在的成员元素的值,将返回undefined。
stooge["middle-name"] //undefined
flight.status //undefined,status:状态,订座情况
stooge["FIRST-NAME"] //undefined
(3)||可以用来填充默认值
var middle= stooge["middle-name"]||"(none)";
var status= flight.status||"unknow";
(4)检索一个undefined值会导致TypeError错误,可以通过&&运算符来避免错误。
flight.equipment //undefined,equipment:设施,集装箱
flight.equipment.model //TypeError
flight.equipment&&flight.equipment.model //undefined
3.3 更新(update)
对象中的值可以通过赋值语句来更新。
(1)如果属性名存在对象中,那么这个属性的值将被替换。
stooge["first-name"]= "Joe";
(2)如果属性名不存在,那么该属性将被扩充到该对象中。
stooge["middle-name"]= "Lester";
stooge.nickname= "Curly";
flight.equipment={
model:"Boeing 747"
};
flight.status= "overdue"; //overdue:逾期
3.4 引用(Reference)
对象通过引用来传递,它们永远不会被拷贝。
var x= stooge;
x.nickname= 'Curly';
var nick= stooge.nickname; //因为x和stooge是指向同一个对象的引用,所以nick为'Curly'
var a= {},b= {},c= {}; //a、b和c每个都引用一个不同的空对象
var a= b= c= {}; //a、b和c都引用同一个空对象
3.5 原型(Prototype)
(1)每个对象都连接到一个原型对象,并且它可以从中继承属性。所有通过对象字面量创建的对象都连接到Object.prototype这个JS标准对象。
(2)当你创建一个新对象时,你可以选择某个对象作为它的原型。我们将给Object增加一个beget方法,这个方法创建一个使用原对象(传入的对象参数)作为其原型的新对象。
if(typeof Object.beget!== 'function'){
Object.beget= function(o){
var F= function(){};
F.prototype= o;
return new F();
};
}
var another_stooge= Object.beget(stooge); //创建以stooge对象为原型的新对象another_stooge
(3)原型连接在更新时是不起作用的(只更新自身对象,不会更新该对象的原型对象),当我们对某个对象做出改变时,不会触及到该对象的原型。
another_stooge['first-name']= 'Harry';
another_stooge.nickname= 'Moe';
alert(another_stooge['first-name']+stooge['first-name']+another_stooge.nickname+stooge.nickname); //'HarryJoeMoeCurly'
(4)原型连接只有在检索值的时候才被用到。如果我们尝试去获取对象的某个属性值,且该对象没有此属性名,那么JS会试着从原型对象中获取属性值。如果那个原型对象也没有该属性,那么再从它的原型中寻找,依此类推,直到该过程最后到达终点Object.prototype。如果想要的属性完全不存在于原型链中,那么结果就是undefined。这个过程成为委托。
原型关系是一种动态的关系。如果我们添加一个新的属性到原型中,该属性会立即对所有基于该原型创建的对象可见。
var stooge.profession= 'actor';
another_stooge.profession //'actor'
3.6 反射(Reflection)
(1)检查对象并确定对象有什么属性是很容易的事情,只要试着去检索该属性并验证取得的值。typeof操作符对确定属性的类型很有帮助。
typeof flight.number //'number'
typeof flight.status //'string'
typeof flight.arrival //'object'
typeof flight.manifest //undefined
(2)原型链中的任何属性也会产生一个值。
typeof flight.toString //'function'
typeof flight.constructor //'function'
(3)有两种方法处理这些不必要的属性。
第一个是让你的程序检查并剔除函数值。一般来说做反射的目标是数据,因此你应该意识到一些值可能会是函数。
另一种方法是使用hasOwnProperty方法,如果对象拥有独有的属性,它将返回true。hasOwnProperty方法将不会检查原型链。
flight.hasOwnProperty('number') //true
flight.hasOwnProperty('constructor') //false
3.7 枚举(Enumeration)
(1)for in(java中称for each)
for in 语句用来遍历一个对象中的所有属性名。该枚举将会列出所有的属性——包括函数和你可能不关心的原型中的属性,所以有必要过滤掉那些你不想要的值。最为常见的过滤器是hasOwnProperty方法,以及typeof操作符来排除函数。
var name;
for(name in another-stooge){
if(typeof another_stooge[name] !== 'function'){
document.writeln(name + ':' +another_stooge[name]);
}
}
(2)普通for
for in属性名出现的顺序是不确定的,因此要对任何可能出现的顺序有所准备。如果你想要确保属性以特定的顺序出现,最好的办法就是完全避免使用for in 语句,而是创建一个数组,在其总以正确的顺序包含属性名。
var i;
var properties=[
'first-name',
'middle-name',
'last-name',
'profession'
];
var length=properties.length;
for(i=0;i<length;i +=1 ){
document.writeln(properties[i] + ':' +another_stooge[properties[i]]);
}
3.8 删除(Delete)
delete运算符可以用来删除对象的属性。它将会移除对象中确定包含的属性,它不会触及原型链中的任何对象。
删除对象的属性可能会让来自原型链中的属性浮现出来。
another_stooge.nickname //'Moe'
delete another_stooge.nickname; //删除another_stooge.nickname属性,从而暴露出原型的nickname属性
another_stooge.nickname //'Curly'
3.9 减少全局变量的污染(Global Abatement)
JS可以很随意地定义那些可保存所有应用资源的全局变量。不幸的是,全局变量削弱了程序的灵活性
(怎么理解:后面红字部分),所以应该避免。
最小化使用全局变量的方法是在你的应用中值创建唯一一个全局变量:
var MYAPP= {};
改变了此时变成了你的应用的容器:
MYAPP.stooge= {
"first-name":"Joe",
"last-name":"Howard"
};
MYAPP.flight={
airline:"Oceanic",
number:815,
departure: {
IATA:"SYD",
time:"2004-09-22 14:55",
city:"Sydney"
},
arrival: {
IATA:"LAX",
time:"2004-09-23 10:42",
city:"LAS Angeles"
}
};
只有把多个全局变量都整理在一个命名空间下,你将显著降低与其他应用程序、组建或类库之间产生糟糕的相互影响的可能性(也就是所谓的高内聚低耦合,程序间的依赖关系越小,程序相对就越灵活)。你的程序也会变得更容易阅读,因为明显MYAPP.stooge指向的是顶层结构。