类与构造函数(继承)

131 阅读6分钟

两大编程思想

  1. 面向过程
    面向过程

POP(Process-oriented programming)面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候在一个个一次调用就可以了
优点:性能比面向对象搞,适合根硬件联系很紧密的东西,比如单片机。
缺点:没有面向对象易维护、易复用、易扩展。
2. 面向对象

面向对象
OOP(Object Oriented Programming)面向对象是把事务分解成一个个对象,然后由对象之间分工与合作.(面向对象的特征:封装性,继承性,多态性)
优点:易维护、易复用、易扩展,由于面向对象有封装,继承,多态性的特性,可以设计出低耦合的系统,使系统更灵活,更加易于维护.
缺点:性能比面向过程低。

类和对象

面向对象的思维特点:
1.抽取(抽象)对象共用的属性和行为组织(封装)成一个类(模板)
2.对类进行实例化,获取类的对象。

对象是由属性和方法组成的:

属性:事物的特征,在对象中用属性来表示。
方法:事物的行为,在对象中用方法来表示。

在ES6中增加了类的概念,可以使用CLASS关键字声明一个类,之后以这个类来实例化对象。
类抽象了对象的公共部分,它泛指某一大类。
对象特指某一个,通过类实例化一个具体的对象。

//创建类
class Person{
    constructor(name){
        this.name = name
    }
    sing(song){
        console.log(this.name+'唱:'+song)
    }
}
let xiaoming = new Person('小明');
console.log(xiaoming.name)
xiaoming.sing('两只老虎')
//1.通过class关键字创建类,类名首字母大写比较规范
//2.类里面有个constructor函数,可以接受传递过来的参数,同时返回实例对象
//3.constructor函数,只要new生成实例,就会自动调用这个函数,如果我们不写这个函数类也会自动生成这个函数。
//4.生成实例new 不能省略。
//5.最后注意语法规范,创建类,类名后面不要加小括号,生成实例 类名后面加小括号,构造函数不需要加function
//6.我们类里面所有的函数不需要写function,多个函数不需要加逗号分割

类的继承

子类可以继承父类的属性和方法

class Father{
    constructor(x,y){
        this.x = x
        this.y = y
    }
    sum(){
        console.log(`${this.x}+${this.y}=${this.x+this.y}`)
    }
}
class Son extends Father{
    constructor(x,y){
        super(x,y);//调用了父类中的构造函数
    }
    minus(s1,s2){
        console.log(`${s1}-${s2}=${s1-s2}`)
    }
}
//super关键字用于访问和调用对象父类上的函数,可以调用父类的构造函数,也可以调用父类的普通函数
class Father{
    say(){
        return '我是爸爸'
    }
}
class Son extends Father{
    say(){
       // console.log('我是儿子')
       console.log(super.say()+'的儿子')
       //super.say()就是调用父类中的普通函数say()
    }
}
var son = new Son();
son.say()
//1.继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先输入子类。如果子类没有就去父类找有没有这个方法,如果有,就执行父类这个方法,就近原则。

继承父类同时子类创建方法

class Star{
    constructor(name,age){
    //constructor里面的this指向的是创建的实例对象
        this.name = name
        this.age = age
    }
    sing(){
        console.log(`${this.name},今年${this.age}`)
    }
}
class Star2 extends Star{
    constructor(name,age,like){
        super(name,age)
        this.name = name
        this.age = age
        this.like = like
    }
    action(){
    //函数里的this 指向的是实例对象 因为实例对象调用了这个函数
        console.log(`${this.name},今年${this.age}岁,喜欢${this.like}`)
    }
}
//1.在ES6中没有变量提升,所以必须先定义类,才能通过实例化对象
//2.类里面的共有属性和方法一定要加this使用
//3.constructor里面的this指向实例对象,方法里面的this指向这个方法的调用者

构造函数和原型

概述
1.在典型的OOP的语言中,都存在类的概念,类就是对象的模板,对象就是类的实例,但在ES6之前,JS并没有引入类的概念。

ES6,全称ECMAcript6.0,2015.06发版,但是浏览器的javascript是ES5版本,大多数高版本的浏览器也支持ES6,不过只实现了ES6的部分特性和功能.

