javaScript中的面向对象编程oop

260 阅读5分钟

一、了解面向对象

1.定义

把事务分解成一个个对象,然后对象之间分工合作

2.使用场景

适合多人合作的大型项目

3.优点

可以设计出低耦合的系统,使系统更加灵活、代码可复用、容易维护

4.特性

封装、继承、多态

5.特点

  • 抽取对象共用的属性金额行为组织封装成一个类

  • 对类进行实例化、对象由属性(事务的特征)与方法(事务的行为)组成获取类的对象

    对象由属性(事务的特征)与方法(事务的行为)组成

二、ES6中的类与对象

1.类class

使用class关键字声明一个类,之后以这个类实例化对象

类抽象了对象的公共部分,它泛指某一大类,类是对象的集合。

对象指通过对类实例化的一个具体的对象

类是对象的抽象,对象是类的实例

2.constructor构造函数

constructor()方法是类的构造函数,用于传递参数,返回实例对象,通过new命令生成对象实例时,自动调用该方法。

举例

公司员工这个集合是一个类,每个员工都是一个对象,其中姓名、工号等是属性,工作、休息等是方法。

代码实现:

// 创建类(类名首字母大写)
class Employee {
    // 类的共有属性放在constructor中
    constructor(name) { // constructor函数可以接收传递过来的参数,同时返回实例对象
        this.name = name
    } // 函数、方法之间不需要用逗号分隔
    work() {
        console.log(this.name + '在工作')
    }
}
​
// 创建实例(用new实例化出一个对象)
var xWang = new Employee('小王')
​
console.log(xWang) // Employee { name: '小王' }
xWang.work() // 小王在工作

3.类的继承

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

class Father {
    // 父类
    constructor(x, y) {
        this.x = x;
        this.y = y
    }
    sum() {
        console.log(this.x + this.y)
    }
}
class Son extends Father {
    // 子类继承父类
    constructor(x, y) {
        super(x, y) // 调用了父类中的构造函数
    }
}
​
var son = new Son(1, 2)
son.sum() // 3
  • extends 关键字
  • super 关键字:用于访问和调用对象父类上的函数,可以调用父类的构造函数,也可以调用父类的普通函数。

继承中使用就近原则来查找一个属性或方法,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的,如果子类中没有,就去查找父类中有没有这个方法,如果有,就执行父类的这个方法。

class Father {
    say() {
        return '我是爸爸'
    }
}
​
class Son {
    say() {
        console.log('我是儿子')
    }
}
​
var son = new Son()
son.say()  // 我是儿子
class Father {
    say() {
        return '我是爸爸'
    }
}
​
class Son {
    say() {
        console.log(super.say() + '的儿子')
    }
}
​
var son = new Son()
son.say()  // 我是爸爸的儿子

super必须在子类this之前调用

注意:

  • es6中类没有变量提升,所以必须先定义类,才能通过类实例化对象

  • 类里面的共有属性和方法一定要加this使用

  • 类中this的指向问题

    • constructor中的this指向的是创建的实例对象
    • 方法中的this指向这个方法的调用者

三、ES5中创建对象

在ES6之前,没有类的概念,所以是通过构造函数来定义对象和它们的特征。

1.构造函数

构造函数:一种特殊的函数,主要用于初始化对象,即为对象成员变量赋初始值,它总与new一起使用,我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数中。

(1)利用new Object()创建

var obj = new Object()

(2)利用对象字面量创建

var obj = {}

(3)利用构造函数创建对象

function Employee(name, number) {
    this.name = name
    this.number = number
    this.work = function() {
        console.log('在工作')
    }
}
​
var xWang = new Employee('小王', '1111')
console.log(xWang) // {"name":"小王","number":"1111"}

new在执行时会:

  • 在内存中创建一个新的空对象
  • 让this指向这个新的对象
  • 执行构造函数中的代码,给这个新对象添加属性和方法
  • 返回这个新对象(构造函数中不需要return)

构造函数中的属性和方法都称为成员

  • 实例成员: 构造函数内部通过this添加的成员,实例成员只能通过实例化的对象来访问。
  • 静态成员: 在构造函数上添加的成员,静态成员只能通过构造函数来访问
// 例:
Employee.sex = '男'
​
console.log(Employee.sex) // 男
console.log(xWang.sex) // undefiend

构造函数的问题:存在内存空间的问题(属性属于简单式类型可以直接赋值,方法属于复杂类型,在new一个对象时,复杂类型会再开辟一个内存空间来存放复杂类型function() {})

2.构造函数原型 prototype

构造函数通过原型分配的函数时所有对象共享的。

每一个构造函数都有一个prototype属性(原型对象),prototype中所有的属性和方法,都会被构造函数所拥有。

公共属性定义到构造函数里面,公共方法放到原型对象上(不必开辟新的内存空间,所有的实例都可以使用这个方法)

// 代码实现
function Employee(name, number) {
    this.name = name
    this.number = number
}
Employee.prototype.work = function() {
    console.log('在工作')
}
​
var xWang = new Employee('小王', '1111')
xWang.work() // 在工作

3.对象原型_proto_

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

4.construct 构造函数

对象原型_proto_和构造函数原型对象prototype中都有constructor属性。

function Employee(name, number) {
    this.name = name
    this.number = number
}
Employee.prototype = {
    // 在这里是直接覆盖了原本的prototype对象,所以必须手动添加constructor属性来指向原本的构造函数。
    constructor: Employee,
    work: function() {
        console.log('在工作')
    },
    rest: function() {
        console.log('在休息')
    }
}
​
var xWang = new Employee('小王', '1111')
​