组合使用构造函数模式和原型模式
创建自定义类型最常见的方式就是组合使用构造函数模式和原型模式。构造函数模式用于定义实力属性,而原型模式用于定义方法和共享的属性。
function Person(name ,age,job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ["Shelby", "Court"];
}
Person.ptototype = {
constructor:Person,
sayName:function(){
alert(this.name);
}
}
var person1 = new Person("hah",32,"doctor");
var person2 = new Person("hah",32,"doctor");
person1.friends.push("van");
alert(person1.friends);Shelby,Court,van
alert(person2.friends);Shelby,Court
alert(person1.sayName = person2.sayName);//true
动态原型模式
把所有信息封装在构造函数中,在构造函数中初始化原型。既可以通过检查某个应该存在的方法是否有效来决定师傅需要初始化原型。
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);
};
}
}
只在sayName方法不存在的情况下,才会将它添加到原型中。即只在初次调用构造函数时才会执行。if语句检查的可以是初始化之后应该存在的任何属性或方法。只需检查一个就行。若不存在则初始化原型的所有属性或方法。
注:使用动态原型模式不能使用对象字面量来重写原型,因为如果在已经创建了实例的情况下重写原型,就会切断现有实例与原型之间的联系。
寄生构造函数模式
在函数中创建一个新对象,然后返回新对象。
function SpecialArray(){
var values = new Array();
values.push.apply(values,argument);
values.toPipedString = function(){
return this.join("|");
}
return values;
}
var colors = new SpecialArray("red","blue","green");
alert(colors.topipedString());//red|blue|green
注:使用这种方式不能使用instaneof来确定对象类型。可以使用其他模式的情况下建议不要使用这种模式。
稳妥构造函数模式
稳妥对象指的是没有公共属性,而且其方法也不引用this的对象。适合在一些安全的环境中或者防止数据被其他应用程序改动时使用。两个特点:
1. 新创建对象的实例方法不引用this
2. 不使用new擦操作符调用构造函数。
function Person(name,age,job){
var 0 = new Object();
//定义私有变量和函数
//添加方法
o.sayName = function(){
alert(name);
}
return o;
}
var friends = Person("hdkl",23,"dlksl");
friends.sayName();//hdkl
继承
ECMAScript只支持实现继承,而且依靠原型链实现
原型链
让原型对象等于另一个类型的实例,而这个原型对象又指向另一个原型,如此层层递进构成了原型链。
实现原型链有一种基本模式
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();
alert(instance.getSuperValue());//true
该继承的本质是重写原型对象,代之以一个新类型的实例。intance指向SubType的原型,SubType的原型又指向SuperType的原型。此时instance.constructor现在指向的是SuperType。现在的搜索过程是沿着原型链向上查找。上面的例子是这样的:
1. 搜索实例
2. 搜索SubType.prototype
3. 搜索SuperType.protoType
别忘记默认的原型
所有引用类型都默认继承了Object,而这个继承也是通过原型链实现的。所有函数的默认原型都是Object的实例,因此默认原型都会包含一个指向Object.prototype的内部指针。
确定原型与实例之间的关系
- 使用instanceof操作符,只要用这个操作符来测试实例与原型链中出现过得构造函数,结果就会返回true.
alert(instance instanceof Object);//true;
alert(instance instanceof SuperType);//true;
alert(instance instanceof SubType);//true;
- 使用isPrototypeOf()方法,只要原型链中出现过的原型,都可以说是该原型链所派生的实例的原型,都会返回true.
alert(Object.prototype.isPrototypeOf(instance)); //true
alert(SuperType.prototype.isPrototypeOf(instance)); //true
alert(SubType.prototype.isPrototypeOf(instance)); //true
谨慎定义方法
在重写超类中的方法或添加方法时要注意,给原型添加方法的代码一定要放在替换原型的语句之后。
还有就是在通过原型链实现继承时,不能使用对象字面量创建原型方法,因为会重写原型链。
原型链的问题
1.引用类型值的原型属性会被所有实例所共享。在通过原型来实现继承时,原型实际上会变成另一个类型的实例,而原先的实例属性也就变成了现在的原型属性了。
2. 在创建子类型的实例时,不能向超类型的构造函数中传递参数。或者说没办法在不影响所有对象实例的情况下给超类型的构造函数传递参数。
因此实践中很少单独使用原型链。
借用构造函数
在子类型构造函数的内部调用超类型构造函数。
function SuperType(){
this.colors = {"red","blue"};
}
function SubType(){
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors);//red,blue,black
var instance2= new SubType();
alert(instance2.colors);//red,blue
结果就是SubType的每个实例都会具有自己的colors属性的副本了。
组合继承
使用原型链实现对原型属性和方法的继承,借用构造函数实现对实例属性的继承。
function SuperType(name){
this.name = name;
this.colors = {"red","blue"};
}
SuperType.prototype.sayName = function(){
alert(this.name);
};
function SubType(name,age){
//继承属性
SuperType.call(this,name);
this.age = age;
}
//继承方法
SubType.prototype = new SuperType();
这是js中最常见的继承方法。
原型式继承
Object.create():接收两个参数,一个是用作原型的对象,一个是为新对象定义个额外属性的对象(可选)
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = Object.create(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
var yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
alert(person.friends); //
var anotherPerson = Object.create(person, {
name: {
value: "Greg"
}
});
alert(anotherPerson.name); //"Greg"
寄生式继承
创建一个仅用于封装继承过程的函数,该函数在内部以某种方法来增强对象。
function createAnother(original){
var clone = Object(original);
clone.sayHi = function(){
alert("hi");
}
return clone;
}
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = createAnother(person);
寄生组合式继承
组合继承的问题就是会调用两次超类型构造函数。一次是在创建子类型原型的时候,一次是在子类型构造函数内部。
寄生组合式继承:使用寄生式继承来继承超类型的原型。
function inheriPrototype(subType,superType){
var prototype = object(superType.ptototype);
prototype.constructor = subType;
subType.prototype = prototype;
};
function SuperType(name){
this.name = name;
this.colors = {"red","blue"};
}
SuperType.prototype.sayName = function(){
alert(this.name);
};
function SubType(name,age){
//继承属性
SuperType.call(this,name);
this.age = age;
}
inheriPrototype(SubType,SuperType)
js最理性的继承方式