面向对象的程序设计
1、创建对象的两种方法
(1 创界一个object的实例 然后再为它添加方法和属性
var person = new object();
person.name = 'nico';
person.age = 29;
person.job = 'software engineer';
person.sayName = function () {
alert(this.name);
}
(2 对象字面量成为创建这种对象的首选模式
var person = {
name: 'Nicolas',
age: 29,
job: 'Software engineer',
sayName: function () {
alert(this.name);
}
}
2、数据属性
1)Configurable 表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。
2) Enumerable 表示能否通过for-in循环返回属性。
3)Writable 表示能否修改属性的值。
4)Value包含这个属性的数据值
一旦设置了configurable为不能配置的(false),就不能再把它变回可配置的了
3、创建对象
1) 工厂模式
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;
}
var person1 = createPerson('Nicolas', 29, 'SoftWare Engineer');
var person2 = createPerson('Ni', 29, 'SoftWare Engineer');
- 构造函数方式
function Person (name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function () {
alert(this.name);
};
}
var person1 = new Person('Nicolas', 29, 'SoftWare Engineer');
var person2 = new Person('Ni', 29, 'SoftWare Engineer');
构造函数可以当做函数
任何函数,只要通过new操作符来调用,那它就可以作为构造函数;而任何函数,如果不通过new操作符来调用,就和普通的函数没有什么两样。
var person = new Person('nicolas', 29, 'SoftWare Exx');
person.sayName();
Person('Greg', 29, 'Doctor');
window.sayName();
var o = new Object();
Person.call(o, 'kristen', 25, 'Nurse');
o.sayName();
构造函数的问题
使用构造函数主要的问题,就是每个方法都要在每个实例上重新创建一遍。比如person1和person2都有一个名为sayName()的方法,凡是这两个方法都不是同一个Function的实例。函数是对象,因此每定义一个函数,也就是实例化了一个对象。
function Person (name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = new Function() {
alert(this.name);
};
}
构造函数中定义函数最大的问题就是,每次用这个构造函数实例化对象的时候就要实例化这个Function实例,实际上是没有必要的,所以引用而来的就是原型的概念。
3)原型模式
每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。
prototype就是通过调用构造函数而创建的那个对象实例的原型对象,使用原型对象的好处是可以让所有对象实例共享它的所包含的属性和方法。
function Person () {}
Person.prototype.name = 'Nico';
Person.prototype.age = 29;
Person.prototype.job = 'SoftWare Engineer';
Person.prototype.sayName = function () {
alert(this.name);
}
var person1 = new Person();
person1.sayName();
var person1 = new Person();
person1.sayName();
alert(person1.sayName == person2.sayName); // true
理解原型对象
function Person () {}
Person.constructor // Function
Person.prototype.constructor // Person
var person1 = new Person();
person1.__proto__ // 指向原型对象
Person.prototype // 指向原型对象
isPrototypeOf() // Person.prototype.isPrototypeOf(person1) => true
Object.getPrototypeOf() // 返回实例的原型对象
Object.getPrototypeOf(person1) // Person.prototype
先在实例上进行搜索属性,然后如果实例上没有这个属性的话,就会去原型上搜索。
delete person1.name // 可以删除实例上的属性
使用hasOwnProperty()方法可以检测一个属性是否存在于实例中,还是存在于原型中。这个方法只在给定属性存在于对象实例中时,才会返回true。
person1.hasOwnProperty('name'); // false
person1.name = 'Greg';
person1.hasOwnProperty('name') // true
delete person1.name;
person1.hasOwnProperty('name') // false
使用in操作符,in操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例中还是原型中。
person1.hasOwnProperty('name'); // false
'name' in person1 // true (不管来自实例还是来自原型)
利用in 和 hasOwnProperty 可以确定该属性到底是存在于对象中,还是存在于原型中
function hasPrototypeProperty(object, name) {
return !object.hasOwnProperty(name) && (name in object); // 存在原型中
}
要取得对象上所有可枚举的实例属性,可以使用object.key() 方法,这个方法接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。
var keys = Object.key(Person.prototype);
alert(keys) // name age job sayName
var p1 = new Person();
p1.name = 'Rob';
p1.age = 31;
var p1keys = Object.key(p1);
alert(p1keys) // name age
如果要得到所有实例属性,无论是否可枚举,都可以用Object.getOwnPropertyNames()方法。
var keys = Object.getOwnPropertyNames(Person.prototype);
alert(keys) // name age job sayName
原生对象的原型
typeof Array.prototype.sort // function
type String.prototype.substring // function
原型的最大的问题
原型问题,1)它省略了为构造函数传递初始化参数这一环节,结果所有的实例在默认情况下都将取得相同的属性值。2)原型模式最大的问题就是其共享的本性所导致。
4)继承
原型链
function SuperType () {
this.property = true;
}
SuperType.prototype.getSuperValue = function () {
return this.property;
}
function SubType () {
this.subproperty = false;
}
// 继承SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function () {
return this.subproperty;
};
var instance = new SubType(); // SubType的实例
instance instanceof Object; true
instance instanceof SuperType; true
instance instanceof SubType; true
Object.prototype.isPrototypeOf(instance); // true
SuperType.prototype.isPrototypeOf(instance); // true
SubType.prototype.isPrototypeOf(instance); // true
通过原型链实现继承的时候,不能使用对象字面量创建原型方法,这样做会导致重写原型链。
原型链的问题
原型链最大的问题在于,包含引用类型的原型属性会被所有实例共享,所以需要要在构造函数中而不是原型对象中定义属性的原因。
如果超类的构造函数中定义一个colors属性,改属性包含一个数组(引用类型值)。 SuperType的每个实例都会有各自包含自己数组的colors属性,而SubType通过原型链继承了SuperType之后,SubType.prototype就变成SuperType的一个实例,因此它拥有了自己的colors属性,所有的SubType的所有实例都会共享这一个colors属性。
原型链第二个问题,创建子类型的实例时,不能向超类型的构造函数中传递参数。
借用构造函数继承
function SuperType () {
this.colors = ['red', 'blue', 'green'];
}
function SubType () {
superType.call(this);
}
使用call()方法或者apply()方法,实际上是在新创建的SubType实例的环境下调用了SuperType构造函数。即在新SubType对象上执行SuperType()函数中定义的所有对象初始化代码,所以,SubType的每个实例就都会具有自己的colors属性的副本了
对于原型链而言,借用构造函数有一个很大的优势,即可以在子类型构造函数中向超类构造函数传递参数。
function SuperType(name) {
this.name = name;
}
function SubType () {
SuperType.call(this, 'Nicolas');
this.age = 29;
}
var instance = new SubType();
弊端: 方法都要在构造函数中定义,因此函数复用就无从谈起。
组合模式
function SuperType(name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
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);
}
这样一来,不同的SubType实例既分别拥有自己属性--包括colors属性,又可以使用相同的方法了。
原型式继承
function object(o) {
function F();
F.prototype = o;
return new F();
}
原型式继承中包含引用类型值的属性始终都会共享相应的值,就像使用原型模式一样。
寄生式继承
组合继承的不足---组合继承最大的问题就是无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类原型的时候,第二次是子类型构造函数内部。
var inheritPrototype(subType, superType) {
var prototype = object(superTyp.prototype); // 创建一个新对象表示是超类的原型对象
prototype.constructor = subType;
subType.prototype = prototype;
}
function SuperType(name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
SuperType.prototype.sayName = function () {
alert(this.name);
}
function SubType (name, age) {
SuperType.call(this, name);
this.age = age;
}
inheritPrototype(subType, superType);
SubType.prototype.sayAge = function () {
alert(this.age);
}