面向对象程序设计
创建对象
工厂模式
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("Lyf", 20, "student");
构造函数模式
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
};
}
var person1 = new Person("Lyf", 20, "student");
按照惯例,构造函数始终以大写字母开头,非构造函数以小写字母开头。
原型模式
每个函数都有一个 prototype(原型)的属性,这个属性是一个指针,指向一个对象,这个对象包含所有实例共享的属性和方法。
使用原型对象的好处是:让所有对象实例共享它所包含的属性和方法。
function Person(){
}
Person.prototype.name = "Lyf";
Person.prototype.age = 20;
Person.prototype.job = "student";
Person.prototype.sayName = function(){
alert(this.name);
}
var person1 = new Person();
person1.sayName(); // "Lyf"
var person2 = new Person();
person2.sayName(); // "Lyf"
alert(person1.sayName == person2.sayName); // true
在所以实例中都无法访问到 [ Prototype ],但是可以通过 isPrototypeOf()方法确定对象之间是否存在这种关系。
alert(Person.prototype.isPrototypeOf(person1)); // true
alert(Person.prototype.isPrototypeOf(person2)); // true
ECMAScript 5 新增的方法:Object.getPrototypeOf(),这个方法返回 [ Prototype ]
alert(Object.getPrototypeOf(person1) == Person.prototype);
// true
alert(Object.getPrototypeOf(person1).name); // "Lyf"
当为对象实例添加属性时,这个属性会 屏蔽 原型对象中保存的同名属性,该属性只会阻止我们访问原型中的那个属性,不会修改那个属性。
delete 操作符可以完全删除实例属性,从而让我们访问原型属性。
使用 hasOwnProperty()方法可以检测一个属性是存在于实例中,还是原型中。
function Person(){
}
Person.prototype.name = "Lyf";
Person.prototype.age = 20;
Person.prototype.job = "student";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
var person2 = new Person();
alert(person1.hasOwnProperty("name")); // false
person1.name = "JackLiu";
alert(person1.name); // "JackLiu" --- 来自实例
alert(person2.name); // "Lyf" --- 来自原型
alert(person1.hasOwnProperty("name")); // true 来自实例
alert(person2.hasOwnProperty("name")); // false 来自原型
delete person1.name;
alert(person1.name); // "Lyf" --- 来自原型
更简洁的原型写法
function Person(){
};
Person.prototype = {
name : "Lyf",
age : 20,
jog : "student",
sayName : function(){
alert(this.name);
}
};
原生对象的问题
function Person(){
}
Person.prototype = {
name : "Lyf",
age : 20,
friends : ["老王", "老张"],
sayName : function(){
alert(this.name);
}
};
var person1 = new Person();
var person2 = new Person();
person1.push("老李");
alert(person1.friends); // "老王,老张,老李";
alert(person2.friends); // "老王,老张,老李";
alert(person1.friends === person2.friend); // true
组合使用构造函数模式和原型模式
组合使用构造函数模式和原型模式,是最常见的方式。
- 构造函数 用于定义实例属性
- 原型模式 用于定义方法和共享属性
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ["老王","老张"];
}
Person.prototype = function(){
constructor : Person,
sayName : function(){
alert(this.name);
}
}
var person1 = new Person("Lyf", 20, "student");
var person2 = new Person("JackLiu", 20, "student");
person1.friends.push("老李") ;
alert(person1.friends); // "老王, 老张, 老李"
alert(person2.friends); // "老王, 老张"
alert(person1.friends === person2.friends); // false
alert(person1.sayName === person2.sayName); // true
实例属性都在构造函数中,实例共享的属性 constructor 和 方法 sayName在原型中定义的。
动态原型模式
function Person(name, age, job){
// 属性
this.name = name;
this.age = age;
this.job = job;
// 方法
if( typeOf this.sayName != "function" ){
Person.prototype.sayName = function(){
alert(this.name);
}
}
}
var friend = new Person("Lyf", 20, "student");
friend.sayName();
寄生构造函数模式
这种模式的基本思想是创建一个函数,该函数的作用是封装创建对象的代码,然后再返回新创建的对象。(不建议使用)
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;
}
var friend = new Person("Lyf", 20, "student");
friend.sayName(); // "Lyf"
稳妥构造函数模式
稳妥对象 指的是没有公用属性,而且其方法也不引用 this 的对象。
function Person(name, age, job){
// 创建要返回的对象
var o = new Object();
// 可以再这里定义私有变量和函数
o.sayName = function(){
alert(name);
}
// 返回对象
return o ;
}
这种对象创建的对象中,除了使用 sayName()方法之外,没有其他方法访问name的值。
var friend = new Person("Lyf", 20, "student");
friend.sayName(); // "Lyf"
继承
原型链
ECMAScript中,将原型链作为实现继承的主要方法。
其基本思想是:利用原型链让一个引用类型继承另一个引用类型的属性和方法。
原型链的搜索机制:
- 首先会再实例中搜索该属性
- 如果没找到,继续搜索实例的原型
确定原型和实例的关系
第一种方法:可以通过 instanceof 操作符,来确认原型和实例之间的关系
alert(instance instanceof Object); // true
第二种方法:使用 isPrototypeOf()方法
alert(Object.prototype.isPrototypeOf(instance)); // true
谨慎的定义方法
通过原型链实现继承,不能使用对象字面量创建原型方法,这样会重写原型链。
function SuperType(){
this.property = true;
}
Supertype.prototype.getSuperValue = function(){
return this.prototype;
}
function SubType(){
this.subproperty = false;
}
// 继承了 SuperType
SubType.prototype = new SuperType();
// 使用字面量添加新方法,会导致上一行代码无效
SubType.prototype = {
getSubValue : function(){
return this.subproperty;
},
someOtherMethod : function(){
return false;
}
};
var instance = new SubType();
alert(instance.getSuperValue()); // error!
以上代码刚把 SuperType实例赋值给原型,接着原型换成字面量表示,现在的原型相当于 Object对象实例,而非 SuperType实例。
原型链的问题
用原型链来继承时,包含引用类型值的原型属性会被所有实例共享。
function SuperType(){
this.color = ["red", "blue", "green"];
}
function SubType(){
}
// 继承了SuperType
SubType.prototype = new SuperType();
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); // "red,blue,green,black"
var instance2 = new SubType();
alert(intance2.colors); // "red,blue,green,black"
借用构造函数
在解决原型中包含引用类型值所带来的问题,开发人员使用借用构造函数的技术(有时也叫伪造对象或经典继承)。即在子类型构造函数的内部调用超类型的构造函数。使用 call()方法和 apply()方法也可以在将来新创建的对象上执行构造函数。
function SuperType(){
this.colors = ["red", "blue", "green"];
}
function SubType(){
// 继承了SuperType
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); // "red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors); // "red,blue,green"
传递参数
相对于原型链而言,借用构造函数可以在子类型中向超类型构造函数传递参数。
function SuperType(name){
this.name = name;
}
function SubType(){
// 继承了SuperType,同时还传递了参数
SuperType.call(this,"Lyf");
// 实例属性
this.age = 20;
}
var instance = new SubType();
alert(instance.name); // "Lyf"
alert(instance.age); // 20
组合继承
组合继承也叫伪经典继承,将原型链和借用构造函数的技术组合到一块,从而发挥二者之长的一种继承模式。
使用原型链实现对原型属性和方法的继承,从而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数的复用,又能保证每个实例都有自己的属性。
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.constructor = SubType;
SubType.prototype.sayAge = function(){
alert(this.age);
}
var instance1 = new SubType("Lyf", 20);
instance1.colors.push("black");
alert(instance1.colors); // "red,blue,green,black"
instance1.sayName(); // "Lyf"
instance1.sayAge(); // 20
var instance2 = new SubType("JackLiu", 20);
alert(instance2.colors); // "red,blue,green"
instance2.sayName(); // "JackLiu"
instance2.sayAge(); // 20