Javascript的继承方式

面向对象的编程语言有类(class)的概念(也是一种特殊的数据概念),在ES6出来之前,JS还不是面向对象的语言,没有类(class)的概念,但是JS可以模拟面向对象的思想编程,JS中会通过构造函数来模拟类的概念(class)。(注:下面讲的都是ES6之前的语法)

例如我们创建一个简单的构造函数Person:

function Person(){
    //Person的属性
    this.name = "猿猿";
    this.age = 20;
    //Person方法
    this.sayHi = function(){
         console.log("Hello world");
    }
}

var p = new Person();
console.log(p.name);  //猿猿
p.sayHi();  //Hello world

上面创建的Person是构造函数,在js里也是对象,它具有自己的属性和方法。

面向对象的特性:封装、继承、多态

封装:

        把一个值存储在一个变量中

        把重复代码放在一个函数中

        把一系列的属性放在一个对象中

        把一些功能类似的函数放在一个对象中

        也可以理解为包装,把大都数相同的属性和方法组合成一个对象,方便调用

继承:

       首先继承是一种关系,类与类之间的关系,JS中没有类,所以通过构造函数模拟类,然后通过原型来实现继承

       JS中的继承是为了实现数据共享(注:通过继承,父级构造函数中包含的属性和方法,子级构造函数也有

多态:

      一个对象有不同的行为,或者是同一个行为针对不同的对象,产生不同的结果,要想有多态,就要先实现继承

面向对象编程思想:

     根据需求,分析对象,找到对象有什么特征和行为,通过代码的方式来实现需求,

     而对于js要想创建对象,就要定义构造函数,通过构造函数来创建对象,通过对象调用属性和方法,来实现相应的功能及需求

让我们来看个继承的例子:

     比如说

     "人"都有姓名、性别、年龄、吃饭、睡觉这些属性和方法

     程序员这个类除了继承人的属性和方法外,本身的属性和方法还可以有:工资、敲代码、吐槽代码

     先通过构造函数实现两个类,

//人:姓名、性别、年龄、吃饭、睡觉

//程序员:姓名、性别、年龄、吃饭、睡觉、工资、敲代码、吐槽代码

function Person(name, age, sex) {
    this.name = name;
    this.age = age;
    this.sex = sex;
}
Person.prototype.eat = function() {
    console.log(this.name + "吃东西");
}
Person.prototype.sleep = function() {
    console.log(this.name + "睡觉");
}
//实例化对象
var per = new Person("小明", 18, "男");

function Programmers(name, age, sex, wages){
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.wages = wages;
}
Programmers.prototype.eat = function() {
    console.log(this.name + "吃东西");
}
Programmers.prototype.sleep = function() {
    console.log(this.name + "睡觉");
}
Programmers.prototype.codeing = function() {
    console.log(this.name + "敲代码");
}
Programmers.prototype.scold = function() {
    console.log(this.name + "吐槽代码");
}

var pro = new Programmers("小媛", 18, "女", 100); 

  上面代码你会发现,当程序员和人之间没有关系,也就是还没有继承时会有很多重复的不必要的代码出现

   接下来通过原型来实现继承,改变学生原型的指向即可,

1.通过改变原型指向实现继承

//人:姓名、性别、年龄、吃饭、睡觉

//程序员:姓名、性别、年龄、吃饭、睡觉、工资、敲代码、吐槽代码

function Person(name, age, sex) {
    this.name = name;
    this.age = age;
    this.sex = sex;
}
Person.prototype.eat = function() {
    console.log(this.name + "吃东西");
}
Person.prototype.sleep = function() {
    console.log(this.name + "睡觉");
}

function Programmers(wages){
    this.wages = wages;
}
//在这里改变学生原型的指向,实现继承,人和程序员有了联系
Programmers.prototype = new Person("小媛", 18, "男");
Programmers.prototype.codeing = function() {
    console.log(this.name + "敲代码");
}
Programmers.prototype.scold = function() {
    console.log(this.name + "吐槽代码");
}

var pro = new Programmers(100); 
console.log(pro.name);
console.log(pro.age);
console.log(pro.sex);
pro.eat();
pro.sleep();
console.log("----------下面是程序员自己有的属性和方法----------");
console.log(pro.wages);
pro.codeing();
pro.scold();

浏览器输出的结果:

   

浏览器是正常输出结果了,通过原型的方式也实现了继承,那会不会有什么问题呢?

上面我们只创建了一个叫 "小媛" 的程序员,那我们创建多个对象试试,直接在程序员类下创建对象,看下图:


var pro1 = new Programmers(100);
console.log(pro1.name, pro1.age, pro1.sex, pro1.wages);

var pro2 = new Programmers(200);
console.log(pro2.name, pro2.age, pro2.sex, pro2.wages);

var pro3 = new Programmers(300);
console.log(pro3.name, pro3.age, pro3.sex, pro3.wages);

直接看输出结果:

看结果会发现虽然我们创建了三个程序员,但除了他们的"工资"属性不同外,其他的属性都是相同的,可能大家已经

发现问题所在,那就是我们在改变"Programmers"原型指向时,给实例化'new Person("小媛", 18, "男")'传了固定的值,

所以前面的属性都是相同的。那有没有方法可以改变我们每一个对象的属性呢?答案是肯定的,继续看图:

var pro1 = new Programmers(100);
console.log(pro1.name, pro1.age, pro1.sex, pro1.wages);

var pro2 = new Programmers(200);
pro2.name = "小源";
console.log(pro2.name, pro2.age, pro2.sex, pro2.wages);

var pro3 = new Programmers(300);
pro3.name = "小猿";
console.log(pro3.name, pro3.age, pro3.sex, pro3.wages);

可以看到改变了各自对象的name属性后,总算是可以更好的分辨三个程序员了。大家可以尝试通过每个对象

改变下父类的方法。

通过上面的分析我们知道通过原型继承是存在几点缺陷的:

       1.通过改变原型指向实现的继承,直接初始化了属性,继承过来的属性值都是一样的了。

       2.实例化的子类无法给父类传参,要想改变对象属性,只能重新调用进行重新赋值。

虽然可以通过重新赋值的方式改变属性,但如果对象的属性太多了,每个对象都要改属性,那不无疑又增加了代码量吗,

所以接下我们要讲的就是——通过父类的构造函数实现继承:

通过构造函数实现继承是不需要改变原型的指向,直接调用父级的构造函数的方式来为属性赋值,在js里也称为冒充继承

2.构造函数实现继承

//注意,这里因为调用的call函数,传入了Person的属性,所以Programmers也要传入相应的属性
function Programmers(name, age, sex, wages){

    //通过构造函数实现继承
    /*
      这里调用函数call()意思是把Person当做一个函数调用并且改变Person的this指向,
      里面传入的第一个参数this在这里用来表示Programmers的实例对象。
   */

    Person.call(this, name, age, sex);
    this.wages = wages;
}
Programmers.prototype.codeing = function() {
    console.log(this.name + "敲代码");
}
Programmers.prototype.scold = function() {
    console.log(this.name + "吐槽代码");
}

var pro1 = new Programmers("小媛", 18, "男", 100);
console.log(pro1.name, pro1.age, pro1.sex, pro1.wages);
var pro2 = new Programmers("小源", 20, "女", 200);
console.log(pro2.name, pro2.age, pro2.sex, pro2.wages);
var pro3 = new Programmers("小猿", 1024, "妖", 130);
console.log(pro3.name, pro3.age, pro3.sex, pro3.wages);

看输出结果:

看结果我们是通过构造函数的方式解决了原型继承存在的问题,但构造函数真的很好的实现继承了吗?

让我们来调用父类的方法看下:

var pro1 = new Programmers("小媛", 18, "男", 100);
console.log(pro1.name, pro1.age, pro1.sex, pro1.wages);
pro1.sleep();
var pro2 = new Programmers("小源", 20, "女", 200);
console.log(pro2.name, pro2.age, pro2.sex, pro2.wages);
var pro3 = new Programmers("小猿", 1024, "妖", 130);
console.log(pro3.name, pro3.age, pro3.sex, pro3.wages);

控制台报错了,方法没有定义,也就是说,我们的子类并没有继承父类的方法,构造函数继承还是存在问题的。。

好吧,前面我们已经讲了两种继承方式,通过原型和构造函数的继承都有各自的缺陷,那接下我们要讲的继承也是

最后一种继承——组合继承:顾名思义,就是把原型和构造函数组合一起实现继承。

3.组合继承

来看终极版:


function Person(name, age, sex) {
    this.name = name;
    this.age = age;
    this.sex = sex;
}
Person.prototype.eat = function() {
    console.log(this.name + "吃东西");
}
Person.prototype.sleep = function() {
    console.log(this.name + "睡觉");
}

function Programmers(name, age, sex, wages){
    //构造函数继承
    Person.call(this, name, age, sex);
    this.wages = wages;
}
//原型继承
//注:现在new Person()可以不需要传值了
Programmers.prototype = new Person();
Programmers.prototype.codeing = function() {
    console.log(this.name + "敲代码");
}
Programmers.prototype.scold = function() {
    console.log(this.name + "吐槽代码");
}

var pro1 = new Programmers("小媛", 18, "男", 100);
console.log(pro1.name, pro1.age, pro1.sex, pro1.wages);
pro1.sleep();
var pro2 = new Programmers("小源", 20, "女", 200);
console.log(pro2.name, pro2.age, pro2.sex, pro2.wages);
pro2.sleep();
var pro3 = new Programmers("小猿", 1024, "妖", 130);
console.log(pro3.name, pro3.age, pro3.sex, pro3.wages);
pro3.sleep();

就这样,我们很好的继承了父类的属性还有方法。[么么哒(づ ̄ 3 ̄)づ]

 

总结:

     1.面向对象的特性:封装、继承、多态

     2.继承为类与类之间的关系,面向对象的语言的继承是为了多态服务的

     3.js不是面向对象的语言,但是可以用构造函数模拟面向对象,模拟继承,为了节省内存空间

     4.继承的方式:

            1)通过改变原型指向实现继承,2)构造函数实现继承,3)组合继承:原型继承+构造函数继承

     5.组合继承可以很好的继承父类的属性和方法。

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值