子类的原型对象-类式继承
//父类
function SuperClass(){
this.superVal = true;
}
//为父类添加公共方法
SuperClass.prototype.getSupperVal = function () {
return this.superVal;
};
//子类
function Child() {
this.subVal = false;
}
//继承父类
Child.prototype = new SuperClass();
//为子类天剑公共方法
Child.prototype.getSubVal = function () {
return this.subVal;
};
类继承原理
父类新创建的对象不仅可以访问父类原型上的属性和方法,还可以访问父类构造函数的属性和方法。
这种继承方式的缺点:
1.子类型通过他的原型prototype对父类实例化,继承了父类。随意说弗雷中的共有属性要是引用类型,就会在子类中被所有的的实例共享,因此一个子类的实例更改子 子类原型从父类构造函数中继承来的共有属性就会直接影响其他子类。
function SuperClass(){
this.books = ['js','html','css'];
}
//子类
function Child() {
}
//继承父类
Child.prototype = new SuperClass();
var instance1 = new Child();
var instance2 = new Child();
console.log(instance1.books);
instance1.books.push("设计模式");
console.log(instance1.books);
两次查询属性发生了变化。
2.由于子类实现的继承是依靠其原型prototype对父类的实例化实现的,因此在创建父类的时候,是无法向父类传递参数的,因为在实例化父类的时候也无法对父类构造器内的属性进行初始化。
创建即继承–构造函数继承
//父类
function SuperClass(id){
//引用类型
this.books = ['js','html','css'];
//值类型
this.id = id;
}
SuperClass.prototype.showBooks = function () {
console.log(this.books);
};
//子类
function ChildClass(id) {
//继承父类
SuperClass.call(this,id);
}
var instance1 = new ChildClass(10);
var instance2 = new ChildClass(11);
console.log(instance1.books);
console.log(instance1.id);
console.log(instance2.books);
console.log(instance2.id);
instance1.showBooks();
这种方式通过 SuperClass.call(this,id);
方式使得子类继承了父类的共有属性。但是这中集成没有涉及到原型,所以父类的原型方法不会被子类继承。而如果想被子类继承就必须要放在构造函数中,这样创建出来的每个实例都会单独拥有一份而不可公用,这样违背了代码复用原则。
组合继承
//父类
function SuperClass(name){
//引用类型
this.books = ['js','html','css'];
//值类型
this.name = name;
}
SuperClass.prototype.getName = function () {
console.log(this.name);
};
//子类
function ChildClass(name,time) {
//继承父类
SuperClass.call(this,name);
//子类属性
this.time = time;
}
ChildClass.prototype = new SuperClass();
ChildClass.prototype.getTime = function () {
console.log(this.time);
};
var instance1 = new ChildClass("js",1024);
instance1.push('设计模式');
instance1.books();
instance1.getTime();
instance1.getName();
var instance2 = new ChildClass("css",1099);
console(instance2.books);
instance2.getTime();
instance2.getName();
这样子类的实例中各自持有从父类继承下来的引用类型属性,各自修改互不影响。并且子类实例化过程中还可以将参数传递到父类构造器。通过原型子类继承类父类远行对象的共有方法。
还要问题:我们在使用构造函数继承时候执行了一遍父类的构造函数,而在实现子类原型的类式继承的时候有调用了一次父类构造函数。因此父类构造函数被调用了2遍。
洁净的继承—原型式继承
function inheriaobject(o){
//声明一个过渡函数对象
function F() {
}
F.prototype = o;
return new F();
}
跟类式继承一样,存在父类引用对象的属性被公用的问题
寄生式继承
function inheriaobject(o){
//声明一个过渡函数对象
function F() {
}
F.prototype = o;
return new F();
}
var book = {
name : "js book",
books : ['js','html','css']
};
function createBook(obj) {
//通过原型继承
var o = inheriaobject(obj);
//拓展对象
o.getName = function () {
console.log(o.name);
};
//返回拓展后的对象
return o;
}
这种方式就是对原型继承的第二次封装,实现l了原先 对继承对象的扩展,新创建的对象不仅仅拥有父类的属性和方法,还添加了新的属性和方法。
终极继承者----寄生组合式继承
function inheritObject(o){
function F() {
}
F.prototype = o;
return new F();
}
/**
* 寄生式继承 继承原型
* @param subClass
* @param superClass
*/
function inheritPrototype(subClass,superClass){
//复制一份父类的原型副本保存在变量中
var p = inheritObject(superClass.prototype);
//修正因为重写子类型原型导致子类的contrustor属性被修改
p.constructor = subClass;
//设置子类型的原型
subClass.prototype = p;
};
//父类
function SuperClass(name){
//引用类型
this.books = ['js','html','css'];
//值类型
this.name = name;
}
SuperClass.prototype.getName = function () {
console.log(this.name);
};
//子类
function ChildClass(name,time) {
//继承父类
SuperClass.call(this,name);
//子类新增的属性
this.time = time;
}
inheritPrototype(ChildClass,SuperClass);
ChildClass.prototype.getTime = function () {
console.log(this.time);
};
var instance1 = new ChildClass("js",1024);
instance1.books.push('设计模式');
instance1.getTime();
instance1.getName();
var instance2 = new ChildClass("css",1099);
console.log(instance2.books);
instance2.getTime();
instance2.getName();
// 在构造函数继承中,子类调用了父类的构造函数,我们不需要在调用父类构造函数。因此 我们需要的就是父类的原型对象的一个副本。而这个副本我们通过原型继承就可以得到,但是这么直接赋值给子类是有问题的,因为对父类原型对象复制得到的复制对象p中的contruetor指向的=不是subclass子类对象,因此在寄生式继承中需要对复制对象p中的constructor指向属性,最后将得到的赋值对象p赋值给子类的原型。这样子类的原型就继承了父类的原型并且没有执行父类的构造函数。