对象简介
定义: 无需属性的集合,属性可以包含基本值、对象或者函数。
通俗理解: 对象是一组没有特定顺序的值,好比一组名值对,值可以是数据或者函数。
6.1 理解对象
1、 创建对象
- 最简单方式(不实用):创建Object实例,再添加属性和方法
var person = vew Object();
person.name = "Jay Chou";
person.age = 29;
person.job = "Singer";
person.sayName = function(){
alert(this.name);
}
- 对象字面量方式(实用)
var person = {
name: "Jay Chou",
age: 29,
job: "Singer",
sayName: function(){
alert(this.name);
}
}; // 此处有分号
2、属性类型
只有内部才用的特性描述了属性的各种特征。特性是内部值,放在两对方括号中,例如[[Enumerable]]
。
ECMAScript有两种属性:数据属性和访问器属性。
1. 数据属性
数据属性包含一个数据值的位置,这个位置可以读取和写入。
1.1 数据属性有4个描述其行为的特性:
[[Configurable]]
:是否可配置:能否通过delete删除属性,能否修改属性的特性,或能否修改为访问器属性。[[Enumerable]]
:能否通过for-in
循环返回属性。[[writable]]
:能否修改属性的值。[[value]]
:包含这个属性的数据值。读取和写入在这个位置,默认为undefined
。
// 举例,直接在对象上定义的属性
var person = {
name: "Jay Chou"
};
例子中,只有[[value]]
被设置成Jay Chou
,其他特性都默认为true
。
1.2 修改属性默认的特性
使用Object.defineProperty()
方法,接收三个参数:属性所在的对象,属性的名字,一个描述符对象。
1)修改属性值
var person = {};
Object.defineProperty(person,"name",{ // 创建一个名为name的属性
writable: false, // 不可修改属性的值
value: "Nicholas" // 设置值
});
alert(person.name); // "Nicholas"
person.name = "Greg";
alert(person.name); // "Nicholas"
2)不可配置的属性
var person = {};
Object.defineProperty(person,"name",{
configurable: false, // 不能从对象中删除属性
value: "Nicholas"
});
alert(person.name); // "Nicholas"
delete person.name;
alert(person.name); // "Nicholas"
3)不可配置不能再变为可配置
可以多次调用Object.defineProperty()
修改同一个属性,但是把configurable
特性设置为false
之后就有限制了。
var person = {};
Object.defineProperty(person, "name", {
configurable: false,
value: "Nicholas"
});
// 抛出错误
Object.defineProperty(person, "name", {
configurable: true, // 不可配置--X--可配置
value: "Nicholas"
});
2. 访问器属性
访问器属性不包含数据值,包含一对getter
setter
函数。读取访问器属性时,调用getter
,返回有效的值。写入时调用setter
传入新值,处理数据。
2.1 访问器属性有4个特性
[[configurable]]
[[Enumerable]]
[[Get]]
:默认为undefined
。[[Set]]
:默认为undefined
。
2.2 定义访问器属性
不能直接定义,要使用Object.defineProperty()
定义
不一定要同时指定getter
setter
,只指定一个意味着另一个不能读/写
var book = { // 创建了一个book对象,定义了两个默认的属性
_year: 2004, // _year的下划线是一种常用的几号,用于表示只能通过对象方法访问的属性
edition: 1
};
Object.defineproperty(book, "year", { // 访问器属性year
get: funtion(){
return this._year;
},
set: funtion(newValue){
if(newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2004;
}
}
});
book.year = 2005;
alert(book.edition); // 2
3、定义多个属性
用Object.defineProperties()
,接收两个对象参数:1. 要添加和修改其属性的对象 2. 第二个对象的属性与第一个对象中要添加或修改的属性一一对应
var book = {};
Object.definePropertyies(book, {
_year: { // 数据属性
writable: true,
value:2004
},
edition: { // 数据属性
writable: true,
value: 1
},
year: { // 访问器属性
get: function(){
return this._year;
},
set: function(newValue){
if(newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2004;
}
}
}
});
4、读取属性的特性
使用Object.getOwnPropertyDescriptor()
方法,取得给定属性的描述符。接收两个参数:1. 属性所在的对象 2. 要读取其描述符的属性名称。返回值是一个对象。
var book = {};
Object.defineProperties(book, {
_year: {
value: 2004
},
edition: {
value: 1
},
year: {
get: function(){
return this._year;
},
set: function(newValue){
if(newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2004;
}
}
}
});
var descriptor = Object.getOwnPropertyDescriptor(book,"_year");
alert(descriptor.value); // 2004
alert(descriptor.configurable); // false
alert(typeof descriptor.get); // "undefined"
var descriptor = Object.getOwnPropertyDescriptor(book, "year");
alert(descriptor.value); // undefined
alert(descriptor.enumerable); //false
alert(typeof descriptor.get); // "function"
6.2 创建对象
创建单个对象可以用6.1中 Object构造函数或对象字面量方法,但是创建多个对象时重复使用一个接口会产生大量的重复代码。为了解决这个问题,使用工厂模式的一种变体。
1、 工厂模式
因为在ECMAScript中无法创建类,开发人员发明一种函数来封装以特定接口创建对象的细节。
function createPerson(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
console.log(this.name);
};
return o;
}
var person1 = createPerson("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");
console.log(person1.name);
console.log(person2);
输出:
Nicholas
{ name: 'Greg', age: 27, job: 'Doctor', sayName: [Function] }
工厂模式解决了创建多个相似对象的问题,没有解决对象识别的问题(即怎样知道一个对象的类型)。
2、 构造函数模式
创建自定义的构造函数
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
console.log(this.name);
};
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
console.log(person1.age);
console.log(person2);
输出:
29
Person { name: 'Greg', age: 27, job: 'Doctor', sayName: [Function] }
构造函数模式和工厂模式的不同之处:
- 没有显示的创建对象
- 直接将属性和方法赋给了
this
对象 - 没有
return
语句
构造函数特点:
- 开头大写字母
- 创建新实例要用
new
操作符
调用构造函数经历的步骤:
- 创建一个新对象
- 将构造函数的作用域赋给新对象(
this
指向了新对象) - 执行构造函数中的代码
- 返回新对象
检测类型
- 构造函数属性:
constructor
person1和person2分别保存着Person的一个不同的实例。这两个对象都有一个constructor(构造函数)属性,该属性指向Person。
console.log(person1.constructor === Person);
console.log(person2.constructor === Person);
输出:
true
true
instanceof
可以更准确的检测对象类型
这个例子中,创建的所有对象,既是Object
的实例,又是Person
的实例。因为所有对象均继承自Object
。
console.log(person1 instanceof Object); // true
console.log(person1 instanceof Person); // true
console.log(person2 instanceof Object); // true
console.log(person2 instanceof Person); // true
构造函数模式 vs 工厂模式
构造函数模式可以将实例标识为一种特定的类型。