1.通过原型链实现继承
改变原型链的指向
// 父类构造函数
function Person(name,age) {
this.name = name;
this.age = age;
}
Person.prototype.hello = function() {
console.log('hello');
}
//子类构造函数
function Student(score) {
this.score = score
}
Student.prototype = new Person('mike',20)
var stu = new Student(100)
console.log(stu.name) //mike
console.log(stu.age) //20
console.log(stu.score) //100
stu.hello() //hello
//当然也可以在改变原型指向的时候不初始化父类的属性值,通过访问对象实例的属性时,再初始化值
Student.prototype = new Person()
var stu = new Student(99)
stu.name = 'peter'
stu.age = 24
console.log(stu.name) //peter
console.log(stu.age) //24
console.log(stu.score) //99
使用原型继承,通过改变子类原型指向的方法,可以使子类继承父类的属性和方法,但是子类生成多个不同属性的实例对象时,若初始化父类属性则所有子类实例的这些继承过来的属性都相同,不初始化父类属性则代码比较冗余,每一项属性都需要重新赋值。
2.借用构造函数
function Person(name,age) {
this.name = name;
this.age = age;
}
Person.prototype.hello = function() {
console.log('hello');
}
function Student(score, name, age) {
// 借用构造函数
Person.call(this, name, age);
this.score = score
}
var stu = new Student(88, 'peter', 24)
console.log(stu.name) //peter
console.log(stu.score) //88
console.log(stu.age) //24
stu.hello() //报错 stu.hello is not a function
通过借用父类构造函数的方法,也可以实现继承,但是只能继承父类的属性,无法继承父类原型上的方法。
3.组合继承:原型链+借用构造函数(经典继承)
function Person(name,age) {
this.name = name;
this.age = age;
}
Person.prototype.hello = function() {
console.log('hello');
}
function Student(score, name, age) {
//借用构造函数
Person.call(this, name, age);
this.score = score;
}
Student.prototype = new Person() //改变原型指向
var stu = new Student(100,'tom',22)
console.log(stu.name) //tom
console.log(stu.age) //22
console.log(stu.score) //100
stu.hello() //hello
组合继承可以同时继承父类的属性和方法,弥补了原型继承和借用构造函数继承的缺点。
4.拷贝继承
多用于对象的继承(产生相似的对象)
function Person() {}
Person.prototype.name = 'kobe'
Person.prototype.hello = function() {
console.log('hello');
}
var obj = {}
for(var key in Person.prototype) {
obj[key] = Person.prototype[key]
}
console.log(obj.name)
obj.hello() //hello
prototype可以看作一个对象,通过遍历的方式,将父类的prototype的属性赋给一个实例对象,这个实例对象就具有了父类prototype中的属性,通过拷贝的方式实现了继承。
注意:此拷贝方式是浅拷贝,仅仅将父类原型在堆空间的东西复制了一份放在实例对象obj的堆空间中。
5.原型式继承
在没有必要创建构造函数,指向让一个对象和另一个对象保持相似的情况下,可以使用原型式继承,ES5中添加的object.create()方法可以实现。
Object.create()方法接受两个参数,第一个参数为作为原型的对象,第二个参数(可选)为自定义的属性,会替换掉原对象中的同名属性。
//object.create()的实现原理
function create(obj) {
function F() {}
F.prototype = obj
return new F()
}
var person = {
name: 'bob'
arr: [1,2,3]
}
var per = Object.create(person) //也可以是var per = create(person)
per.name = 'tom'
per.arr.push(5)
console.log(per.name) //tom
console.log(per.arr) //[1,2,3,5]
6.寄生式继承
寄生式继承思路与原型式继承紧密相关,创建一个仅用于封装继承过程的函数,该函数内部以某种方式来增强对象。
function create(obj) {
function F() {}
F.prototype = obj
return new F()
}
function createAnother(obj) {
var clone = create(obj)
clone.prototype.hello = function () { //增强对象
console.log('hello')
}
return clone
}
create()方法可以用任何能够返回新对象的函数替代,即作用为返回一个对象的副本。
缺点:不能做到函数复用而降低效率,因为增强对象的方式是固定的。
7.寄生组合继承
通过借用构造函数来继承方法,通过原型链来继承方法,它的基本思路是:不必为了指定子类型的原型而借用父类型的构造函数,只需要父类型原型的一个副本而已。
function create(obj) { //一个返回新对象的方法
function F() {}
F.prototype = obj
return new F()
}
function Father(name) {
this.name = name
this.age = 50
}
Father.prototype.hello = function () {
console.log('this is father')
}
function Son(name, age) {
Father.call(this, name)
this.age = age
}
// 寄生组合继承
function createNew(son, father) { //接受两个参数,子类型的构造函数和父类型的构造函数
var prototype = create(father.prototype) //创建父原型的副本
prototype.constructor = son //补写因重写原型失去的构造器属性
son.prototype = prototype //将创建的副本赋值给子类型的原型,实现继承
}
createNew(Son, Father)
Son.prototype.logAge = function () {
console.log(this.age)
}
var son = new Son('mike', 24)
console.log(son.name) // mike
console.log(son.age) //24
son.logAge() //24
son.hello() // this is father
优点:只调用了一个父类的构造函数,避免了在父类的prototype上创建多余的属性,原型链同时保持不变(有的继承会改变原型链的指向),还能够正常的使用instanceof()和isPrototypeOf()两个方法。是一种比较优秀的继承方法。