JavaScript-继承

1 继承的基本概念
继承是指一个对象直接使用另一对象的属性和方法
js里常用的如下三种继承方式:
通过原型链方式实现继承(对象间的继承) 
类式继承(构造函数间的继承) 
组合继承


由于js不像Java那样是真正面向对象的语言,js是基于对象的,它没有类的概念。所以,要想实现继承,可以用js的原型prototype机制或者用apply和call方法去实现。

2 程序中的继承方法

1 原型继承
    /**
     * 优点:
     * 实例是父类的实例也是子类的实例
     * 父类新增原型方法或者原型属性 子类都能访问到
     * 简单 易于实现
     * 缺点:
     * 无法实现多继承
     * 来自原型对象的引用属性和实例是所有子类共享的
     * 创建子类实例的时候,无法向父构造函数传参
     * 可以在Cat构造函数中,为Cat实例增加实例属性。如果要新增原型属性和方法,则必须放在new Animal()这样的语句之后执行。
     * **/
    function Animal(name) {
        this.name=name;//属性
        this.sleep=function () {//实例方法
            console.log(this.name+'正在睡觉')
        }
    }
    //原型方法
    Animal.prototype.eat=function (food) {
        console.log(this.name+'正在吃'+food)
    };
    //原型链继承---核心:将父类的实例作为子类的原型
    function Cat() {
    }
    Cat.prototype=new Animal('小花猫');
    var cat=new Cat();
    console.log(cat);
    //  cat._proto_====>Animal 可以使用Animal里面的name属性 sleep方法
    //  也可以使用 cat._proto_._proto_的eat方法 即animal的原型方法
    console.log(cat.name);
    cat.eat('鱼');
    cat.sleep();
2 构造函数继承(类式继承)
    /**
     * 构造继承 相当于复制父类的实例给子类 (没有用到原型)
     * 只能使用父类的属性和方法 不能继承原型属性和方法 所以cat不能使用eat方法
     * **/
    function Animal(name) {
        this.name = name;//属性
        this.sleep = function () {//实例方法
            console.log(this.name + '正在睡觉!');
        }
    }
    // 原型方法
    Animal.prototype.eat = function (food) {
        console.log(this.name + '正在吃:' + food);
    };
    function Cat(name) {
        Animal.call(this, name);//Cat就可以使用Animal的
        // this.name=name
    }
    var cat = new Cat('小黑猫');
    console.log(cat);//只能使用Animal的属性和实例方法 不能使用原型的方法
    //cat的_proto_(指向的是一个函数)是cat  构造器是Cat
    console.log(cat.name);
    cat.sleep();
    cat.eat('鱼'); //不能使用原型方法,所以此处会报错
3 组合继承
    function Animal(name) {
        this.name = name;
        this.sleep = function () {
            console.log(this.name + "正在睡觉");
        };
    }

    Animal.prototype.eat = function (food) {
        console.log(this.name + "正在吃" + food);
    };

    function Cat(name) {
        //借用构造函数继承属性成员
        Animal.call(this, name)
    }

    //修改Cat原型的指向
    Cat.prototype = new Animal();
    Cat.prototype.show = function () {//可以在子类的原型上加方法 不影响父类
        console.log("玩的很开心");
    };
    var cat = new Cat('小花猫');
    console.log(cat);//cat可以直接使用animal的属性和实例方法
    cat.sleep();
    cat.eat('鱼');
    cat.show();

3 Call()、 apply()、bind()介绍

函数内 this 指向的不同场景

函数的调用方式决定了 this 指向的不同:

调用方式非严格模式this指向
普通函数调用window
构造函数调用实例对象
对象方法调用该方法所属对象
事件绑定方法绑定事件对象
定时器函数window
call的用法

call()方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

语法格式:

函数.call(thisArg, arg1, arg2, ...);
              
thisArg
可选的。在 function 函数运行时使用的 this 值。
如果指定了 null 或者 undefined 则内部 this 指向 window
arg1, arg2, ...
指定的参数列表。

描述:
call() 允许为不同的对象分配和调用属于一个对象的函数/方法。
call() 提供新的 this 值给当前调用的函数/方法。你可以使用 call 来实现继承:写一个方法,然后让另外一个新的对象来继承它(而不是在新对象中再写一次这个方法)。

apply的用法

apply() 方法调用一个具有给定this值的函数,以及作为一个数组提供的参数。

语法格式:

函数.apply(thisArg, [argsArray])

thisArg
必选的。在 func 函数运行时使用的 this 值。
argsArray
可选的。一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。如果该参数的值为 null 或  undefined,则表示不需要传入任何参数。

apply()call() 非常相似,不同之处在于提供参数的方式。
apply() 使用参数数组而不是一组参数列表。

bind()的用法

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

语法格式:

函数.bind(thisArg[, arg1[, arg2[, ...]]])

thisArg
调用绑定函数时作为 this 参数传递给目标函数的值。 
arg1, arg2, ...
当目标函数被调用时,被预置入绑定函数的参数列表中的参数。

4 、ES6 中的继承

class Parent {
    constructor(x,y){
        this.x = x;
        this.y = y;
    }
    toString(){

    }
}

class Child extends Parent{
    constructor(x,y,color){
        super(x,y);
        this.color = color;
    }
    toString(){
        return super.toString();
    }
}
/*
* 通过extends关键字继承了Parent类所有的属性和方法。
* 子类的constructor方法 和 toString方法中都出现了super关键字,
* 在这里表示父类的构造函数,用来新建父类的this对象。
* 子类必须在constructor方法中调用super方法,因为子类的this对象必须通过父类的构造函数完成创建,
* 并且得到与父类同样的实例属性和方法。如果不调用,子类就得不到this对象,就不能进行二次加工。
* 要注意的一个地方是,在子类的构造函数中,只有调用super才可以使用this关键字,
* 所以this关键字要放到super关键字之后,
* 是因为子类实例的构建,基于父类实例,只有super方法能调用父类实例。
*/
/*
* ES5的继承实质是先创建子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))
* ES6的继承实质是先将父类实例对象的属性和方法加到this上面(所以需先调用super方法),然后再用子类构造函数修改this
*/

class Child extends Parent{

}
//等同于
class Child extends Parent{
    constructor(...args){
        super(...args);
    }
}
/*
* 如果子类没有定义constructor方法,这个方法会被默认添加,
* 也就是说不管有没有显示的定义,子类都有constructor方法。
*/
class Parent {

}

class Child extends Parent{
    constructor(){
        super();
    }
}

/*
* super这个关键字既可以当做函数使用,也可以当做对象使用
* 作为函数调用时,代表父类的构造函数,子类构造函数必须执行一次super函数
* super虽然代表了父类Parent的构造函数,但是返回的是子类Child的实例
* 即super内部的this指的是Child,super在这里就相当于:
* Parent.prototype.constructor.call(this)
* super作为函数使用时,super()只能用在子类的构造函数中*/

class Parent {
    outPut(){
        console.log(1)
    }
}

class Child extends Parent{
    constructor(){
        super();
        console.log(super.outPut())
    }
}

/*
* super作为对象时,在普通方法中指向父类的原型对象,静态方法中指向父类
* super在普通方法中代表Parent.prototype,所以super.outPut()就相当于
* Parent.prototype.outPut()
* 注意,父类实例上的方法或属性是无法通过super调用的*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会撸串的傻狍子

感谢您的认可

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值