欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JavaScript实现继承方式的整理

程序员文章站 2022-04-13 15:47:47
前言 javascript是作为面向对象的弱类型语言,继承是其非常强大的特性。许多oo语言都支持两种继承方式,就是接口继承和实现继承。接口继承只继承方法签名,而实现继承则继承实际...

前言

javascript是作为面向对象的弱类型语言,继承是其非常强大的特性。许多oo语言都支持两种继承方式,就是接口继承和实现继承。接口继承只继承方法签名,而实现继承则继承实际的方法。因为函数没有签名,所以在ecmascript中无法实现接口继承,只支持实现继承,而实现继承又主要依靠原型链来实现。

为了实现继承,我们先定义一个父类:

function person(){
    this.name = name || "person";
    this.sayname = function (){
        alert(this.name);
    }
}

person.prototype.greet = function(){
    alert("hi,i'm " + this.name + "!");
}

1.原型链继承

重点:将父类的实例作为子类的原型

function boy(){
}

boy.prototype = new person();
boy.prototype.name = "mike";

var person1 = new boy();

console.log(person1.name);    //mike
person1.sayname();            //mike
person1.greet();              //hi,i'm mike!
console.log(person1 instanceof boy);      //true
console.log(person1 instanceof person);   //true

特点:

这是一个很简单纯粹的继承关系,实例是子类的实例,同时也是父类的实例; 当父类添加原型方法或属性的时候,子类能够及时地访问到。

缺点:

原型对象中的引用属性是共享的; 无法实现多继承; 在创建子类实例时,无法向父类的构造函数传递参数; 为子类新增属性和方法时,必须先用实例替换原型后,再定义方法。

注意:在通过原型链实现继承时,不能使用对象字面量创建原型的方法,否则会重写原型链,导致错误。

确定原型和实例的关系:
alert(object.prototype.isprototypeof(person1));   //true
alert(boy.prototype.isprototypeof(person1));      //true
alert(person.prototype.isprototypeof(person1));   //true

使用isprototypeof()方法可以测试,上面三个输出表示只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型。


2.借用构造函数

重点:使用父类的构造函数来增强子类实例,通过使用call()和apply()方法在新创建的对象上执行构造函数,等于是复制父类的实例属性给子类(并没有用到原型)。

function boy(name){
    person.call(this);
    this.name = name || "boy";
}

var person1 = new boy();

console.log(person1.name);    //boy
person1.sayname();            //boy
console.log(person1 instanceof boy);      //true
console.log(person1 instanceof person);   //false

特点:

解决了子类实例共享父类引用属性的问题; 子类实例创建的时候可以传参;

可同时call多个父类对象实现多继承。

缺陷:

子类只能继承父类的实例属性和方法,不能继承原型属性和方法;

实例是子类实例,不是父类实例; 无法实现函数复用,影响性能。


组合继承(最常用)

重点:将原型链和借用构造函数的技术组合到一块,使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。

function boy(){
    person.call(this);
    this.name = name || "boy";
}

boy.prototype = new person();
boy.prototype.constructor = person;   //显式调整constructor属性的指向

var person1 = new boy();
console.log(person1.name);       //boy
person1.sayname();               //boy
console.log(person1 instanceof boy);      //true
console.log(person1 instanceof person);   //true

特点:

既可以继承实例属性和方法,也可以继承原型属性和方法; 既是子类实例,又是父类; 子类实例创建的时候可以传参; 可以实现函数复用。


原型式继承

重点:借助原型可以基于已有的对象创建新对象,同时不必创建自定义类型,并没有使用到严格意义上的构造函数。其思想可以用下面的函数来说明:

function object(o) {
    function f(){}
    f.prototype = o;
    return new f();
}

下面是一个实例:

var person = {
    name:"mike",
    friends:["shelby","court","van"]
};

var anotherperson =new object(person);
anotherperson.name = "greg";
anotherperson.friends.push("rob");

var yetanotherperson =new object(person);
yetanotherperson.name = "linda";
yetanotherperson.friends.push("barbie");

alert(person.friends);   //"shelby","court","van","rob","barbie"

原型式继承的本质就是浅复制,以一个对象为模板来复制出新的对象。


寄生式继承

重点:创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来增强对象,最后再像真正是它做了所有工作一样返回对象。

function createanother(original) {
    var clone =new  object(original);
    clone.sayhi = function () {
    alert("hi");
    };
    return clone;
}

var person = {
    name:"mike",
    friends:["shelby","court","van"]
};

var anotherperson = createanother(person);
anotherperson.sayhi();      //"hi"

寄生式继承就是把原型式继承再次封装,然后在对象上扩展新的方法,再把新对象返回。它不能做到函数复用。


寄生组合式继承

组合继承最大的问题在于无论什么情况,都会调用两次超类型构造函数:一次在创建子类型原型的时候,一次在子类型构造函数内部。寄生组合式继承解决了这个问题。

function boy(name){
    person.call(this);
    this.name = name || "boy";
}

(function(){    // 创建一个没有实例方法的类
    var super = function(){};
    super.prototype = person.prototype;     //将实例作为子类的原型
    boy.prototype = new super();
    boy.prototype.constructor = boy;
})();

var person1 = new boy();
console.log(person1.name);         //boy
person1.sayname();                 //boy
console.log(person1 instanceof boy);          //true
console.log(person1 instanceof person);       //true

寄生组合式继承是最理想的一种继承方式,但是实现比较复杂。


总结:这里整理了javascript实现继承的几种方法,各有优缺点,在学习过程中一定要理解透彻。