理解JS构造函数继承

对象之间的继承有以下几个方法:

用call和apply(不推荐单独使用,定义在prototype中的属性和方法不能继承)

prototype模式(注意prototype需要先继承后定义)

传统prototype模式(推荐,关键点是Child.prototype = new Parent.prototype以及重新改变Child.prototype.constructor)

利用空对象间接继承(只继承prototype中的属性,关键点在于利用一个空的构造函数当中介)

拷贝继承(不推荐单独使用,for循环逐一拷贝)

以上推荐使用传统prototype模式以及call和apply与拷贝继承相配合的模式

用call和apply
function Chinese() {
    this.nationality = "Chinese";
}
function Person(name, age) {
    Chinese.apply(this); //这里改变了Chinese中this的指向
    this.name = name;
    this.age = age;
}
var p1 = new Person("Oli", 18);
console.log(p1.nationality); //Chinese
传统prototype模式
function Chinese() {
    this.nationality = "Chinese";
}
function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype = new Chinese(); //这里因为在prototype中使用了new,则会指向Chinese
console.log(Person.prototype.constructor); //Chinese(){}
Person.prototype.constructor = Person; //这里需要把constructor构造函数重新改为Person
console.log(Person.prototype.constructor); //Person(){}
var p1 = new Person("Oli", 18);
console.log(p1.nationality); //Chinese

需要注意的是:在继承中,如果替换了prototype,那么新的prototype必须修改constructor属性,将这个属性指回到原来的构造函数。

利用空对象间接继承
function Chinese() {}
Chinese.prototype.nationality = "Chinese";

function Person(name, age) {
    this.name = name;
    this.age = age;
}

function F(){}; //空对象几乎不占用内存
F.prototype = Chinese.prototype;
Person.prototype = new F();
Person.prototype.constructor = Person;

Person.prototype.sayName = function() { //Person的prototype中的方法和属性需要在继承之后定义
    console.log(this.name);
};

var p1 = new Person("Oli", 18);
console.log(p1.nationality); //Chinese
p1.sayName(); //Oli
可以将该方法定义为函数:
function extend(Child, Parent) {
    var F = function() {};
    F.prototype = Parent.prototype;
    Child.prototype = new F();
    Child.prototype.constructor = Child;
    Child.uber = Parent.prototype; //用于访问父对象的prototype,可用可不用
}

举例:

function extend(Child, Parent) {
    var F = function() {};
    F.prototype = Parent.prototype;
    Child.prototype = new F();
    Child.prototype.constructor = Child;
    Child.uber = Parent.prototype;
}

function Chinese() {}
Chinese.prototype.nationality = "Chinese";

function Person(name, age) {
    this.name = name;
    this.age = age;
}

extend(Person, Chinese);

Person.prototype.sayName = function() {
    console.log(this.name);
};

var p1 = new Person("Oli", 18);
console.log(p1.nationality); //Chinese
p1.sayName(); //Oli
拷贝继承

使用下面的函数逐一将prototype的属性和函数拷贝到对象中:

function extend(Child, Parent) {    
    var p = Parent.prototype;    
    var c = Child.prototype;    
    for (var i in p) {      
        c[i] = p[i];      
    }    
    c.uber = p;  
}
不需要先继承后定义

传统prototype模式继承(例子)

function Parent() {
    this.name = "thisIsName";
}
Parent.prototype.sayName = function() {
    return this.name;
};

function Child() {
    this.age = "thisIsAge";
}

Child.prototype = new Parent();
Child.prototype.constructor = Child;

Child.prototype.sayAge = function() {
    return this.age;
};

var c = new Child();
console.log(c.name);
console.log(c.age);
console.log(c.sayName());
console.log(c.sayAge());
call和apply与拷贝继承相配合(例子)
function extend(C, P) {
    var p = P.prototype;
    var c = C.prototype;
    for(var i in p){
        c[i] = p[i];
    }
    c.uber = p;
}

function Parent() {
    this.name = "thisIsName";
}
Parent.prototype.sayName = function() {
    return this.name;
};

function Child() {
    Parent.apply(this); //继承构造函数内的属性和方法
    this.age = "thisIsAge";
}

Child.prototype.sayAge = function() {
    return this.age;
};

extend(Child, Parent); //不需要先继承后定义

var c = new Child();
console.log(c.name);
console.log(c.age);
console.log(c.sayName());
console.log(c.sayAge());