【JavaScript】面向对象

类的声明和实例化

声明类有两种方法:

// es5构造函数里面的方法和属性
function Animal(name){
	this.name = name;
}
// es6 类的构造函数,实例化的时候执行,new的时候执行
class Animal2 {
  constructor(name){
  	this.name = name;
  }
}

类的实例化只有一种方式

var a1 = new Animal('shape');
var a2 = new Animal2('cat');

面向对象的三大特征:封装、继承、多态

封装

我们平时所用的方法和类都是一种封装,当我们在项目开发中,遇到一段功能的代码在好多地方重复使用的时候,我们可以把他单独封装成一个功能的方法,这样在我们需要使用的地方直接调用就可以了

封装:封装的优势在于定义只可以在类内部进行对属性的操作,外部无法对这些属性指手画脚,要想修改,也只能通过你定义的封装方法

继承

继承:继承减少了代码的冗余,省略了很多重复代码,开发者可以从父类底层定义所有子类必须有的属性和方法,以达到耦合的目的。

继承有多种实现方式

借助构造函数实现继承

先看代码,Parent1 是父类,Child1 是子类。通过 Parent1.call(this, name) 改变了 this 指向,使子类继承了父类的属性,即 Child1 也有了 name 属性。

/*
	构造函数实现继承
*/
function Parent1(name){
  this.name = name;
}
function Child1(name, age){
  Parent1.call(this, name)
  this.age = age
}
var c1 = new Child1('bobo', 19)
console.log(c1)

运行程序,打印结果:
在这里插入图片描述
但是, 这种方式不能继承父类原型链上的属性,只能继承在父类显式声明的属性 。
看下面的代码,通过Parent1.prototype.say给 Parent1 新增一个 say 方法,那么 Child1 能不能也继承呢?

function Parent1(name){
  this.name = name;
}
Parent1.prototype.say = function(){
  console.log('say hello')
}
function Child1(name, age){
  Parent1.call(this, name)
  this.age = age
}

var p1 = new Parent1('hehe')
var c1 = new Child1('bobo', 19)
console.log(p1)
console.log(c1)

输出结果如下:

Parent1 中有 say 方法,而 Child1 中没有,说明没有继承到。
在这里插入图片描述

借助原型链实现继承

实现原理是将 Child.prototype 赋值为一个新的 Parent 对象,即 Child2.prototype = new Parent2('bob')

function Parent2(name){
  this.name = name;
  this.arr = [1,2,3]
}

function Child2(age){
  this.age = age
}

//重点在这句
Child2.prototype = new Parent2('bob')

c2 = new Child2(20)
c3 = new Child2(22)
console.log('c2', c2)
console.log('c3', c3)

控制台输出如下:

在这里插入图片描述

这种方法也有缺点,看 arr 属性是一个数组,如果创建两个实例对象 c2、c3,因为这两个实例对象的 arr 指向同一个引用,所以改变其中一个的值,另一个也会跟着改变。

我们来看下面的实验:c2.arr 和 c3.arr 值都是 [1,2,3],此时通过 c2.arr.push(4) 给 c2.arr 添加一个元素,c2.arr 变成了 [1,2,3,4],这没有问题。但再看 c3.arr ,也是 [1,2,3,4],这就有问题了,不是我们期望的。
在这里插入图片描述

组合方式优化

// 组合方式优化
function Parent5(name){
  this.name = name
}
function Child5(name, age){
  Parent5.call(this, name)
  this.age = age
}
Child5.prototype = Object.create(Parent5.prototype)
Child5.prototype.constructor = Child5

var c8 = new Child5()
console.log(c8 instanceof Child5, c8 instanceof Parent5)
console.log(c8.constructor)

** Object.create() **方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。即返回一个带着指定的原型对象和属性的新对象。

Child5.prototype 和 Parent5.prototype 不再使用同一个引用,中间用一个新的对象隔离开了。

此时 Child5 还没有 constructor,所以要手动指定一个: Child5.prototype.constructor = Child5

至此,继承的完美解决方案诞生了。

es6继承的书写方法

class Father {
   constructor(name) {
         this._name = name;
     }   //实例方法,通过实例对象调用
     getName() {
         console.log(this._name);
     }
   // 静态方法不会被继承,并且是通过类名去调用的   static hitXiaoMing() {
         console.log("打小明")
     }
 }
 
 class Son extends Father {
     constructor(name, age) {
         //实例化子类的时候把子类的数据传给父类(这里的super必须有,super里的参数是所继承的父类实例化所需要的数据)
         super(name);
         this._age = age;
     } }
 
 var DaMing = new Father('大明');
 Father.hitXiaoMing(); //打小明 DaMing.getName(); //大明 
 var XiaoMing = new Son('小明'15);

 XiaoMing.getName(); //小明

特别提醒:继承会继承父类的实例属性和实例方法,并不会继承静态属性和静态方法,并且静态方法只能通过类名去调用。

多态

多态:多态实现了方法的个性化,不同的子类根据具体状况可以实现不同的方法,光有父类定义的方法不够灵活,遇见特殊状况就捉襟见肘了

多态的具体表现为方法重载和方法重写:

  • 方法重载:重载是指不同的函数使用相同的函数名,但是函数的参数个数或类型不同。调用的时候根据函数的参数来区别不同的函数
  • 方法重写:重写(也叫覆盖)是指在派生类中重新对基类中的虚函数(注意是虚函数)重新实现。即函数名和参数都一样,只是函数的实现体不一样
class Father {
   constructor(name) {
         this._name = name;
     }
   //实例方法,通过实例对象调用
     getName() {
         console.log(this._name);
     }
     work() {
       console.log('我的工作是累死累活,赚钱养家')
     }
   // 静态方法不会被继承,并且是通过类名去调用的
   static hitXiaoMing() {
         console.log("打小明")
     }
 }
 
 class Son extends Father {
     constructor(name, age) {
         //实例化子类的时候把子类的数据传给父类(这里的super必须有,super里的参数是所继承的父类实例化所需要的数据)
         super(name);
         this._age = age;
     } 
     // 方法重写
     work() {
       console.log('我的工作是好好学习,天天向上。')
     }
}
 
 var DaMing = new Father('大明');
 DaMing.work() // 我的工作是累死累活,赚钱养家。
 var XiaoMing = new Son('小明'15);  
 XiaoMing.work(); // 我的工作是好好学习,天天向上。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值