第十七章 面向对象编程

文章目录

  • 一、什么是面向对象
  • 二、面向过程和面向对象
  • 三、对象的创建
  • 四、原型
  • 五、总结

一、什么是面向对象

  1. 面向对象编程(Object-Oriented Programming, OOP)是一种软件开发方法,它将数据和方法封装在一起,形成一个对象,通过对象之间的交互实现程序的功能。
  2. 每个对象都有自己的状态和行为,对象之间通过调用彼此的方法来实现交互。
  3. 这种编程范式强调代码的可重用性、可维护性和灵活性,使得程序开发更加高效和可靠。
  4. 同时,面向对象编程也是一种更加自然的编程方式,因为它更接近我们日常生活中的实体和行为。

二、面向过程和面向对象

  1. 面向过程: 关注过程的编程模式
    • 在面向过程编程中,数据和方法是分离的,程序主要由一系列函数组成,每个函数都是一个独立的模块,它们之间通过参数和返回值进行交互。
    • 程序的执行顺序是由函数的调用顺序决定的。
    • 优点:性能高,步骤联系密切
    • 缺点:不好维护,不方便复用
  2. 面向对象:关注对象的编程模式
    • 在面向对象编程中,数据和方法是封装在一起的,程序主要由一组对象组成,每个对象都有自己的属性和方法。对象之间通过方法调用和消息传递进行交互。
    • 程序的执行顺序是由对象之间的交互决定的。
    • 优点:易维护、可复用、可扩展、灵活性高
    • 缺点:性能没有面向过程高
  3. 如:吃面
    • 面向过程
      • 用多少面
      • 用多少水
      • 怎么和面
      • 怎么切面条
      • 做开水
      • 煮面
      • 吃面
    • 面向对象
      • 找到一个面馆
      • 叫一碗面
      • 等着吃
  4. 面向对象的核心思想:封装

三、对象的创建

  1. 单例模式:单个实例,单个对象,每个对象都是独立的,无复用关系
    • const obj = {}
    • const obj = new Object()
const o1 = {
  name:"张三",
  age: 18,
  sex:"男"
}

const o2 = {
  name:"李四",
  age:19,
  sex:"女"
}
  1. 工厂模式:在单例模式的基础上抽象出相同属性和方法,每次调用工厂,都会产生同类的不同对象
function createObj(n, a, s){
  const obj = {};
  obj.name = n;
  obj.age = a;
  obj.sex = s;
  return obj;
}

const o1 = createObj("张三", 18, "男");
const o2 = createObj("李四", 19, "女");
  • 工厂函数需要经历三个步骤
    • 原料:创建对象
    • 加工:添加成员
    • 出厂:返回对象
  1. 内置工厂模式(构造自定义函数)
    • 使用new关键字执行函数
    • new的原理(new执行函数时做了什么)******
      1. 创建一个空对象
      2. 将该对象的原型链属性__proto__,指向函数的原型对象属性prototype
      3. 将函数的this指向该对象
      4. 执行该函数内所有代码
      5. 检测函数是否主动返回对象,如无,则返回this
function Person(name, age, sex) {
  this.name = name;
  this.age = age;
  this.sex = sex;
  this.sayHello = function(){
    console.log(`hello, 我是${this.name}`)
  }
}

const p1 = new Person('小王', 18, "女");
const p2 = new Person('小赵', 19, "男");
  • 构造函数也是函数,但一般情况下必须通过 new 执行,否则可能会造成大量全局变量的产生(注意this指向)
  • 为了与普通函数区分,构造函数的首字母建议大写
  • new关键字有执行函数功能,所以使用new执行函数时,如果不需要传参,可以省略小括号
  1. 在构造函数创建对象过程中,如果多次new同一个构造函数,每次构造函数的执行都是独立的,此时会在内存中产生了多个对象,这本来是一种正常现象,每个对象确实应该独占一份空间。
    • 但此时每个对象中都会具有一个同名方法,他们功能一致,来自于同一个模板,完全没有必要在内存中重复存储
