理解对象:数据属性和访问器属性。
创建对象:工厂模式、构造函数模式、原型模式、组合使用构造函数和原型模式、动态原型模式、寄生构造函数模式、稳妥构造函数模式。
1. 对象的属性 property
2.1 数据属性
行为特性:
- configurable 能否删除
- enumerable 能不能通过for in
- writable 能否修改
- value 设定属性值
Object.defineProperty(person, "name", {
configurable: false,
value: "Nicholas"
});
2.2 访问器属性
- configurable 能否删除
- enumerable 能不能通过for in
- get
- set
访问器属性不包含数据值;它们包含一对儿 getter 和 setter函数(optional)
。
在读取访问器属性时,会调用 getter函数,这个函数负责返回有效的值;在写入访问器属性时,会调用setter 函数并传入新值,这个函数负责决定如何处理数据。
也就是说不需要显示的调用set和get函数,它是自动执行的。
- 只指定 getter 意味着属性是不能写
- 只指定 setter 函数的属性也不能读,否则在非严格模式下会返回 undefined,
var book = {
_year: 2004, //这个属性表示只能通过对象(object)访问
};
// 这里访问器属性是year,_year是数据属性
Object.defineProperty(book, "year", {
get: function(){
return this._year;
},
set: function(newYear){
this._year=newYear;
}
});
book.year = 2005;
console.info(book._year); //2005
查看属性的信息:
Object.getOwnPropertyDescriptor(book,"_year")
2. 创建对象的方法
2.1 使用Object方式创建
2.1.1 new
使用 new 操作符后跟 Object 构造函数
var p1 = new Object();
p1.name="p1";
2.1.2 对象字面量
var p2 = {
name:"p2"
};
虽然 Object 构造函数或对象字面量都可以用来创建单个对象,但这些方式有个明显的缺点: 使用同一个接口创建对象时会产生大量代码
2.2. 更有效的创建对象的方法
2.2.1. 工厂模式
用函数来封装以特定接口创建对象的细节
function createPerson(name, age, job){
var o = new Object();
o.name = name;
}
var person1 = createPerson("Nicholas");
2.2.2. 构造函数模式
ECMAScript 中的构造函数可用来创建特定类型的对象。像 Object 和 Array 这样
的原生构造函数,在运行时会自动出现在执行环境中。此外,也可以创建自定义的构造函数,从而定义自定义对象类型的属性和方法。
function Person(name) {
this.name = name;
};
var person1 = new Person("Nicholas");
var person2 = new Person("Nicholas");
// 这两个对象都有一个 constructor(构造函数)属性,该属性指向 Person
要创建 Person 的新实例,必须使用 new 操作符。以这种方式调用构造函数实际上会经历以下 4个步骤:
(1) 创建一个新对象;
(2) 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象);
(3) 执行构造函数中的代码(为这个新对象添加属性);
(4) 返回新对象。
console.info(person1.constructor == person2.constructor); //t
console.info(person1.constructor == Person); //t
console.info(person1 instanceof Person); //t
console.info(person1 instanceof Object); //t
构造函数的三个特点:
- 没有显式地创建对象;
- 直接将属性和方法赋给了 this 对象;
- 没有 return 语句。
只要用new调用的就是构造函数,不用new的就不是。
// 当作构造函数使用
var person = new Person("Nicholas", 29, "Software Engineer");
person.sayName(); //"Nicholas"
// 作为普通函数调用
Person("Greg", 27, "Doctor"); // 添加到 window
window.sayName(); //"Greg"
// 在另一个对象的作用域中调用
var o = new Object();
Person.call(o, "Kristen", 25, "Nurse");
o.sayName(); //"Kristen"
2.2.3. 原型模式
无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个 prototype属性,这个属性指向函数的原型对象。
在默认情况下,所有原型对象都会自动获得一个 constructor(构造函数)属性,这个属性包含一个指向 prototype 属性所在函数的指针。
function Person(name) {
this.name = name;
};
// 函数自带了prototype属性
// 这个属性指向 系统为这个函数自动创建的 “原型对象” Person Prototype
console.info(Person.prototype); //Person {}
// “原型对象”里面有一个属性 叫做constructor 这个属性指向 这个函数
console.info(Person.prototype.constructor); //[Function: Person]
当用构造函数创建一个新实例后,该实例的内部会包含一个指针,指向刚才的那个“原型对象”,这个指针叫[[prototype]], 使用__proto__
访问。
也就是说, 每个实例只是指向了“原型对象”,而不与它们的构造函产生直接的联系。
即 实例的指针 -> 原型对象 -> 原型对象的指针 -> 构造函数
function Person(){
}
Person.prototype.name = "Nicholas";
var p1=new Person();
console.info(Person.prototype); //Person { name: 'Nicholas' }
console.info(p1.__proto__); //Person { name: 'Nicholas' }
console.info(Person.prototype.constructor); //[Function: Person]
2.2.4. 组合使用构造函数模式和原型模式
- 构造函数模式用于定义实例属性
- 原型模式用于定义方法和共享的属性
每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存。
function Person(name) {
this.name = name;
this.friends = ["Shelby", "Court"];
}
// typeof(Person.prototype) object
Person.prototype = {
constructor: Person,
sayName: function () {
alert(this.name);
}
}
var person1 = new Person("Nicholas");
var person2 = new Person("Greg");
person1.friends.push("Van");
alert(person1.friends);//"Shelby,Count,Van"
alert(person2.friends);//"Shelby,Count"
alert(person1.friends === person2.friends);//false
alert(person1.sayName === person2.sayName);//true
2.2.5. 动态原型模式
这里只在 sayName()方法不存在的情况下,才会将它添加到原型中。
function Person(name) {
this.name = name;
this.friends = ["Shelby", "Court"];
if (typeof this.sayName != "function"){
Person.prototype.sayName = function(){
alert(this.name);
}
}
}
2.2.6. 寄生构造函数模式
除了使用构造函数的方法初始化对象以外,这个方法和工厂模式是一样的。
function Person(name) {
var o = new Object();
o.name = name;
o.sayName = function () {
alert(this.name);
};
return o;
}
var friend = new Person("Nicholas");
friend.sayName(); //"Nicholas"
2.2.7. 稳妥构造函数模式
- 不用 this
- 不用 new
function Person(name, age, job) {
//创建要返回的对象
var o = new Object();
o.sayName = function () {
alert(name);
};
//返回对象
return o;
}
var friend = Person("Nicholas", 29, "Software Engineer");
// name只能通过sayname来访问
friend.sayName(); //"Nicholas"
一些变量只能通过函数访问,确保安全性。