面向对象的程序设计
6.1 理解对象
Object.defineProperties与Object.defineProperty
1. 数据属性
- configurable
- enumerable
- writable
- value
2. 访问器属性
- get
- set
- configurable
- enumerable
"use strict";
var book = {
_year: 2004,//以下划线开头,用于表示只能通过对象方法访问的属性。_
edition: 1,
visTimes:0
};
Object.defineProperty(book, 'year', { //设置一个year访问器属性
get: function() {
this.visTimes++;
return this._year;
},
set: function(newValue) {
if (newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2004;
}
},
enumerable:false,
configurable:true
});
book.year = 2005;
Object.getOwnPropertyDescriptor(book,"year");
通过设置自定义访问器属性的get和set方法,可以做许多事情,比如说记录访问次数。
- Object.getOwnPropertyDescriptor(book,”year”) 读取属性的特性
6.2 创建对象
6.2.1 工厂模式
抽象了创建具体对象的过程。
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;
}
6.2.2 构造函数模式
构造与工厂的区别:
1. 没有显示的创建对象
2. 直接将属性和方法赋给了this对象
3. 没有return语句
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function () {
console.log(this.name);
}
}
var person = new Person("wilson",20,"student");
person.sayName(); //当构造函数使用
Person("wilson2",21,"student"); //当普通函数使用
window.sayName();
var o = new Object();
Person.apply(o,["wilson3",22,"student"]) //在另一个对象的作用域中调用
o.sayName();
构造函数的4个步骤:
1. 创建一个新对象
2. 将构造函数的作用域赋给新对象
3. 执行构造函数中的代码
4. 返回新对象
注:创建自定义函数的构造函数意味着将他的实例标识为一种特定的类型,这也是构造模式胜过工厂模式的地方。
构造函数的问题:每个方法都要在每个实例上重新创建一遍。
6.2.3 原型模式
我们创建的每一个函数都有一个prototype
属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。
原型的问题:
第一:如果原型中有属性是引用类型的话,那么所有继承该原型的实例将共享着一个属性的引用类型。
第二:创建子类型的实例时,无法向超类型的构造函数中传递参数。
6.2.4 组合使用构造函数模式和原型模式
构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性。结果,每个实例都会有自己的一份实例属性的副本,
但同时共享着对方法的引用,最大限度的节省了内存。另外,还支持向构造函数传递参数。
6.2.5 动态原型模式
通过检测某个应该存在的方法是否有效来决定是否需要初始化原型。
6.3 继承
6.3.1 原型链
方法:将原型对象指向另一个类型的实例
6.3.2 借用构造函数
function SuperType(){
this.property = true;
}
function SubType(){
SuperType.call(this,"wilson"); //继承了SuperType,同时还传递了参数
this.age = 20;
}
var instance = new SubType();
6.3.3 组合继承
使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。
组合继承避免了原型链和借用构造函数的缺陷,融合了两者的优点。
6.3.4 原型式继承
基于原型可以基于已有对象来创建。
function object(o){
function F(){}
F.prototype = o;
return new F();
}
等价于ECMA5新增的Object.create()
6.3.5 寄生式继承
与原型式继承紧密相关,用于封装继承过程。
function createAnother(original){
"use strict";
var clone = Object.create(original);
clone.sayHi = function () {
alert("hi");
};
return clone;
}
6.3.6 寄生组合式继承
因为组合继承在任何情况下都会调用两次构造函数:一次是在创建子类型原型的时候,第二次是在子类型构造函数内部。
第一次创建子类型原型时,即会继承构造函数的属性,不过是存储在原型对象上,第二次时在对象创建并覆盖掉存储在原型对象上的属性。