资讯专栏INFORMATION COLUMN

JS继承

Tangpj / 1765人阅读

摘要:继承创建子类型的实例时,不能向超类型的构造函数中传递参数。借用构造函数伪造对象经典继承在子类型构造函数内部调用超类型构造函数。组合继承使用原型链实现对原型属性和方法的继承,而通过构造函数来实现实例属性的继承。

JS继承 原型链 构造函数、原型、实例的关系

每个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针,而实例包含一个指向构造函数的指针。
原型链的构建是通过将一个类型的实例赋值给另一个构造函数的原型实现。

function SuperType(){
        this.property=true;
    }
    SuperType.prototype.getSuperValue=function () {
        return this.property;
    };
    function SubType(){
        this.subproperty=false;
    }
    //继承SuperType
    SubType.prototype=new SuperType();
    SuperType.prototype.getSubValue=function () {
        return this.subproperty;
    }
    var instance=new SubType();
    console.log(instance.getSubValue());  //false
    console.log(instance.getSuperValue());  //true

SubType继承SuperType是通过创建SuperType实例,并将该实例赋给SubType.prototype实现的。本质是重写原型对象,代之以一个新类型的实例。
instance指向SubType的原型,SubType的原型又指向SuperType的原型。要注意的是instance.constructor现在指向的是SuperType,应为SubType的原型指向了SuperType的原型,而该原型对象的constructor属性指向的SuperType。

原型搜索机制

调用instance.getSuperValue()会经历一下三个步骤:
1)搜索实例
2)搜索SubType.prototype
3)搜索SuperType.prototype,最后一步才会找到该方法。在找不到属性或方法的情况下,搜索过程总要一环一环地前行到原型链末端为止。

确定原型与实例的关系

1.instanceof
instance instanceof Object
2.isPrototypeOf
Object.prototype.isPrototypeOf(instance)

定义方法

1.子类型需要覆盖超类型中的某个方法,或需要添加超类型中不存在的某个方法。给原型添加方法的代码要放在替换原型的语句后面。

    function SuperType(){
        this.property=true;
    }
    SuperType.prototype.getSuperValue=function () {
        return this.property;
    };
    function SubType(){
        this.subproperty=false;
    }
    //继承SuperType
    SubType.prototype=new SuperType();
    //添加新方法
    SuperType.prototype.getSubValue=function () {
        return this.subproperty;
    }
    //重写超类型中的方法
    SubType.prototype.getSuperValue=function () {
        return false;
    }
    var instance=new SubType();
    console.log(instance.getSuperValue());

2.通过原型链实现继承时,不能使用对象字面量创建原型方法。这样会重写原型链。

    function SuperType(){
        this.property=true;
    }
    SuperType.prototype.getSuperValue=function () {
        return this.property;
    };
    function SubType(){
        this.subproperty=false;
    }
    //继承SuperType
    SubType.prototype=new SuperType();
    SubType.prototype={
        getSubValue:function () {
            return this.subproperty;
        }
    };
    var instance=new SubType();
    console.log(instance.getSuperValue());  //error

以上代码先把SuperType的实例赋值给原型,紧接着又将原型替换成一个对象字面量。由于现在的原型包含的是一个Object实例,而非SuperType的实例。因此,原来的SuperType和SubType之间的原型链被切断。

原型链的问题

1.包含引用类型的原型属性会被所有实例共享。

    function SuperType(){
        this.nums=[1,2];
    }
    function SubType(){

    }
    //继承SuperType
    SubType.prototype=new SuperType();
    var instance=new SubType();
    instance.nums.push(5);
    console.log(instance.nums);  //[1,2,5]
    var instance1=new SubType();
    instance1.nums.push(0);
    console.log(instance1.nums);  //[1,2,5,0]

2.创建子类型的实例时,不能向超类型的构造函数中传递参数。

借用构造函数(伪造对象/经典继承)

在子类型构造函数内部调用超类型构造函数。

function SuperType(){
    this.nums=[1,2];
}
function SubType() {
    SuperType.call(this);
}
SubType.prototype=new SubType();
var instance=new SubType();
instance.nums.push(3);
console.log(instance.nums);

var instance2=new SubType();
console.log(instance2.nums);

通过使用call()方法(或apply()方法),在新创建的SubType实例的环境下调用了SuperType构造函数。这样就会在新SubType对象上执行SuperType函数中定义的所有对象初始化代码。SubType的每个实例都会具有自己的nums属性的副本。
1.传递参数
子类型构造函数向超类型构造函数传递参数。

