面向对象编程--继承
一.子类的原型继承 -- 类式继承
// 声明父类
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 b = new SubClass();
console.log(b.getSuperValue()) // true
总结:类式继承需要将父类的实例赋值给子类的原型。why?类的原型对象的作用就是为类的原型添加共有方法,但类不能直接访问这些属性和方法,必须通过原型prototype来访问。实例化一个父类,新创建的对象复制了父类的构造函数内的属性与方法并且将原型__proto__指向了父类的原型对象。这样就拥有了父类的原型对象上的属性与方法,并且这个新创建的对象课直接访问到父类原型对象上的属性与方法。如果我们将这个新创建的对象赋值给子类的原型,那么子类的原型就可以访问大父类的原型属性与方法了。新建的对象不仅仅可以访问父类原型上的属性和方法,也可以访问从父类构造函数中复制出来的属性与方法。 将这个赋值给子类的原型,那么这个子类的原型同样可以访问父类原型上的属性与方法与从父类构造函数中复制出来的属性与方法。 ---类式继承原理
通过instanceof检测某个对象是否是某个类的实例,或者说某个对象是否继承了某个类。这样就可以判断对象与类之间的关系了
instanceof是通过判断对象的prototype链来确定这个对象是否是某个对象的实例,而不关心对象与类的自身结构。
console.log(b instanceof SuperClass); //true
console.log(b instanceof SubClass); //true
console.log(SubClass instanceof SuperClass); //false
console.log(SubClass.prototype instanceof SuperClass); //true
//你所创建的对象都是Object的实例,Javascript为我们提供的原生对象Object.
console.log(SuperClass instanceof Object) //true
console.log(SubClass instanceof Object) //true
console.log(b instanceof Object) //true
Object是所有对象的祖先。
缺点:
1.由于子类通过器原型prototype对父类实例化,继承了父类,所以说父类中的共有属性必须是引用类型,就会在子类中被所有实例公用,因此一个子类的实例改变子类原型从父类构造函数中继承来的共有属性就会直接影响待其他子类。
var SuperClass =function(){
this.book = ['html',"css","javascript"];
this.superValue = true
}
//为父类添加共有方法
SuperClass.prototype.getSuperValue = function(){
return this.superValue
}
//声明子类
var SubClass = function(){
this.subValue = false;
}
//继承父类
SubClass.prototype = new SuperClass();
//为子类添加公共的方法
SubClass.prototype.getSubValue = function(){
return this.subValue;
}
var b = new SubClass();
var b1 = new SubClass()
console.log(b.book) //["html", "css", "javascript"]
console.log(b1.book) //["html", "css", "javascript"]
b1.book.push("python");
console.log(b.book) //["html", "css", "javascript", "python"]
console.log(b1.book) //["html", "css", "javascript", "python"]
2.由于子类实现的继承是靠其原型prototype对父类的实例化的,因此,在创建父类的时候,无法向父类传递参数,因此在实例化父类的时候也无法对父类的构造函数内的属性进行初始化。
二:创建即继承 -- 构造函数继承
//构造函数式继承
//声明父类
function SuperClass(id){
//引用类型共有属性
this.books = ['javascript','html','css'];
//值类型共有属性
this.id = id;
}
//声明父类原型方法
SuperClass.prototype.showBooks = function(){
console.log(this.books)
}
//声明子类
function SubClass(id){
console.log(this) //SubClass {}
//调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.call(A, args1,args2);即A对象调用B对象的方法。
SuperClass.call(this,id);
}
//创建第一个子类的实例
var instance1 = new SubClass(10)
//创建第一个子类的实例
var instance2 = new SubClass(10)
//每个实例都会单独拥有一份而不能供用
console.log(instance1)
// SubClass {books: Array(3), id: 10}
// books: (4) ["javascript", "html", "css", "设计模式"]
// id: 10
// __proto__: Object
console.log(instance2)
// SubClass {books: Array(3), id: 11}
// books: (3) ["javascript", "html", "css"]
// id: 11
// __proto__: Object
instance1.books.push("设计模式");
console.log(instance1.books) //["javascript", "html", "css", "设计模式"]
console.log(instance1.id) //10
console.log(instance2.books) //["javascript", "html", "css"]
console.log(instance2.id) //11
特点: 由于Call这个方法可以更改函数的作用环境,因此在子类中,对SuperCalss调用这个方法就是将子类中的变量在父类在执行一遍,由于父类中是给this绑定属性的,因此子类资源就继承了父类的共有属性。
缺点:由于这种类型的继承没有设计原型prototype,所以父类的原型自然不会被子类继承,而如果想要被子类继承就必须放在构造函数中,这样创建出来的每个实例都会单独拥有一份而不能供用,违背了代码复用的原则。
三:组合继承
类式继承是要通过子类的原型prototype对父类的实例化来的,
构造函数式继承是通过子类的构造函数作用环境中执行一次父类的构造函数来实现的。
//声明父类
function SuperClass(name){
//值类型共有属性
this.name = name;
//引用类型共有属性
this.books = ["html","css","javascript"];
}
//父类原型属性共有方法
SuperClass.prototype.getName = function(){
console.log(this.name)
}
//声明子类
function SubClass(name,time){
//构造韩式式继承父类name属性
SuperClass.call(this, name);
//子类中新增共有属性
this.time = time;
}
// 类式继承 子类原型继承父类
SubClass.prototype = new SuperClass();
// 子类原型方法
SubClass.prototype.getTime = function(){
console.log(this.time)
}
var instance1 = new SubClass("js book", 2014);
console.log(instance1)
// SubClass {name: "js book", books: Array(3), time: 2014}
// books: (4) ["html", "css", "javascript", "设计模式"]
// name: "js book"
// time: 2014
// __proto__: SuperClass
instance1.books.push("设计模式");
console.log(instance1.books) //["html", "css", "javascript", "设计模式"]
instance1.getTime() //2014
instance1.getName() //js book
var instance2 = new SubClass("css book", 2012);
console.log(instance2)
//SubClass {name: "css book", books: Array(3), time: 2012}
//books: (3) ["html", "css", "javascript"]
//name: "css book"
//time: 2012
//__proto__: SuperClass
console.log(instance2.books) //["html", "css", "javascript"]
instance2.getTime() //2012
instance2.getName() //css book
总结: 子类的实例中更改父类继承下来的引用类型属性,根本不会影响到其他实例,并且子类实例化过程中又能将参数传递到父类的构造函数。
缺点: 使用构造函数继承时执行了一遍父类的构造函数,而在实现子类原型的类式继承又调用了一遍父类的构造函数,因此父类调用了两遍。