在ES6之前,对象不是基于类创建的,而是用一种构建函数的特殊函数来定义对象和它们的特征。
创建对象可以通过以下三种方式:

  1. 对象字面量。var obj = {};
  2. var obj1 = new Object()
  3. 自定义构造函数.
//构造函数中的属性和方法我们称为成员,成员可以添加
function Star(name,age){
    this.name = name
    this.age = age
    this.sing = function(){
        console.log(`我会唱歌,我叫${this.name},${this.age}岁`)
    }
}
let liu = new Star('刘德华',18)
//实例成员就是构造函数内部通过this添加的成员 name age sing 就是实例成员
console.log(liu.name)//实例成员只能通过实例化的对象来访问

console.log(Star.name)//不可以通过构造函数来访问实例成员

star.sex = '男'//静态成员 在构造函数本身上添加的成员

console.log(Star.sex)//sex就是静态成员,只能通过构造函数来访问
console.log(liu.sex)//不能通过对象来访问

liu.sing()
//构造函数用于创建某一类对象,其首字母要大写。
//构造函数要和new 一起使用才有意义.

new在执行是会做四件事:

  1. 在内存中创建一个新对象
  2. 让this指向这个新对象
  3. 执行构造函数里面的代码,给这个新对象添加属性和方法
  4. 返回这个新对象(所以构造函数里面不需要return)

prototype

构造函数通过原型分配的函数是所有对象所共享的
每一个构造函数都有一个prototype属性,指向另一个对象,注意这个prototype就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。

function Star(name,age){
    this.name = name
    this.age = age
}
Star.prototype.sing  = function(){
    console.log(`我会唱歌,我叫${this.name},${this.age}岁`)
}
//一般情况下我们公共属性定义到构造函数里面,公共的方法我们放到原型对象身上
//方法查找规则,就近原则先看实例对象自身再看其原型有没有,没有就继续向上找

对象都会有一个属性__porto__指向构造函数的prototype原型对象,之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有__proto__原型的存在。

__proto__对象原型和原型对象prototype是等价的

__proto__对象原型的意义就在于为对象的查找机制提供一个方向,或者是哦一条线路,但是它是一个非标准的属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象prototype.

constructor 构造函数

对象原型(proto)和构造函数(prototype)原型对象里面都有一个属性constructor属性,constructor我们称为构造函数,因为它指回构造函数本身。
constructor主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数

Star.prototype = {//用点的方式没问题,但赋值一个对象的方式会覆盖掉原来的constructor属性。
    constructor:Star,//手动添加constructor重新指回Star
    sing:function(){
        console.log('我会唱歌')
    },
    movie:function(){
        console.log('我会演电影')
    }
}

原型对象
js原型公式
对象.proto === 其构造函数.prototype
根公理
Object.prototype是所有对象的直接原型或者间接原型
任何函数.proto === Function.prototype

借用构造函数继承父类属性

在Es6之前并没有提供给我们extends继承,我们可以通过构造函数+原型对象模拟实现继承,被称为组合继承.

//call用法
function fn(x,y){
    console.log('我想喝手磨咖啡')
    console.log(this)
    return x+y
}
var o = {
    name:'andy'
}
fn.call(o,5,6)//调用这个函数,并且修改函数运行时的this指向o

例1

//核心原理:通过call()把父类型的this指向子类型的this,这样就可以实现子类型继承父类型的属性。
//借用父构造函数继承属性
//1.父构造函数
function Father(name,age){
    this.name = name
    this.age = age
}
Father.prototype.money = function(){
    console.log(100000)
}
//子构造函数
function Son(name,age,score){
    Father.call(this,name,age)//将this传给father,就可以用父类里面的属性了
    this.score = score
}
Son.prototype = new Father//将Son的原型改为Father的实例
//如果利用原型对象的形式修改类原型对象,别忘了利用constructor指回原来的构造函数
Son.prototype.constructor = Son
let son1 = new Son('小可爱',18)
son1.money()
console.log(son1)

类的本质

//类的本质其实还是一个函数,我们也可以简单地认为 类就是 构造函数的另一种写法
1.class本质还是function
2.类的所有方法都定义在类的prototype属性上
3.类创建的实例,里面也有__proto__指向类的prototype原型对象
4.所以ES6的类它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰,更像面向对象编程的语法而已。
5.所以ES6的类就是语法糖。