function Person(name, age, sex) {
  this.name = name;
  this.age = age;
  this.sex = sex;
  this.sayHello = function(){
    console.log(`hello, 我是${this.name}`)
  }
}

const p1 = new Person('小王', 18, "女");
const p2 = new Person('小赵', 19, "男");

console.log( p1.sayHello === p2.sayHello );   // false
  • 原型的出现,就是为了解决这种来自于同一个构造函数的对象之间重复方法的问题

四、原型

  1. 每一个函数(箭头函数除外)天生自带一个属性:prototype,对象类型。
    • 用来被 通过自身创建的对象的**__proto__** 指向
    • 语法:函数.prototype
  2. 每一个对象天生自带一个属性:__proto__,对象类型。
    • 用于指向 创建该对象的构造函数的**prototype**
    • 语法:对象.__proto__
  3. 一个对象的__proto__指向了创建该对象的构造函数的prototype
function Person(){}
const p1 = new Person()
console.log(p1.__proto__ === Person.prototype)   // true
  1. 隐式原型的使用规则:
    • 原型链:每个对象都具有__proto__属性,对象在使用属性或方法时,会先在自身查找,如果自身不存在,就会沿着__proto__的指向,依次查找,任意一层找到了,就停止查找并使用,直到Object.prototype,如果依然未找到,抛出undefined
    • 一个构造函数可以创建多个实例,如果给构造函数的prototype添加方法或属性,将来的每个实例都可以访问,达到节省内存的效果
    • 构造函数的prototype类似于将来所有实例的公共空间,提供公共方法,被实例使用
  2. 补充:构造函数的prototype自带了一个constructor的属性,用来标记当前prototype对象所属的函数
  3. 那么,之前构造函数创建对象,多个对象的相同方法在内存中重复存储的缺点,就可以被解决了
    • 把对象的方法写在构造函数的 prototype 中
    • 实例化对象访问的时候,自己没有,就会自动去 proto 中找
function Person() {}
Person.prototype.sayHi = function () {
  console.log('hello Person')
}

const p1 = new Person()
const p2 = new Person()

p1.sayHi()
p2.sayHi()

console.log( p1.sayHi === p2.sayHi );    // true

p1 自身没有 sayHi 方法,就会去自己的 proto 中查找
p1.proto 就是 Person.prototype
因为我们提前向 Person.prototype 中添加了 sayHi 方法
所以 p1.sayHi 可以执行

p2 自身没有 sayHi 方法,就会去自己的 proto 中查找
p2.proto 就是 Person.prototype
我们提前向 Person.prototype 中添加了 sayHi 方法
所以 p2.sayHi 可以执行了

且因为 p1.proto 和 p2.proto 指向都是 Person.prototype
所以 p1.proto 和 p2.proto 相等
所以 p1.sayHi 和 p2.sayHi 找到的是同一个方法

五、总结

  1. 使用构造函数创建对象时
    • 属性写在构造函数内,通过this绑定
    • 方法写在构造函数的原型对象上,减少内存的消耗
  2. 关于this指向
    • 因为 new 关键字的原因,构造函数内的this指向将来new出来的实例
    • 因为原型上的方法都是被当前实例调用的,所以原型方法内的this也指向将来new出来的实例
function Person(name) {
  this.name = name
  console.log('构造函数体内的打印')
  console.log('this : ', this)
  console.log('*******************')
}
// 构造函数的原型对象上添加一个方法
// sayHi 方法内的 this 是谁 ?
Person.prototype.sayHi = function() {
  console.log('我是原型上的 sayHi 方法')
  console.log('this : ', this)
  console.log('*******************')
}
//实例化一个p1对象
const p1 = new Person('张三')
//调用该方法
p1.sayHi()
console.log('p1', p1)

你可能感兴趣的:(从零开始学JavaScript,javascript,前端,OOP,面向对象编程,ecmascript,原型,prototype)