一、简介
继承是面向对象中一个比较核心的概念。
其他正统面向对象语言都会用两种方式实现继承:一个是接口实现,一个是继承。
而ECMAScript只支持继承,不支持接口实现。
1、代码
//定义一个父类
function Father (money){
this.money = money;
}
//定义一个子类
function Son (age){
this.age = age;
}
/*说明:
new Father ();新创建一个Father类型的实例对象,这个实例对象包含了Father类的原型对象信息和构造函数信息。
将新创建的这个心对象赋值给Son类型的原型对象,相当于所有的Son类型的实例在创建时都包含了Father类型的原型对象信息、构造函数信息、还有自身的构造函数信息。
以上通过设置原型对象属性的方法完成了继承关系,这种方式叫做原型链。
*/
Son.prototype = new Father(1000);//此行代码就完成了子类继承父类
var son = new Son(18);//创建一个子类实例对象
alert(son.money);//1000 子类实例对象具有money属性。
2、从属关系
//从属关系:所有的对象都从属于Object类型
//子类从属与父类
var father = new Father();
var son = new Son();
alert(father instanceof Object);//true
alert(son instanceof Object);//true
alert(son instanceof Father);//true
alert(father instanceof Son);//false
当一个类型中的父类有属性A,而子类的构造函数中也有属性A时,应当取父类的A属性还是子类的属性A;
每个对象都有一个构造函数和原型对象,当构造函数和原型对象里具有一个同名的属性时,系统会先在构造函数里查找,有的话直接返回,没有的话再去原型对象里寻找。这就是所谓的就近原则,也更好的实现了共享与自身的灵活性。换句话说就是自己有的东西就不去原型那里取,自己没有就去找原型要,原型也没有就彻底没有了。
子类继承父类,继承的父类的构造函数和原型对象。将父类的构造函数和原型对象作为子类的原型对象,所以当子类的构造函数里有属性A就直接返回,没有的话就去自己的原型对象取,也就是父类里取。所以继承关系也符合就近原则。
//定义一个父类
function Father (){
this.money = 1000;
}
//定义一个子类
function Son (){
this.money = 2000;
this.age = 18;
}
Son.prototype = new Father();
var son = new Son();
alert(son.money);//2000 自己有就不会取父类里的money
4、存在问题
通过原型链实现继承存在问题:
第一个:子类的prototype是父类的一个实例对象,存储的信息是固定的。不能从子类进行传参。
第二个:字面量重写原型会中断关系。Son.prototype = new Object();重写后断开继承关系
三、实现继承-对象冒充
1.代码
//定义一个父类
function Father (money){
this.money = money;
}
//定义一个子类
function Son (money,age){
// 个人觉得:
// 此处是当普通函数使用的。
// 使用对象冒充之后Son的实例对象可以调用Father这个方法
// 调用Father方法之后实例对象就增加了money属性。也就相当于从父类继承来了money属性,并且参数是子类传的。
Father.call(this,money);
this.age = age;
}
var son = new Son(1000,18);
alert(son.money);//1000
alert(son.age);//18
2.存在问题
借用对象冒充虽然解决了刚才两种问题,但没有原型,复用则无从谈起。所以,我们需要 原型链+对象冒充 的模式,这种模式成为组合继承。
四、原型链+对象冒充
//定义一个父类
function Father (money){
this.money = money;
}
Father.prototype.run = function (){
return '这是父类方法....';
};
//定义一个子类
function Son (money,age){
Father.call(this,money);
this.age = age;
}
Son.prototype = new Father();//个人觉得应该加上:Son.prototype.constructor = Son;这行代码
//个人觉得:对象冒充使子类有了一个money属性,而原型链也使子类有了一个money属性,只不过这个money属性是存在原型对象里,会就近原则被隐藏掉。
var son = new Son(1000,18);
alert(son.money);//1000
alert(son.age);//18
alert(son.run());//这是父类方法...
五、组合模式
//临时中转函数
function obj(o) {
function F() {}
F.prototype = o;
return new F();
}
//完成继承关系。要想在子类的原型对象里增加新的属性可以在这个方法内完成。个人觉得:也可以在中转函数中完成
function create(Father, Son) {
var f = obj(Father.prototype);
f.constructor = Son; //调整原型对象的构造指针
son.prototype = f;
}
//定义父类型
function Father(name, age) {
this.name = name;
this.age = age;
}
Father.prototype.run = function () {
return this.name + this.age + '运行中...'
}
function Son(name, age) {
Father.call(this, name, age); //
}
//创建对象
create(Father, Son); //创建继承关系的函数
var son = new Son('Lee', 100);
alert(son.run());
alert(son.constructor);