function SuperType(name){
    this.name=name;
    this.school=["whu"];
}
function SubType() {
    SuperType.call(this,"张三");
    //实例属性
    this.age=20;
}
var instance=new SubType();
instance.school.push("hhu");
console.log(instance.name);
console.log(instance.age);
console.log(instance.school);
var instance2=new SubType();
console.log(instance2.school);

2.存在的问题
方法都在构造函数中定义,函数无法复用;
在超类型的原型中定义的方法,对于子类型而言是不可见的,那么所有类型都只能使用构造函数模式。

组合继承

使用原型链实现对原型属性和方法的继承,而通过构造函数来实现实例属性的继承。

function SuperType(name){
    this.name=name;
    this.city=["武汉","杭州"];
}
SuperType.prototype.sayName=function () {
    console.log(this.name);
}
function SubType(name,age) {
    //继承属性
    SuperType.call(this,name);
    //实例属性
    this.age=age;
}
//继承方法
    SubType.prototype=new SuperType();
SubType.prototype.constructor=SubType;
SubType.prototype.sayAge=function () {
    console.log(this.age);
}

var instance1=new SubType("chen",18);
instance1.city.push("北京");
console.log(instance1.city);
instance1.sayName();
instance1.sayAge();
var instance2=new SubType("huang",19);
console.log(instance2.city);
instance2.sayName();
instance2.sayAge();
原型式继承
function object(o){
    function F(){}
    F.prototype=o;
    return new F();
}

在object函数内部,先创建一个临时性的构造函数,然后将传入对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例。本质上是object()对传入其中的对象执行了一次浅复制。

var navy={
    name:"海军",
    weapon:["航母","驱逐舰"]
};
function object(o){
    function F() {

    }
    F.prototype=o;
    return new F();
}
var navy1=object(navy);
navy1.name="俄罗斯";
navy1.weapon.push("巡洋舰");
var navy2=object(navy);
navy2.name="美国";
navy2.weapon.push("护卫舰");
console.log(navy.weapon);

ECMAScript5新增Object.create()方法规范了原型式继承,接收两个参数:一个是作用于新对象原型的对象,(可选)二是一个新对象定义额外属性的对象.

var car={
    name:"奔驰",
    weapon:["车轮","发动机"]
};
var car1=Object.create(car,{
    name:{
        value:"宝马"
    }
});
console.log(car1.name);

原型式的问题依然是包含引用类型的属性会所有实例被共享

寄生式继承
    function object(o){
        function F() {

        }
        F.prototype=o;
        return new F();
    }
    function createAnother(original) {
        var clone=object(original);  //通过调用函数创建一个新对象
        clone.sayHi=function () {   //以某种方式增强这个对象
            console.log("hi");
        };
        return clone;   //返回对象
    }
    var person={
        name:"coder",
        ability:["Java","R"]
    };
    var another=createAnother(person);
    another.sayHi();

寄生式的问题依然是不能做到函数复用

寄生组合式继承

组合继承最大的问题就是会调用两次超类型构造函数:一次是创建子类型原型时候,一次是在子类型构造函数内部。

    function SuperType(name){
        this.name=name;
        this.city=["南京","苏州"];
    }
    SuperType.prototype.sayName=function () {
        console.log(this.name);
    }
    function SubType(name,age) {
        SuperType.call(this,name);//第二次调用SuperType
        this.age=age;
    }
    SubType.prototype=new SuperType();  //第一次调用SuperType
    SubType.prototype.constructor=SubType;
    SubType.prototype.sayAge=function () {
        console.log(this.age);
    }

在第一次调用SuperType构造函数时,SubType.prototype会得到两个属性:name和colors;都是SuperType的实例属性,只不过现在位于SubType的原型中.当调用SubType构造函数,又会调用一次SuperType的构造函数,这一次又在新对象上创建了实例属性name和colors.于是这两个属性就屏蔽了原型中的两个同名属性.
寄生组合式继承通过借用构造函数来继承属性,通过原型链的混成形式来继承方法.其基本思想是:不必为了指定子类型的原型而调用超类型的构造函数,所需要的只是超类型原型的一个副本.本质上,就是使用寄生式继承来继承超类型的原型,然后再讲结果指定给子类型的原型.

    function inheritPrototype(subType,superType){
        var prototype=object(superType.prototype);  //创建对象
        prototype.constructor=subType;             //增强对象
        subType.prototype=prototype;               //指定对象
    }

