JavaScript 面向过程与面向对象

JavaScript 面向过程与面向对象 

一、编程范式基础

1.1 什么是编程范式

编程范式就是编写代码的「核心思想+风格套路」,本质是解决问题的不同思路。JavaScript 是多范式语言,能同时用两种核心范式:

  • 面向过程:关注「步骤、流程、先后顺序」,像按菜谱做菜,一步一步执行;

  • 面向对象:关注「事物本身、特征、行为」,像搭乐高,先做独立模块再组合。

1.2 本次分享核心目标

  1. 理解两种编程思想的核心区别;

  2. 能看懂/写出基础演示代码;

  3. 掌握各自优缺点和适用场景;

  4. 知道开发中该选哪种方式。

二、面向过程编程(POP)

2.1 核心思想

以「过程/步骤」为核心,把复杂问题拆解成一个个简单的步骤,按“自上而下”的顺序执行,逐步完成需求。

核心特点

  • 代码线性执行,从上到下,顺序清晰

  • 「数据」和「功能(函数)」是完全分离的(数据是独立变量,函数是独立工具)

  • 无需复杂设计,上手就能写

2.2 优点&缺点

优点缺点
简单直观,符合线性思维数据和函数无关联,可读性差
小功能开发速度快复用性极差,易重复写代码
代码量少,易调试扩展性差,改一处动全身

2.3 代码演示 1:最简单的面向过程(求和案例)

需求:计算 1~10 的所有整数之和,一步一步实现。

// 步骤1:定义存储结果的变量(数据)
let sum = 0; // 初始值为0,用于累加
​
// 步骤2:执行累加逻辑(功能)
// 循环从1到10,每次把i的值加到sum中
for (let i = 1; i <= 10; i++) {
  sum += i; // 等价于 sum = sum + i
}
​
// 步骤3:输出最终结果
console.log('1~10 的总和:', sum); // 输出结果:55

说明:这段代码完全遵循“步骤化”思路,每一步都清晰,适合新手入门,但数据(sum)和功能(循环)是分离的。

2.4 代码演示:学生成绩计算(核心简化版)

需求:计算单个学生三科总分、平均分

// 步骤1:定义独立数据
let name = '张三';
let chinese = 90, math = 85, english = 92;
​
// 步骤2:定义独立功能函数
function getTotal(c, m, e) {
  return c + m + e;
}
function getAvg(total) {
  return (total / 3).toFixed(2);
}
​
// 步骤3:按步骤调用
let total = getTotal(chinese, math, english);
let avg = getAvg(total);
​
console.log(`${name}总分:${total},平均分:${avg}`);

关键说明:缺点:数据(name/成绩)和函数(计算逻辑)完全分离,函数需传参才能用数据;若新增10个学生,要复制10遍数据变量,维护成本极高。

2.5 适用场景

  • 简单工具函数(如计算两数差值、判断奇偶);

  • 一次性脚本(批量处理少量数据);

  • 小型页面效果(如点击按钮显示提示)。

三、面向对象编程(OOP)

3.1 核心思想

以「对象」为核心,将现实事物抽象成代码中的对象,把「数据(属性)」和「功能(方法)」打包成独立模块。核心公式:对象 = 属性 + 方法

举个例子:现实中的“学生”,对应代码中的“学生对象”:

  • 属性(静态特征):姓名、年龄、语文成绩、数学成绩(对应代码中的变量)

  • 方法(动态行为):计算总分、计算平均分、评级(对应代码中的函数)

3.2 核心概念

  • 对象:现实事物的抽象(如学生对象、用户对象);

  • 属性:对象的静态特征(如姓名、成绩,本质是对象内部变量);

  • 方法:对象的动态行为(如计算总分,本质是对象内部函数);

  • this:指向当前对象本身,用于访问对象自身的属性/方法。

3.3 JS 创建对象的两种核心方式

方式1:对象字面量(适合单个对象)

语法:用 {} 包裹,里面写属性和方法,属性用「键值对」表示(key: value),方法是函数。

// 学生对象:属性+方法打包
let student = {
  name: '李四',
  chinese: 88,
  math: 95,
  english: 90,
  // 方法:用this访问自身属性
  getTotal() {
    return this.chinese + this.math + this.english;
  },
  getAvg() {
    return (this.getTotal() / 3).toFixed(2);
  }
};
​
// 调用:对象.属性 / 对象.方法()
console.log(`${student.name}总分:${student.getTotal()},平均分:${student.getAvg()}`);

核心优势:数据和方法打包在一起,结构清晰,不用单独定义变量和函数,直接通过对象调用,可读性极强。

方式2:构造函数(适合批量创建对象)

如果需要创建多个“结构相同”的对象(比如10个学生、5个用户),用对象字面量会重复写大量代码,此时用「构造函数」(相当于“对象模板”),批量创建,高效复用。

语法:

  • 构造函数名首字母大写(约定俗成,区分普通函数)

  • this 定义属性和方法

  • new 关键字,调用构造函数,创建对象

