面向对象编程——继承
-
类式继承
//声明父类 function superclass(){ this.superValue = true; } //为父类添加共有方法 superclass.prototype.getSuperValue = function(){ return this.superValue; }; //声明子类 function subclass(){ this.subValue = false; } //继承父类 subclass.prototype = new superclass(); //为子类添加共有方法 subclass.prototype.getSubValue = function(){ return this.subValue; } var instance = new subclass(); console.log(instance.getSuperValue());//true console.log(instance.getSubValue());//flase
这里声明了两个类,第二个类的原型prototype被赋予了第一个类的实例。类的原型对象的作用就是为类的原型添加共有方法,但是类不能直接访问这些属性和方法,必须通过原型prototype来访问。新创建的对象不仅仅可以访问父类原型上的属性和方法,也可以访问父类构造函数中复制的属性和方法。将这个对象赋值给子类的原型,那么这个子类的原型同样可以访问父类原型上的属性和方法与从父类构造函数中复制的属性和方法。
此外,我们还可以通过instanceof来检测某个对象是否是某个类的实例,或者说是是否继承了某个类。instanceof 是通过判断对象的prototype链来检测的:
console.log(instance instanceof superclass);
console.log(instance instanceof subclass);
console.log(subclass instanceof superclass);
在上面的调试看来,instanceof是判断前面对象是否是后面类的实例,并不表示两者的继承 。
有时候因为类继承,我们会 无意间伤害到一些变量的属性:
function superclass(){
this.books = ['javascript模式设计', 'html', 'css'];
}
function subclass(){}
subclass.prototype = new superclass();
var instance1 = new subclass();
var instance2 = new subclass();
console.log(instance2.books);//['javascript模式设计', 'html', 'css'];
instance1.books.push('小白学习');
console.log(instance2.books) ;//['javascript模式设计', 'html', 'css', '小白学习'];
instance1的一个无意修改就无情的伤害了instance2的book属性。
-
构造函数继承
为了解决上面的问题,我们就可以用构造函数继承。//声明父类 function superclass(id){ //引用类型公有属性 this.books = ['javascript', 'html', 'css']; //值类型共有属性 this.id = id; } //父类声明原型方法 superclass.prototype.showbooks = function(){ console.log(this.books); } //声明子类 function subclass(id){ superclass.call(this, id); } //创建第一个子类的实例 var instance1 = new subclass(19); //第二个 var instance2 = new subclass(22); instance1.books.push('小白学习'); console.log(instance1.books);//['javascript', 'html', 'css', '小白学习']; console.log(instance1.id);//19 console.log(instance2.books);//['javascript', 'html', 'css']; console.log(instance2.id);//22 instance1.showbooks();//typeerror
在这里,superclass.call(this, id);是构造函数继承的精华,由于call这个方法可以更改函数的作用环境,所以在子类中,对superclass调用这个方法就是将子类中的变量在父类中执行一遍,由于父类中是给this绑定属性的,因此子类自然也就继承了父类的共有属性。由于这种类型的继承没有涉及原型prototype,所以父类的原型方法自然不会被子类继承。
-
组合继承
//声明父类 function superclass(name){ //引用类型公有属性 this.books = ['javascript', 'html', 'css']; //值类型共有属性 this.name = name; } //父类原型共有方法 superclass.prototype.getName = function(){ console.log(this.name); } //声明子类 function subclass(name, time){ //构造函数的方式继承父类name属性 superclass.call(this, name); //子类中新增共有属性 this.time = time; } //类式继承 子类原型继承父类 subclass.prototype - new subclass(); //子类原型方法 subclass.prototype.getTime = function(){ console.log(this.time); } //创建第一个子类的实例 var instance1 = new subclass('js book', 2014); instance1.books.push('小白学习'); console.log(instance1.books);//['javascript', 'html', 'css', '小白学习']; instance1.getName();//js book instance1.getTime();//2014 //第二个 var instance2 = new subclass('css book', 2013); console.log(instance2.books);//['javascript', 'html', 'css']; instance2.getName();//css book instance2.getTime();//2013
子类的实例中更改父类继承下来的引用类型属性如books,根本不会影响到其他的实例。因为我们在使用构造函数继承时执行力一边父类的构造函数,而在实现子类原型的类继承时有调用了一百年父类的构造函数。
-
原型式继承
是借助原型prototype可以根据已有的对象创建一个新对象,同时不必创建一个新的自定义的对象类型。代码实现如下:function inheritObject(o){ //声明一个过度函数 function F(){} //过度对象的原型继承父对象 F.prototype = o; //返回过度对象的一个实例,该实例的原型继承了父类对象 return new F(); }
过度对象的出现目的是为了创建要返回的新的实例化对象。(其实就是对类继承的封装)
-
寄生式继承
var book = { name: 'js book', alikebook: ["css book", 'html book'] }; function createBook(obj){ //通过原型继承方式创建新对象 var o = new inheritObject(obj); //拓展新对象 o.getName = function(){ console.log(name); }; //返回拓展新对象 return o; }
其实他就是对原型继承 的二次封装,并且在这第二次封装过程中对继承的对象进行了拓展,这样新创建的对象不仅仅有父类的属性和方法,而且还添加新的属性和方法。
- 寄生组合式继承
待续