js 对象&设计模式
对象
- 概念
无序属性的集合,其属性可以包含基本值、对象或者函数。”严格来讲,这就相当于说对象是一组没有特定顺序的值。对象的每个属性或方法都有一个名字,而每个名字都映射 到一个值。正因为这样,我们可以把对象想象成散列表: 无非就是一组名值对,其中值可以是数据或函数。
- 对象的属性
数据属性:
-
[[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。默认值为 true。
-
[[Enumerable]]:表示能否通过 for-in 循环返回属性。默认值为 true。
-
[[Writable]]:表示能否修改属性的值。默认值为 true
-
[[Value]]:包含这个属性的数据值。读取属性值的时候,从这个位置读; 写入属性值的时候,把新值保存在这个位置。这个特性的默认值为 undefined。
修改属性默认的特性: Object.defineProperty()方法
这个方法 接收三个参数:属性所在的对象、属性的名字和一个描述符对象。其中,描述符(descriptor)对象的属 性必须是:configurable、enumerable、writable 和 value。设置其中的一或多个值,可以修改 对应的特性值。
var person = {};
Object.defineProperty(person, "name", {
writable: false,
value: "Nicholas"
});
alert(person.name); //"Nicholas"
person.name = "Greg";
alert(person.name); //"Nicholas"
访问器属性:
在读取访问器属性时,会调用 getter 函数,这个函数负责返回有效的值;在写入访问器属性时,会调用 setter 函数并传入新值,这个函数负责决定如何处理数据。访问器属性有如下 4 个特性
-
[[Configurable]]
-
[[Enumerable]]
-
[[Get]]:在读取属性时调用的函数。默认值为 undefined。
-
[[Set]]:在写入属性时调用的函数。默认值为 undefined。
访问器属性不能直接定义,必须通过 Object.defineProperty() 方法来定义
var book = {
_year: 2004,
edition: 1
};
Object.defineProperty(book, "year", {
get: function(){
return this._year;
},
set: function(newValue){
if (newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2004;
} }
});
book.year = 2005; alert(book.edition); //2
不一定非要同时指定 getter 和 setter。只指定 getter 意味着属性是不能写,尝试写入属性会被忽略。 在严格模式下,尝试写入只指定了 getter 函数的属性会抛出错误。类似地,只指定 setter 函数的属性也 不能读,否则在非严格模式下会返回 undefined,而在严格模式下会抛出错误。
Object.defineProperties()方法:
利用这个方法可以通过描述符一次定义多个属性。这个方法接收两个对象参数:第一 个对象是要添加和修改其属性的对象,第二个对象的属性与第一个对象中要添加或修改的属性一一对应。例如:
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;
}
} }
});
Object.getOwnPropertyDescriptor()方法:
可以取得给定属性的描述 符。这个方法接收两个参数:属性所在的对象和要读取其描述符的属性名称。返回值是一个对象,如果 是访问器属性,这个对象的属性有 configurable、enumerable、get 和 set;如果是数据属性,这 个对象的属性有 configurable、enumerable、writable 和 value。
设计模式
1. Object 构造函数/对象字面量,缺点:使用同一个接口创建多个对象,会产生大量重复的代码。
2. 工厂模式
function createPerson(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}
这种模式抽象了创建具体对象的过程,用函数来封装以特定接口来创建对象的细节。解决了创建多个相似对象的问题,却没有解决对象的识别问题,即怎样知道一个对象的类型。
2. 构造函数模式
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
};
}
1) 没有显式地创建对象;
2) 直接将属性和方法赋给了 this 对象;
3) 没有 return 语句。
要创建 Person 的新实例,必须使用 new 操作符。以这种方式调用构造函数实际上会经历以下 4 个步骤:
(1) 创建一个新对象;
(2) 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象);
(3) 执行构造函数中的代码(为这个新对象添加属性);
(4) 返回新对象。
可以将构造函数的名称当作一种特定的类型,这是比工厂模式好的地方。
缺点:每个方法都要在每个实例上重新创建一遍
改进:
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
}
function sayName(){
alert(this.name);
}
3. 原型模式
function Person(){}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
};
好处:可以让所有对象实例共享它所包含的属性和方法,不必在构造函数中定义对象实例的信息。
缺点:
1) 所有实例在默认情况下都会取得相同的默认值。
2) 如果原型中包含引用型数据,当实例修改时,所有实例都会发生变化。
4. 组合使用构造函数模式和原型模式
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ["Shelby", "Court"];
}
Person.prototype = {
constructor : Person,
sayName : function(){
alert(this.name);
}
}
构造函数用于定义实例属性,原型模式用于定义方法和共享属性。
5. 动态原型模式
function Person(name, age, job){
//属性
this.name = name;
this.age = age;
this.job = job;
//方法
if (typeof this.sayName != "function"){
Person.prototype.sayName = function(){
alert(this.name);
};
}
}