// 构造函数(对象模板):首字母大写
function Student(name, chinese, math, english) {
  // 属性
  this.name = name;
  this.chinese = chinese;
  this.math = math;
  this.english = english;
  // 方法
  this.getTotal = function () {
    return this.chinese + this.math + this.english;
  };
  this.getAvg = function () {
    return (this.getTotal() / 3).toFixed(2);
  };
}
​
// 批量创建对象
let s1 = new Student('小明', 90, 85, 95);
let s2 = new Student('小红', 88, 92, 90);
​
console.log(`${s1.name}总分:${s1.getTotal()}`);
console.log(`${s2.name}总分:${s2.getTotal()}`);

核心优势:只需要写一次构造函数,就能批量创建无数个结构相同的对象,修改构造函数,所有对象都会同步生效,极大提升复用性和维护性。

3.4 面向对象三大特征(核心简化版)

  1. 封装:属性+方法打包,对外只暴露调用接口,隐藏内部细节(比如调用getAvg()不用管内部怎么计算);

    通俗理解:就像手机,我们只需要按屏幕上的按钮(调用接口),就能打电话、发消息,不需要知道手机内部的芯片、电路是怎么工作的(隐藏内部细节)。

    // 封装的核心:外部不用关心内部怎么实现,直接调用即可
    let s1 = new Student('小明', 90, 85, 95);
    console.log(s1.getAvg()); // 直接调用方法,不用管内部怎么计算平均分

    封装的优点

    安全:内部属性和方法不会被外部随意修改

    简洁:外部调用只需要关注“结果”,不用关注“过程”

    易维护:修改内部实现,不影响外部调用(比如修改平均分的保留位数,外部调用代码不变)

  2. 继承:让一个对象(子类),拥有另一个对象(父类)的属性和方法,无需重复编写代码,实现代码复用。

    通俗理解:儿子继承父亲的财产和技能,不用自己重新学习,直接拥有。

    // 父类:Student(已经定义好,拥有name属性和study方法)
    function Student(name) {
      this.name = name;
      this.study = function () {
        console.log(this.name + ' 正在学习');
      };
    }
    ​
    // 子类:HighStudent(高中生),继承Student的属性和方法
    function HighStudent(name, grade) {
      // 关键:继承父类的属性和方法(call:改变this指向,让子类拥有父类的内容)
      Student.call(this, name);
      // 子类新增属性:年级
      this.grade = grade;
    }
    ​
    // 创建子类对象
    let hs = new HighStudent('小王', '高三');
    // 调用继承来的方法
    hs.study(); // 输出:小王 正在学习
    // 访问继承来的属性
    console.log(hs.name); // 输出:小王
    // 访问子类新增的属性
    console.log(hs.grade); // 输出:高三

    核心优势:子类不用重复编写父类的代码,只需要关注自己的新增属性和方法,极大提升代码复用性。

  3. 多态:同一个方法名,不同的对象执行,会产生不同的结果(同一行为,不同表现)。

    通俗理解:同样是“说话”,学生说“我在学习”,老师说“我在讲课”,行为相同,内容不同。

    
    
    	
    // 学生类
    function Student() {}
    // 学生的say方法
    Student.prototype.say = function () {
      console.log('我是学生,我在学习');
    };
    ​
    // 老师类
    function Teacher() {}
    // 老师的say方法(和学生的方法名相同)
    Teacher.prototype.say = function () {
      console.log('我是老师,我在讲课');
    };
    ​
    // 创建对象
    let s = new Student();
    let t = new Teacher();
    ​
    // 调用同一个方法名,输出不同结果
    s.say(); // 输出:我是学生,我在学习
    t.say(); // 输出:我是老师,我在讲课
    ​

3.5 优点&适用场景

  • 优点:复用性强、结构清晰、扩展性好、适合多人协作;

  • 适用场景:中大型项目、网页组件(轮播/弹窗)、后台系统、复杂交互开发。

四、两种范式实战对比

统一需求:简单登录验证

核心功能:

  1. 非空验证:账号、密码不能为空

  2. 长度验证:账号长度3~10位,密码长度6~12位

  3. 账号密码验证:默认账号admin,密码123456,匹配则登录成功,否则失败

  4. 返回验证结果(提示文字)

