ECMAScript依靠原型链来实现继承;
原型链
原型链,是利用原型让一个引用类型继承另一个引用类型的属性和方法。
每个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。
如果,一个原型又是另一个原型的实例,那么上述关系依旧成立,如此层层递进,就构成了实例和原型的链条,就是所谓的原型链
eg:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function SuperType ( ) { this .property = true ; } SuperType.prototype.getSuperValue = function ( ) { return this .property; } function SubType ( ) { this .subproperty = false ; } SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function ( ) { return this .subproperty; }; var instance = new SubType();
以上代码定义了两个类型:SuperType和SubType。 SubType继承了SuperType。 这个继承是通过:创建一个SuperType的实例,然后将这个实例赋值给SubType.prototype实现的。
实现的本质是:重写原型对象,代之以一个新类型的实例。
所有的引用类型都默认几成了Object。这个继承也是通过原型链实现的。
原型和实例的关系: 可以通过instanceof操作符和isPrototypeOf()方法来确认。
给原型添加方法的代码,一定要放在替换原型的语句之后。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 function SuperType ( ) { this .property = true ; } SuperType.prototype.getSuperValue = function ( ) { return this .property; } function SubType ( ) { this .subproperty = false ; } SubType.prototype = new SubperType(); SubType.prototype.getSubValue = function ( ) { return this .subproperty;} SubType.prototype.getSuperValue = function ( ) { return false ; } var instance = new SubType();alert(instance.getSuperType());
以上代码,添加了getSubValue()方法到SubType中,而重写了SubType中的getSubType()方法,将SuperType中的getSuperValue()覆盖了。
原型链的问题
1.在通过原型实现继承时,原型会变成另一个类型的实例。于是,原先的实例属性就会变成现在的原型属性了。
eg:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function SuperType ( ) { this .color = {"red" , "blue" , "green" }; } function SubType ( ) {} SubType.prototype = new SuperType(); var instance1 = new SubType();instance1.colors.push("black" ); alert(instance1.colors); var instance2 = new SubType();alert(instance2.colors);
2.创建子类型的实例时,不能向超类型的构造函数中传递参数。
借用构造函数
为了解决原型中包含引用类型值,所带来的问题。人们开始使用一种叫借用构造函数
的技术(伪造对象,或者经典继承)。
这种方法:在子类型构造器的内部调用超类型的构造函数。
eg:
1 2 3 4 5 6 7 8 9 10 11 12 13 function SuperType ( ) { this .colors = ["red" , "blue" , "green" ]; } function SubType ( ) { SuperType.call(this ); } var instance1 = new SubType();instance1.colors.push("black" ); var instance2 = new SubTyp();instance2.colors
上述代码,在构造函数中,调用了超类型的构造函数。等于说是在这个构造函数中,执行了一边超类型的构造函数的代码。
另外,这个方式还可以像超类型的构造函数传递参数:
1 2 3 4 5 6 7 8 9 10 11 12 function SuperType (name ) { this .name = name; } function SubType ( ) { SuperType.call(this , "Nicholas" ); this .age = 29 ; } var instance = new SubType();alert(instance.name); alert(instance.age);
这种方式存在的问题: 这种方式的继承,方法都在构造函数中定义,所以就没办法做函数复用了。
组合继承
组合继承,是指将原型链和借用构造函数组合。
eg:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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); }; var instance1 = new SubType("Nicholas" , 29 );instance1.colors.push("black" ); instance1.sayName(); instance1.sayAge(); var instance2 = new SubType("Gred" , 27 );instance2.colors; instance2.sayName(); instance2.sayAge();
这是JavaScript中最常用的继承模式了。
原型式继承
基于原有的对象创建新对象,就不必创建自定义类型了。
1 2 3 4 5 function object (o ) { function F ( ) {} F.prototype = o; return new F(); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 var person = { name: "Nicholas" , friends: ["Shelby" , "Court" , "Van" ] }; var anotherPerson = object(person);anotherPerson.name = "Greg" ; anotherPerson.friends.push("Rob" ); var yetAnothorPerson = object(person);yetAnothorPerson.name = "Linda" ; yetAnothorPerson.friends.push("Barbie" ); alert(person.friends);
这种原型式继承,要求必须有一个对象可以作为另一个对象的基础。
ECMAScript 5新增的Object.create()方法,规范了原型式继承:
1 2 3 4 5 6 7 8 9 10 11 12 var person = { name: "Nicholas" , friends: ["Shelby" , "Court" , "Van" ] }; var anotherPerson = Objcet.create(person);var yetAnothorPerson = Objcet.create(person, { name: { value: "Greg" } });
这种方式,适用于只想让一个对象和另一个对象保持相思的情况。
寄生式继承
创建一个仅用于封装继承过程的函数,这个函数在内部以某种方式增强对象,最后再返回对象。
1 2 3 4 5 6 7 function createAnther (original ) { var clone = object(original); clone.sayHi = function ( ) { alert("Hi" ); }; return clone; }
寄生组合式继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 function inheritProtytype (subtype, superType ) { var prototype = object(superType.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); };