对象(object)是最基本、最通用的类型,具有复合性结构,属于引用型数据,对象的结构具有弹性,内部的数据是无序的,每个成员被称为属性。在JavaScript中,对象是一个泛化的概念,任何值都可以转换为对象,所有对象都继承于Object类型,拥有很多原型属性。
在JavaScript中定义对象有3种方法,下面进行详细介绍。
使用new运算符调用构造函数,可以构造对象。具体语法格式如下:
var objectName = new functionName(args);
简单说明如下:
【示例】使用new运算符调用不同的类型函数,构造不同的对象:
var o = new Object(); //定义一个空对象
var a = new Array(); //定义一个空数组
var f = new Function(); //定义一个空函数
使用直接量可以快速定义对象,具体语法格式如下:
var objectName = {
属性名1 : 属性值1,
属性名2 : 属性值2,
……
属性名n : 属性值n
};
在对象直接量中,属性名与属性值之间通过冒号进行分隔,属性值可以是任意类型的数据,属性名可以是JavaScript标识符,或者是字符串型表达式。属性与属性之间通过逗号进行分隔,最后一个属性末尾不需要逗号。
【示例1】使用对象直接量定义1个对象:
var o = { //对象直接量
a : 1, //定义属性
b : true //定义属性
}
【示例2】如果不包含任何属性,则可以定义一个空对象:
var o = { } //定义一个空对象直接量
提示:ECMAScript 6新增Map类型的数据结构,本质与Object结构类似。两者的区别:Object强制规定key只能是字符串;而Map结构的key可以是任意对象。
Object.create是ECMAScript 5新增的一个静态方法,用来定义对象。该方法可以指定对象的原型和对象特性。具体语法格式如下:
Object.create(prototype, descriptors)
参数说明如下:
prototype:必须参数,指定原型对象,可以为null。
descriptors:可选参数,包含一个或多个属性描述符的JavaScript对象。属性描述符包含数据特性和访问器特性,其中数据特性说明如下:
访问器特性包含两种方法,简单说明如下:
【示例】使用Object.create定义一个对象,继承null,包含两个可枚举的属性size和shape,属性值分别为“large”和“round”:
var newObj = Object.create(null, {
size: { //属性名
value: "large", //属性值
enumerable: true //可以枚举
},
shape: { //属性名
value: "round", //属性值
enumerable: true //可以枚举
}
});
console.log(newObj.size ); //large
console.log(newObj.shape ); //round
console.log(Object.getPrototypeOf(newObj)); //null
属性也称为名/值对,包括属性名和属性值。属性名可以是包含空字符串在内的任意字符串,一个对象中不能存在两个同名的属性。属性值可以是任意类型的数据。
在对象直接量中,属性名与属性值之间通过冒号分隔,冒号左侧是属性名,右侧是属性值,名/值对之间通过逗号分隔。
在对象直接量中,属性名与属性值之间通过冒号分隔,冒号左侧是属性名,右侧是属性值,名/值对之间通过逗号分隔:
var obj = { //定义对象
x:1, //属性
y:function(){ //方法
return this.x + this.x;
}
}
【示例2】通过点语法,可以在构造函数内或者对象外添加属性:
var obj = {} //定义空对象
obj.x = 1; //定义属性
obj.y = function(){ //定义方法
return this.x + this.x;
}
使用Object.defineProperty()函数可以为对象添加属性,或者修改现有属性。如果指定的属性名在对象中不存在,则执行添加操作;如果在对象中存在同名属性,则执行修改操作。具体语法格式如下:
Object.defineProperty(object, propertyname, descriptor)
参数说明:
Object.defineProperty返回值为已修改的对象。
【示例3】先定义一个对象直接量obj,然后使用Object.defineProperty()函数为obj对象定义属性:属性名为x、值为1、可写、可枚举、可修改的特性:
var obj = {};
Object.defineProperty(obj, "x", {
value: 1, //属性值
writable: true, //属性可读可写
enumerable: true, //属性可枚举
configurable: true //属性可修改
});
console.log(obj.x ); //1
使用Object.defineProperties()函数可以一次定义多个属性,具体语法格式如下:
object.defineProperties(object, descriptors)
参数说明:
【示例4】使用Object.defineProperties()函数将数据属性和访问器属性添加到对象obj上:
var obj = {};
Object.defineProperties(obj, {
x: { //定义属性x
value: 1,
writable: true, //可写
},
y: { //定义属性y
set: function (x) { //设置访问器属性
this.x = x; //改写obj对象的x属性的值
},
get: function () { //设置访问器属性
return this.x; //获取obj对象的x属性的值
},
}
});
obj.y = 10;
console.log ( obj.x ); //10
使用点语法可以快速地读写对象属性,点语法左侧是引用对象的变量,右侧是属性名。
【示例1】定义对象obj,包含属性x,然后使用点语法读取属性x的值:
var obj = { //定义对象
x:1,
}
console.log(obj.x); //访问对象属性x,返回1
obj.x = 2; //重写属性值
console.log(obj.x); //访问对象属性x,返回2
可以使用中括号读写对象属性。
【示例2】针对上面示例,使用中括号语法读写对象obj的属性x的值:
console.log(obj["x"]); //2
obj["x"] = 3; //重写属性值
console.log(obj["x"]); //3
注意:在中括号语法中,必须以字符串形式指定属性名,而不能够使用标识符。中括号内可以使用字符串,也可以是字符型表达式,即只要表达式的值为字符串即可。
【示例3】使用for/in遍历对象的可枚举属性,并读取它们的值,然后重写属性值:
for(var i in obj){ //遍历对象
console.log(obj[i]); //读取对象的属性值
obj[i] = obj[i] + obj[i]; //重写属性值
console.log(obj[i]); //读取修改后属性值
}
在上面代码中,中括号中的表达式i是一个变量,其返回值为for…in遍历对象时,枚举的每个属性名。
使用Object.getOwnPropertyNames()函数能够返回指定对象私有属性的名称。私有属性是指用户在本地定义的属性,而不是继承的原型属性。具体语法格式如下:
Object.getOwnPropertyNames(object)
参数object表示一个对象,返回值为一个数组,包含所有私有属性的名称,其中包括可枚举的和不可枚举的属性和方法的名称。如果仅返回可枚举的属性和方法的名称,应该使用Object.keys()函数。
【示例4】定义一个对象,该对象包含3个属性,然后使用getOwnPropertyNames获取该对象的私有属性名称:
var obj = { x:1, y:2, z:3 }
var arr = Object.getOwnPropertyNames( obj );
console.log (arr); //返回属性名:x,y,z
使用Object.keys()函数仅能获取可枚举的私有属性名称,具体语法格式如下:
Object.keys(object)
参数object表示指定对象,可以是JavaScript对象或DOM对象。返回值是一个数组,其中包含对象的可枚举属性名称。
使用Object.getOwnPropertyDescriptor()函数能够获取对象的属性描述符,具体语法格式如下:
Object.getOwnPropertyDescriptor(object, propertyname)
参数object表示指定对象,propertyname表示属性的名称。返回值为属性的描述符对象。
【示例5】定义一个对象obj,包含3个属性,然后使用Object.getOwnPropertyDescriptor()函数获取属性x的数据属性描述符,并使用该描述符将属性x设置为只读。最后,再调用Object.defineProperty()函数,使用数据属性描述符修改属性x的特性。遍历修改后的对象,可以发现只读特性writable为false:
var obj = { x:1, y:2, z:3 } //定义对象
var des = Object.getOwnPropertyDescriptor(obj, "x"); //获取属性x的数据属性描述符
for (var prop in des) { //遍历属性描述符对象
console.log(prop + ': ' + des[prop]); //显示特性值
}
des.writable = false; //重写特性,不允许修改属性
des.value = 100; //重写属性值
Object.defineProperty(obj, "x", des); //使用修改后的数据属性描述符覆盖属性x
var des = Object.getOwnPropertyDescriptor(obj, "x"); //重新获取属性x的数据属性描述符
for (var prop in des) { //遍历属性描述符对象
console.log(prop + ': ' + des[prop]); //显示特性值
}
注意:一旦为未命名的属性赋值后,对象会自动定义该名称的属性,在任何时候和位置为该属性赋值,都不需要定义属性,而只会重新设置它的值。如果读取未定义的属性,则返回值都是undefined。
使用delete可以删除对象的属性。
【示例】使用delete删除指定属性:
var obj = { x: 1} //定义对象
delete obj.x; //删除对象的属性x
console.log(obj.x); //返回undefined
提示:当删除对象属性之后,不是将该属性值设置为undefined,而是从对象中彻底清除属性。如果使用for…in语句枚举对象属性,只能枚举属性值为undefined的属性,但不会枚举已删除属性。
属性描述符是ECMAScript 5新增的一个内部对象,用来描述对象属性的特性。
属性描述符包含6个特性,简单说明如下:
【示例1】使用value读写属性的值:
var obj = {}; //定义空对象
Object.defineProperty(obj, 'x', { value: 100 }); //添加属性x,值为100
console.log( Object.getOwnPropertyDescriptor(obj, 'x').value ); //返回100
【示例2】使用writable特性禁止修改属性x:
var obj = {}; //定义对象直接量
Object.defineProperty(obj, 'x', { //添加属性
value: 1, //设置属性默认值为1
writable: false //禁止修改属性值
});
obj.x = 2; //修改属性x的值
console.log( obj.x ) //返回值为1,说明修改失败
在正常模式下,如果writable为false,重写属性不会报错,但是操作会失败;而在严格模式下,会抛出异常。
可以使用点语法、中括号语法访问属性的值,也可以使用访问器访问属性的值。
访问器包括set和get两个方法,其中set可以设置属性值,get可以读取属性值。使用访问器的好处:为属性访问绑定高级功能,如设计访问条件、数据再处理、与内部数据进行互动等。
【示例1】设计对象obj的x属性值必须是数字,这里使用访问器对用户的访问操作进行监控。当使用obj.x取值时,就会调用get方法;赋值时,就会调用set方法:
注意:取值方法get()不能接收参数,存值方法set()只能接收一个参数,用于设置属性的值。
【示例2】JavaScript支持一种简写方法。针对示例1,通过如下方式可以快速定义属性:
var obj ={ //定义对象直接量
_x : 1, //定义_x私有属性
get x() { return this._x }, //定义x属性的get方法
set x( value ) { //定义x属性的set方法
if(typeof value != "number" ) throw new Error('请输入数字');
//如果输入的值不是数字,则抛出异常
this._x = value; //把用户输入的值保存到内部私有属性中
}
};
console.log(obj.x); //访问属性值,返回值为 1
obj.x = 2; //为属性x赋值,值为数字2
console.log(obj.x); //返回数字2
属性描述符是一个内部对象,不允许直接读写,可以通过下面几个函数进行操作:
JavaScript提供了3种方法,用来精确控制一个对象的读写状态,以防止对象被篡改:
同时提供3个对应的辅助检查函数,简单说明如下:
【示例】分别使用Object.preventExtensions、Object.seal和Object.freeze函数控制对象的状态,然后再使用Object.isExtensible、Object.isSealed和Object.isFrozen函数检测对象的状态:
var obj1 = {}; //定义对象直接量obj1
console.log( Object.isExtensible(obj1) ); //检测对象obj1是否可扩展,返回 true
Object.preventExtensions(obj1); //禁止对象扩展属性
console.log( Object.isExtensible(obj1) ); //检测对象obj1是否可扩展,返回 false
var obj2 = {}; //定义对象直接量obj2
console.log( Object.isSealed(obj2) ); //检测对象obj2是否已禁止配置,false
Object.seal(obj2); //禁止配置对象的属性
console.log( Object.isSealed(obj2) ); //检测对象obj2是否已禁止配置,true
var obj3 = {}; //定义对象直接量obj3
console.log( Object.isFrozen(obj3) ); //检测对象obj3是否已冻结,false
Object.freeze(obj3); //冻结对象的属性
console.log( Object.isFrozen(obj3) ); //检测对象obj3是否已冻结,true
在JavaScript中,Object是所有对象的基类,Object内置的原生方法包括两类:Object静态函数和Object原型方法。Object原型方法定义在Object.prototype对象上,也称为实例方法,所有的对象都自动拥有这些方法。
toString()方法能够返回一个对象的字符串表示,它返回的字符串比较灵活,可能是一个具体的值,也可能是一个对象的类型标识符。
【示例】显示实例对象、类型对象的toString()方法返回值是不同的:
function F(x,y){ //构造函数
this.x = x;
this.y = y;
}
var f = new F(1,2); //实例化对象
console.log(F.toString()); //返回函数的源代码
console.log(f.toString()); //返回字符串“[object Object]”
toString()方法返回信息简单,为了能够返回更多的有用信息,可以重写该方法。例如,针对实例对象返回的字符串都是“[object Object]”,可以重写该方法,让对象实例返回构造函数的源代码:
Object.prototype.toString = function(){
return this.constructor.toString();
}
提示:当把数据转换为字符串时,JavaScript一般都会调用toString()方法来实现。由于不同类型的对象在调用该方法时,所转换的字符串表示不同且有一定规律,所以开发人员常用它来判断对象的类型,弥补typeof运算符和constructor属性在检测对象数据类型方面的不足。
valueOf()方法能够返回对象的值。它的主要用途:JavaScript自动类型转换时会默认调用这个方法。Object默认valueOf()方法返回值与toString()方法返回值相同,但是部分类型对象重写了valueOf()方法。
【示例】Date对象的valueOf()方法返回值是当前日期对象的毫秒数:
var o = new Date(); //对象实例
console.log(o.toString()); //返回当前时间的UTC字符串
console.log(o.valueOf()); //返回距离1970年1月1日午夜之间的毫秒数
console.log(Object.prototype.valueOf.apply(o)); //默认返回当前时间的UTC字符串
对于String、Nutuber和Boolean类型的对象来说,由于都有明显的原始值,因此它们的valueOf()方法会返回合适的原始值。
根据继承关系不同,对象属性可以分为两类:私有属性(或称本地属性)和继承属性(或称原型属性)。使用hasOwnProperty()原型方法可以快速检测属性的类型,如果是私有属性,则返回true,否则返回false。
【示例1】在自定义类型中,this.name表示对象本地的私有属性,而原型对象中的name属性就是继承的属性:
function F(){ //自定义数据类型
this.name = "私有属性"; //本地属性
}
F.prototype.name = "继承属性"; //原型属性
【示例2】针对示例1,实例化对象,然后判定实例对象的属性name是什么类型:
var f = new F(); //实例化对象
console.log(f.hasOwnProperty("name")); //返回true,说明当前调用的name是私有属性
console.log(f.name); //返回字符串“私有属性”
注意:对于原型对象自身来说,这些原型属性是它们的私有属性,返回值是true。
使用propertyIsEnumerable()原型方法可以检测一个私有属性是否可以枚举,如果允许枚举,则返回true,否则返回false。
【示例】实例化对象o,使用for…in循环遍历它的所有属性,但是JavaScript允许枚举的属性只有a、b和c,而能够枚举的本地属性只有a和b:
function F(){ //构造函数
this.a =1; //本地属性a
this.b =2; //本地属性b
}
F.prototype.c =3; //原型属性c
F.d = 4; //类型对象的属性
var o = new F(); //实例化对象
for(var I in o){ //遍历对象的属性
console.log(I); //打印可枚举的属性
}
console.log(o.propertyIsEnumerable("a")); //返回值为true,说明可以枚举
console.log(o.propertyIsEnumerable("b")); //返回值为true,说明可以枚举
console.log(o.propertyIsEnumerable("c")); //返回值为false,说明不可以枚举
console.log(o.propertyIsEnumerable("d")); //返回值为false,说明不可以枚举
使用isPrototypeOf()方法可以检测当前对象是否为指定对象的原型。
var b = F.prototype.isPrototypeOf(o);
console.log(b); //返回true
Object静态函数是定义在Object类型对象上的本地方法,通过Object直接调用,既不需要实例化,也不需要继承。
Object()是一个类型函数,它可以将任意值转换为对象。如果参数为空,或者为undefined和null,将创建一个空对象。
【示例】如果参数为数组、对象、函数,则返回原对象,不进行转换。根据这个特性,可以设计一个类型检测函数,专门检测一个值是否为引用型对象:
function isObject(value) {
return value === Object(value);
}
console.log( isObject([])); //true
console.log( isObject(true) ); //false
Object()不仅可以包装对象,还可以当作构造函数使用。如果使用new调用Object()函数,将创建一个实例对象。
【示例】创建一个新的实例对象:
var obj = new Object();
Object类型对象包含很多静态函数,简单总结如下: