安装了markdownpad之后,右边栏无法实时刷新,也无法滚动,原因是让我更新显卡驱动(-。-虽然很扯,但事实就是这样的【摊手】)解决办法,公司电脑配置太差,缺省的文件太多了,还是在这儿写吧。2015.9.8
第六章 面向对象的程序设计
面向对象都有类的概念,而ECMAScript没有类的概念。因此它的对象也和基于类的语言中的对象不一样。js中每个对象都是基于引用类型创建的,这个引用类型可以是第五章讨论的原生类型,也可以开发人员自己定义的。
1 属性类型
1.1 数据属性
1.1.1
数据属性的特性 | 说明 | 默认值 | 备注 |
---|---|---|---|
[[Configurable]] | 表示可否通过delete删除属性从而重新定义属性 | true | |
[[Enumerable]] | 表示可否通过for-in循环返回属性 | true | |
[[Writable]] | 表示可否修改属性的值 | true | |
[[Value]] | 包含这个属性的数据值 | undefined | 读取属性值的时候从这个位置读,写入的新值保存在这个位置 |
ECMAScript5有一个可以修改属性默认特性的方法:object.denfineProperty()
1.2 访问器属性
2 创建对象
虽然Object构造函数和对象字面量都可以用来创建单个对象。但这个方式有个明显的缺点:使用同一个借口创建很多对象,会产生大量的代码重复。
2.1 工厂模式
这种设计模式抽象了创建具体对象的过程。ECMAScript利用这个模式,发明了一种函数:该函数封装已特定接口创建对象的细节。
function creatPerson(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
}
return o;
}
var person1 = creatPerson("dan", 18, "student");
工厂模式虽然解决了创建多个相似对象的问题,但是却没有解决对象的识别问题(即知道一个对象的类型)。
2.2 构造函数模式
构造函数模式会创建特定类型的对象。创建自定义的构造函数,从而定义自定义对象类型的属性和方法。
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
};
}
var person2 = new Person("dan", 18, "student");
alert(person2.constructor == Person); //true
alert(person2 instanceof Object); //true
alert(person2 instanceof Person); //true
使用构造函数模式的主要问题是:每个方法都要在实例上重新创建一遍。
2.3 原型模式
使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。
只要创建了一个新函数,就会为该函数创建一个prototype属性,这个属性指向函数的原型对象。默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性。这个属性包含一个指向prototype属性所在函数的指针。
2.3.1 原型模式中常用的函数
原型模式中常用的函数名 | 用法 | 例子 | 例子返回值 | 备注 |
---|---|---|---|---|
isPrototype() | 确定对象之间是否存在原型关系 | Person.prototype. isprototypeOf(person1) | true | |
Object.getPrototypeOf() | 返回[[prototype]]的值 | Object.getPrototypeOf (person1)==Person.prototype | true | *1 |
hasOwnProperty() | 检测一个属性是存在于实例中,还是存在于原型中 | person1.hasOwnProperty (‘name’) | false | |
in | in操作符会在通过对象能够访问到给定属性时返回true | alert(‘name’ in person1) | true | |
hasPrototypeProperty() | 确定属性是否是原型中的属性 | hasPrototypeProperty (person1, ‘name’) | true | |
for-in | 返回的是所有能够通过对象访问的、可枚举属性,(既包括实例中的属性也包括原型中的属性) | *2例1 | ||
Object.keys() | 取得对象上所有可枚举的对象 | Object.keys(person1) | 例1 | |
Object. getOwnPropertyNames() | 取得所有实例属性,无论它是否可枚举 | 例1 |
以上的例子是基于构造函数模式的例子。
*1 , ECMAScript5。
*2 , 所有开发人员定义的属性都是可枚举的,只有IE8及更早的版本例外。
//例1,Object.keys();
var person1,
keys1,keys2,keys3,keys4,keys5,
str1 = [],str2 = [],str3 = [];
function Person(){
}
Person.prototype.name = 'dan';
Person.prototype.age = 18;
Person.prototype.job = 'student';
Person.prototype.sayName = function(){
alert(this.name);
};
person1 = new Person();
person1.name = "jun";
person1.age = 18;
keys1 = Object.keys(person1);
keys2 = Object.keys(Person);
keys3 = Object.keys(Person.prototype);
keys4 = Object.getOwnPropertyNames(person1);
keys5 = Object.getOwnPropertyNames(Person.prototype);
alert(keys1); //"name,age"
alert(keys2); //""
alert(keys3); //"name,age,job,sayName"
alert(keys4); //"name,age"
alert(keys5); //"constructor,name,age,job,sayName"
for (var prop in person1){
str1.push(prop);
}
for (var prop in Person){
str2.push(prop);
}
for (var prop in Person.prototype){
str3.push(prop);
}
alert(str1); //"name,age,job,sayName"
alert(str2); //""
alert(str3); //"name,age,job,sayName"
2.3.1 更简单的原型语法
前面的例子每添加一个属性和方法就要敲一遍Person.prototype。可以封装这个功能。
function Person(){
}
Person.prototype = {
//constructor : Person, 若加上这一条第三个输出就是true,但constructor属性同也变成可枚举类型。
name : "dan",
age : 18,
job : "student",
sayName : function () {
alert(this.name);
}
};
var friend = new Person();
alert(friend instanceof Person); //true
alert(friend instanceof Object); //true
alert(friend.constructor == Person); //false
alert(friend.constructor == Object); //true
注意:当我们使用这个方法封装propotype之后,本质上就完全重写了prototype对象,因此constructor属性也就变成新对象的constructor属性(指向Object构造函数),不再指向Person函数。
2.3 组合使用构造模式和原型模式
这是最常见的创建自定义类型的方式,也可以说是默认的方式创建自定义的引用类型。
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ['dan', 'jun'];
}
Person.prototype = {
constructor: Person,
sayName: function () {
alert(this.name);
}
};
var person1 = new Person('jing', 18, 'student');
var person2 = new Person('jenny', 18, 'teacher');
person1.friends.push('ru');
alert(person1.friends); //'dan,jun,ru'
alert(person2.friends); //'dan,jun'
alert(person1.sayName == person2.sayName); //true
2.3 动态原型模式
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
if(typeof this.sayName !== 'function'){
this.sayName = function(){
alert(this.name);
};
}
}
2.4 寄生构造函数模式
function Person(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}
3 继承
原型链作为实现继承的主要方式,但是实践中很少会单独使用这种方法。
3.1 原型链
构造函数,原型,实例之间的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向该构造函数的指针,而实例都包含一个指向原型对象的内部指针。
function SuperType(){
this.property = true;
this.name = 'dan';
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
SuperType.prototype.age = 18;
function SubType (){
this.subProperty = false;
}
SubType.prototype = new SuperType();
//原来存在于*Supertype实例*中的所有方法和属性,现在也存在于SubType.prototype中!
SubType.prototype.getSubValue = function(){
return this.subProperty;
};
var instance = new SubType();
alert(instance.getSuperValue()); //true
alert(SubType.prototype.isPrototypeOf(instance)); //true
alert(SuperType.prototype.isPrototypeOf(instance)); //true
alert(Object.getPrototypeOf(instance) === SubType.prototype); //true
alert(Object.getPrototypeOf(instance) === SuperType.prototype); //false
alert(instance.hasOwnProperty('name')); //false
alert(instance.hasOwnProperty('getSubValue')); //false
var str = [],str2 = [],
instance2 = new SuperType();
for (var prop in instance2){
str2.push(prop);
}
for(var prop in instance){
str.push(prop);
}
alert(str); //'property,name,subProperty,getSupValue,getSubValue,age'
alert(str2); //'property,name,getSupValue,age'
alert(Object.getOwnPropertyNames(instance)); //'subProperty'
alert(Object.getOwnPropertyNames(instance2)); //'name,property'
alert(Object.getOwnPropertyNames(SubType.prototype)); //'property,name,getSubValue'
alert(Object.getOwnPropertyNames(SuperType.prototype)); //'constructor,age,getSuperValue'
很少单独使用原型链实现继承的原因有两个:1,在通过原型实现继承时,原型实际上会变成另一个类型的实例,于是,原先的实例属性也顺理成章的变成现在的原型属性。带来的结果是这些属性会被现有原型的实例所共享(下例)。2,在创建子类型的实例时,不能向超类型的构造函数传递参数。
function superType(){
this.color = ['red','green','blue'];
}
function subType(){
}
subType.prototype = new superType();
var instance1 = new subType();
var instance2 = new subType();
instance1.color.push('black');
alert(instance1.color); //'red,green,blue,black'
alert(instance2.color); //'red,green,blue,black'
3.2 借用构造函数
这种技术的基本思想是:在子类型构造函数的内部调用超类型的构造函数;这种方式可以解决单独使用原型链实现继承的两个缺点。
function superType(){
this.color = ['red','green','blue'];
}
function subType(){
superType.call(this);
}
var instance1 = new subType();
var instance2 = new subType();
instance1.color.push('black');
alert(instance1.color); //'red,green,blue,black'
alert(instance2.color); //'red,green,blue'
3.2 组合继承
组合继承是指将原型链和借用构造函数的技术集合到一起,从而发挥两者之长。
function superType(name){
this.name = name;
this.color = ['red','green','blue'];
}
superType.prototype.sayName = function(){
alert(this.name);
}
function subType(name, age){
//继承属性
superType.call(this,name);
this.age = age;
}
//继承方法
subType.prototype = new superType();
subType.prototype.sayAge = function(){
alert(this.age);
}
var instance1 = new subType('dan',18);
instance1.color.push('black');
alert(instance1.color); //'red,green,blue,black'
instance1.sayAge(); //18
instance1.sayName(); //'dan'
var instance2 = new subType('jun',19);
alert(instance2.color); //'red,green,blue'
instance2.sayAge(); //19
instance2.sayName(); //'jun'
3.3 原型式继承
var person = {
name: 'dan',
friends: ['jenny','jun']
}
//same1 原型式继承要求必须有一个对象作为另一个对象的基础,此例中person作为object的基础
function object(o){
function F(){}
F.prototype = o;
return new F();
}
var anotherPerson = object(person);
anotherPerson.name = 'jin';
anotherPerson.friends.push('Rob');
var yetAnotherPerson = object(person);
yetAnotherPerson.name = 'Linda';
yetAnotherPerson.friends.push('Van');
alert(person.friends); //'jenny,jun,Rob,Van'
//same 2 ECMAScript5通过新增加Object.create()方法规范了原型式继承
var anotherPerson = Object.create(person);
anotherPerson.name = 'jin';
anotherPerson.friends.push('Rob');
var yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = 'Linda';
yetAnotherPerson.friends.push('Van');
alert(person.friends); //'jenny,jun,Rob,Van'
3.4 寄生式继承
3.5 寄生组合式继承
未完续待。2015.9.17