inheritPrototype函数接受两个参数:子类型构造函数、超类型构造函数
1.创建超类型原型的一个副本
2.为创建的副本添加constructor属性,弥补因重写原型而失去的默认的constructor属性.
3.将新创建的对象(即副本)赋值给子类型的原型.

    function SuperType(name){
        this.name=name;
        this.city=["南京","苏州"];
    }
    SuperType.prototype.sayName=function () {
        console.log(this.name);
    }
    function SubType(name,age) {
        SuperType.call(this,name);//第二次调用SuperType
        this.age=age;
    }
    inheritPrototype(SubType,SubType);
    SubType.prototype.sayAge=function () {
        console.log(this.age);
    }

上述只调用了一次SuperType构造函数,并因此避免了在SubType.ptototype上创建不必要的、多余的属性.与此同时,原型链还能保持不变.

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/93627.html

相关文章

  • JS中的继承(上)

    摘要:中的继承上学过或者之类语言的同学应该会对的继承感到很困惑不要问我怎么知道的的继承主要是基于原型的对的原型感兴趣的同学可以了解一下我之前写的中的原型对象相信很多同学也跟我一样刚开始接触的面向对象编程的时候都抱着一种排斥的心态为什么这么 JS中的继承(上) 学过java或者c#之类语言的同学,应该会对js的继承感到很困惑--不要问我怎么知道的,js的继承主要是基于原型(prototype)...

    Fundebug 评论0 收藏0
  • JavaScript继承方式详解

    摘要:可以通过构造函数和原型的方式模拟实现类的功能。原型式继承与类式继承类式继承是在子类型构造函数的内部调用超类型的构造函数。寄生式继承这种继承方式是把原型式工厂模式结合起来,目的是为了封装创建的过程。 js继承的概念 js里常用的如下两种继承方式: 原型链继承(对象间的继承) 类式继承(构造函数间的继承) 由于js不像java那样是真正面向对象的语言,js是基于对象的,它没有类的概念。...

    Yangyang 评论0 收藏0
  • JS面向对象之五 【继承

    摘要:首先为了模拟类创建对象的功能搞出了构造函数。也就是名字肤色肤色这里是继承里的自有属性生命值这里继承的共有属性的方法攻击力兵种美国大兵攻击防御死亡肤色 JS面向对象之五 【继承】 我们已经准备了很多前置知识,包括 原型链,对象和对象之间的关系 this,对象和函数之间的关系 new, 用函数批量创建特定的对象的语法糖 JS面向对象的前世今生 我们说,面向对象是一种写代码的套路。因为如...

    genefy 评论0 收藏0
  • JS基础(对象创建,构造函数、原型、实例之间关系,继承方式)

    摘要:对象创建的三种方式字面量创建方式系统内置构造函数方式自定义构造函数构造函数原型实例之间的关系实例是由构造函数实例化创建的,每个函数在被创建的时候,都会默认有一个对象。 JS 对象创建的三种方式 //字面量创建方式 var person= { name:jack } //系统内置构造函数方式 var person= new Object(); person.name = jack; ...

    PAMPANG 评论0 收藏0
  • JS专题之继承

    摘要:构造函数所以,就有了畸形的继承方式原型链继承三原型链继承改变构造函数的原型对象继承了属性以上例子中,暴露出原型链继承的两个问题包含引用类型数据的原型属性,会被所有实例共享,基本数据类型则不会。 前言 众所周知,JavaScript 中,没有 JAVA 等主流语言类的概念,更没有父子类继承的概念,而是通过原型对象和原型链的方式实现继承。 于是,我们这一篇讲一讲 JS 中的继承(委托)。 ...

    rollback 评论0 收藏0
  • js原型和继承

    摘要:举例说明组合继承组合继承利用原型链借用构造函数的模式解决了原型链继承和类式继承的问题。示例组合式继承是比较常用的一种继承方法,其背后的思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。 对js原型和继承的理解一直处于不懂-懂-不懂-懂-不懂。。。的无限循环之中,本来打算只是简单总结下js继承方式,可看了些网上的资料后,发现又不懂继承了。。。这篇文章只...

    Hujiawei 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<