Javascript学习日志 (四) 面向对象的程序设计
1 对象概念
1.1 属性类型
属性类型:数据属性和访问器属性;
1.1.1 数据属性:
- [[Configurable]]:能否delete,重定义;
- [[Enumerable]]:能否通过for-in枚举,循环返回属性;
- [[Writable]]:是否可写;
- [[Value]]:保存属性值,且可通过此改变属性值;
1.1.2 修改属性特性方法:
- Object.defineProperty():三个参数,属性所在对象,属性名,描述符对象(configurable, enumerable, writable, value,可一个或同时多个,格式{attr:value, …};
1.1.3 访问器属性:
- [[Configurable]]:delete可否;
- [[Enumerable]]:可枚举?;
- [[Get]]:读取属性时调用的函数,默认为undefined;
- [[Set]]:写入属性时调用的函数,默认为undefined;
1.1.4 定义访问器属性:
- Object.defineProperty();
1: Object.defineProperty(obj, "attr", {
2: // 访问器属性
3: get: function() {
4: },
5:
6: set: function() {
7: }
8: });
1.1.5 定义多个属性
1: var bk = {};
2:
3: Object.defineProperties(
4: name : {
5: value : book1
6: },
7:
8: edition : {
9: value : 1
10: },
11:
12: year : {
13: get: function(){},
14: set: function(){}
15: }
16: );
1.1.6 读取属性特性
- Object.getOwnPropertyDescriptor():var1属性所在对象,var2属性名,返回一个对象包含属性特性descriptor(数据属性,或访问器属性)
可通过descriptor.value/configurable/enumerable/writable/set/get来获取对应属性特性的值。
2 创建对象
创建对象模式:
2.1 工厂模式
采用函数封装,直接调用函数创建对象
function createObj() { var o = new Object();
// 添加属性
// 添加方法
return o;
}
缺点:无法解决对象识别问题(即如何知道一个对象的类型), value instanceOf object;
2.2 构造函数模式
function Obj(a, b, c) {
this.a = a;
this.b = b;
this.c = c;
this.f = function() {};
}
var o = Obj(1, 2, 3);
条用构造函数过程:
- 创建新对象
- 将构造函数作用域赋给新对象
- 执行构造函数中的代码
- 返回新对象
指定作用域来调用构造函数:
var oo = new Object();
Obj.call(oo, 2, 3, 4);
优点:解决了对象类型无法识别,可使用instanceOf, o instanceOf Obj;result: true。
缺点:方法不能共享,每创建一个对象,同样会创建一个函数副本,但是函数的处理与其他对象都一样,重复创建副本太累赘也浪费空间。
2.3 原型模式
每个函数都会有一个函数原型属性:prototype,它是一个指针,指向一个对象,此对象包含了可以由特定类型的所有实例共享的属性和方法。
function Obj(){
}
Obj.prototype.name = “a”;
Obj.prototype.f = function(){};
var o1 = new Obj();
var o2 = new Obj();
在这种情况下name, f都会被新创建的对象所共享,也就是说o1, o2共享name, 和f,如果o1改变了name值,也会在o2的对象上体现出来。
原型函数中有一个属性:constructor,是一个指针,指向该函数原型的构造函数。
- hasOwnProperty():o1.hasOwnProperty(“name”);判断一个属性是存在于原型中,还是实例中,如果在实例中返回true,否则false;
- in操作符:判断实例或原型中是否存在某属性,”name” in o1, 判断name是否在,存在返回true,否则false。
以上两个同时使用时可以确定某属性到底是在原型中还是实例中。
简单的原型语法:
Obj.prototype = {
constructor : Obj,
name : “a”,
f : function () {}
};
此种情况下constructor被重定向了,不再指向构造函数了,可通过在Obj.prototyoe中添加一项(如上黑体部分)。
缺点:1. 构造函数没有初始化参数传递,结果是所有实例默认情况下都是取了同一属性值;2. 所有属性与方法被共享(最大缺点)。
2.4 构造函数模式和原型模式组合使用
将属性放到构造函数中,方法置于原型中,解决原型模式的第二个缺点。
function Obj(a, b, c){
this.a = a;
this.b = b;
this.c = c;
this.arrs = [“Tom”, “Jim”];
}
Obj.prototype = {
constructor: Obj,
f: function () {}
};
var o1 = new Obj(1, 2, 3);
var o2 = new Obj(2, 3, 4);
此时创建o1, o2时,对arrs的操作不会互相影响,并且也达到了方法共享,是最佳的用来定义引用类型的一种默认模式。
2.5 动态原型模式
动态的给原型中添加方法或属性
function Obj() {
// 属性
// 动态给原型添加方法
if (typeof this.f != “function”) {
// 表示如果f方法不存在,则在原型中添加该方法,从而带到需则取目的
Obj.prototype.f = function(){};
}
}
2.6 寄生构造函数模式
工厂模式的进化??在构造函数里封装对象创建的过程,然后返回新创建的对象。
function Obj() {
// 构造函数里面创建对象
var o = new Object();
// 属性
// 方法
return o;
}
var o1 = new Obj(); //事实上在Obj里面使用new Object();完成
创建的对象o1与构造函数和原型函数属性之间没有关系,因此不能使用instanceOf来确定对象类型,不推荐使用。
2.7 稳妥构造函数模式
此种模式是在安全度需求较高的地方使用较多,此类对象的属性只能通过对象的方法来访问,外界无法访问。
3 继承
3.1 原型链
通过将一个对象的实例赋值给另一个对象的原型来实现。
function SuperObj () {
}
function SubObj () {
}
SubObj.prototype = new SuperObj();
缺点:1.引用类型数据被共享,SuperObj中的引用类型会被SubObj的实例所共享。
2.无法再不影响所有对象实例的情况下,给超类型的构造函数传递参数???
3.2 借用构造函数
在子类型的构造函数内部调用超类型的构造函数,从而使得子类型的构造函数所创建的每个实例都有了一个引用类型副本,而避免了引用类型被共享情况。
function SuperObj(name){
this.name = name;
this.arrs = [“Tom”, “Jim”];
}
function SubObj(){
SuperObj.call(o[, name]); // call指定执行环境,还可以传递参数,如上黑体部分
}
由此每个由SubOjb()创建的实例都拥有了自己的arrs引用类型的副本,避免了被共享的情况发生。
缺点:方法都在构造函数里,所以方法无法被共享而复用。
3.3 组合继承
通过在超类型的原型中定义方法来实现,如此子类中也将继承超类型原型中的方法,且共享这些方法。
function SuperObj(name){
this.name = name;
}
SuperObj.prototype.f = function() {};
function SubObj(name, age){
SuperObj.call(this, name);
this.age = age;
}
// 继承方法
SubObj.prototype = new SuperObj();
SubObj.prototype.constructor = SubObj;
// 这样SubObj创建的实例也都共享了SuperObj中的f方法了
优点:避免了原型链和借用构造函数的缺点,融合了他们的优点,最常用。
缺点:两次调用了超类型的构造函数。
3.4 原型式继承
function object(o) {
function F() {
}
F.prototype = o;
return new F();
}
ECMAScript5添加了Object.create()规范了原型式继承,两个参数,一个用作新对象原型的对象和一个可选的为新对象定义的额外属性的对象。
要求必须有一个对象可以作为另一个对象的基础。
3.5 寄生式继承
function createAnother(original) {
var clone = object(original);
clone.f = function() {};
return clone;
}
var obj = {
name : “a”,
arrs : [“Tom”, “Jim”]
};
var anotherObj = createAnother(obj);
3.6 寄生组合式继承
解决了组合式继承中超类型构造函数被两次调用的问题。
// 关键函数
function inheritPrototype(subObj, superObj) {
var ptototype = object(superObj.prototype); // 创建对象
prototype.constructor = subObj;
subObj.prototype = prototype;
}
function SuperObj(name){
this.name = name;
}
SuperObj.prototype.f = function() {};
function SubObj(name, age){
SuperObj.call(this, name);
this.age = age;
}
// 继承方法
inheritPrototype(SubObj, SuperObj);
SubObj.prototype.f = function() {};
总结:看到这里最纠结了,对象模式,继承模式,一个一个的脑子都搞糊涂了,不过经过仔细阅读一遍,再在这里记录了一遍,基本上对每一种模式都有了一定的理解了,呵呵。工厂模式 –> 构造函数模式 –> 原型模式 –> 构造与原型组合模式 –> 动态原型模式 –> 寄生构造函数模式 –> 稳妥构造函数模式,原型链式继承 –> 借用构造函数式继承 –> 组合继承 –> 原型式继承 –> 寄生式继承 –> 寄生组合式继承,……!!!!!!!!!!!!!!!!!!!!!!!!!!

浙公网安备 33010602011771号