【新手向】JS继承代码示例

348 阅读4分钟

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()两个方法。是一种比较优秀的继承方法。