1. 面向过程写法
// 第一步:定义独立数据(全局变量)
let username = 'admin'; // 账号
let password = '123456'; // 密码
​
// 第二步:定义独立功能函数
// 1. 非空验证:判断字符串是否为空
function checkEmpty(str) {
  // 排除空格(比如用户输入全是空格,也算空)
  return str.trim() !== '';
}
​
// 2. 长度验证:判断字符串长度是否在min~max之间
function checkLength(str, min, max) {
  return str.length >= min && str.length <= max;
}
​
// 3. 登录核心逻辑
function login(user, pwd) {
  // 步骤1:非空验证
  if (!checkEmpty(user) || !checkEmpty(pwd)) {
    return '账号或密码不能为空!';
  }
  // 步骤2:长度验证
  if (!checkLength(user, 3, 10) || !checkLength(pwd, 6, 12)) {
    return '账号长度3~10位,密码长度6~12位!';
  }
  // 步骤3:账号密码匹配验证
  if (user === 'admin' && pwd === '123456') {
    return '登录成功!';
  } else {
    return '账号或密码错误!';
  }
}
​
// 第三步:调用函数,执行登录
let result = login(username, password);
console.log(result); // 输出:登录成功!
2. 面向对象写法
// 第一步:定义User构造函数(用户对象模板)
function User(username, password) {
  // 属性:账号、密码(打包在对象内部)
  this.username = username;
  this.password = password;
​
  // 方法1:非空验证(内部方法,只给当前对象使用)
  this.checkEmpty = function () {
    // 访问当前对象的username和password属性
    return this.username.trim() !== '' && this.password.trim() !== '';
  };
​
  // 方法2:长度验证
  this.checkLength = function () {
    return (
      this.username.length >= 3 && this.username.length <= 10 &&
      this.password.length >= 6 && this.password.length <= 12
    );
  };
​
  // 方法3:登录核心逻辑
  this.login = function () {
    if (!this.checkEmpty()) {
      return '账号或密码不能为空!';
    }
    if (!this.checkLength()) {
      return '账号长度3~10位,密码长度6~12位!';
    }
    return this.username === 'admin' && this.password === '123456'
      ? '登录成功!'
      : '账号或密码错误!';
  };
}
​
// 第二步:创建用户对象,传入账号密码
let user = new User('admin', '123456');
​
// 第三步:调用登录方法,获取结果
let result = user.login();
console.log(result); // 输出:登录成功!

核心对比总结

对比维度面向过程(POP)面向对象(OOP)
核心关注点步骤、流程、先后顺序对象、模块、属性与方法
数据与功能关系完全分离,无直接关联封装在一起,关联性强
代码复用性较差,需大量复制粘贴极强,通过构造函数、继承复用
维护难度高,改一处动全身低,独立模块,修改不影响其他代码
扩展性差,新增功能需修改大量代码好,新增功能只需给对象加方法
适用场景小功能、脚本、一次性代码中大型项目、组件、系统开发
思维方式线性思维(先做什么,再做什么)模块化思维(先做模块,再组合)

五、总结+互动提问

5.1 核心总结

  • 面向过程:简单快速,核心是“步骤”,适合小功能;

  • 面向对象:封装/继承/多态,核心是“对象”,适合复杂项目;

  • 实际开发:混合使用(小功能用过程,大模块用对象)。

5.2 互动提问

问题列表

  1. 什么是编程范式?JavaScript 支持的两种核心编程范式核心思路分别是什么?

  2. 面向过程编程的核心特点有哪些?适合用在什么场景?

  3. 面向对象编程的核心公式是什么?属性和方法分别对应现实事物的什么?

  4. JavaScript 中创建对象有哪两种方式?各适合什么场景?

  5. 面向对象的封装、继承、多态分别是什么意思?

  6. 面向过程做学生成绩计算时,数据和函数分离会有什么问题?面向对象怎么解决?

  7. 登录验证功能中,面向过程和面向对象在数据与功能关系上有什么核心差异?

  8. 构造函数的作用是什么?命名有什么规则?new 关键字的作用?

  9. this 关键字在面向对象代码里的作用?省略 this 会有什么问题?

  10. 实际开发为什么要混合使用面向过程和面向对象,而非只用一种?

答案列表

  1. 编程范式是编写代码的核心思想和风格套路,是解决问题的不同思路。JavaScript支持的两种核心范式:面向过程关注步骤、流程和先后顺序;面向对象关注事物本身、特征和行为。

  2. 核心特点:代码线性执行、顺序清晰;数据和功能完全分离;上手简单。适用场景:简单工具函数、一次性脚本、小型页面效果。

  3. 核心公式:对象 = 属性 + 方法。属性对应现实事物的静态特征,方法对应现实事物的动态行为。

  4. ①对象字面量:适合创建单个对象;②构造函数:适合批量创建结构相同的多个对象。

  5. ①封装:将属性和方法打包,对外暴露调用接口,隐藏内部细节;②继承:让子类拥有父类的属性和方法,无需重复写代码;③多态:同一个方法名,不同对象执行产生不同结果。

  6. 问题:函数需传参才能使用数据,新增多个学生需复制多遍数据变量,维护成本高。解决:将学生的属性和方法封装到对象中,数据与方法直接关联;批量创建可用构造函数,降低维护成本。

  7. 面向过程:数据是全局独立变量,功能是独立函数,二者完全分离,函数需传参使用数据;面向对象:数据作为对象属性,功能作为对象方法,二者封装在一起,方法通过this直接访问属性。

  8. 构造函数:作为对象模板,批量创建结构相同的对象;命名规则:首字母大写;new关键字:调用构造函数,创建并返回新对象实例,让构造函数内的this指向该新对象。

  9. this作用:指向当前对象本身,用于在对象方法中访问自身的属性和方法;省略this问题:方法无法访问对象属性,会将属性名识别为全局变量,导致逻辑执行错误。

  10. 两种范式各有适配场景,混合使用能兼顾开发效率和代码可维护性:小功能、简单需求用面向过程,开发快、写法简洁;复杂、需复用/扩展的核心模块用面向对象,便于维护和功能扩展。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值