JavaScript 面向过程与面向对象
一、编程范式基础
1.1 什么是编程范式
编程范式就是编写代码的「核心思想+风格套路」,本质是解决问题的不同思路。JavaScript 是多范式语言,能同时用两种核心范式:
-
面向过程:关注「步骤、流程、先后顺序」,像按菜谱做菜,一步一步执行;
-
面向对象:关注「事物本身、特征、行为」,像搭乐高,先做独立模块再组合。
1.2 本次分享核心目标
-
理解两种编程思想的核心区别;
-
能看懂/写出基础演示代码;
-
掌握各自优缺点和适用场景;
-
知道开发中该选哪种方式。
二、面向过程编程(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 面向对象三大特征(核心简化版)
-
封装:属性+方法打包,对外只暴露调用接口,隐藏内部细节(比如调用
getAvg()不用管内部怎么计算);通俗理解:就像手机,我们只需要按屏幕上的按钮(调用接口),就能打电话、发消息,不需要知道手机内部的芯片、电路是怎么工作的(隐藏内部细节)。
// 封装的核心:外部不用关心内部怎么实现,直接调用即可 let s1 = new Student('小明', 90, 85, 95); console.log(s1.getAvg()); // 直接调用方法,不用管内部怎么计算平均分封装的优点:
安全:内部属性和方法不会被外部随意修改
简洁:外部调用只需要关注“结果”,不用关注“过程”
易维护:修改内部实现,不影响外部调用(比如修改平均分的保留位数,外部调用代码不变)
-
继承:让一个对象(子类),拥有另一个对象(父类)的属性和方法,无需重复编写代码,实现代码复用。
通俗理解:儿子继承父亲的财产和技能,不用自己重新学习,直接拥有。
// 父类: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); // 输出:高三核心优势:子类不用重复编写父类的代码,只需要关注自己的新增属性和方法,极大提升代码复用性。
-
多态:同一个方法名,不同的对象执行,会产生不同的结果(同一行为,不同表现)。
通俗理解:同样是“说话”,学生说“我在学习”,老师说“我在讲课”,行为相同,内容不同。
// 学生类 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 优点&适用场景
-
优点:复用性强、结构清晰、扩展性好、适合多人协作;
-
适用场景:中大型项目、网页组件(轮播/弹窗)、后台系统、复杂交互开发。
四、两种范式实战对比
统一需求:简单登录验证
核心功能:
-
非空验证:账号、密码不能为空
-
长度验证:账号长度3~10位,密码长度6~12位
-
账号密码验证:默认账号admin,密码123456,匹配则登录成功,否则失败
-
返回验证结果(提示文字)
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 互动提问
问题列表
-
什么是编程范式?JavaScript 支持的两种核心编程范式核心思路分别是什么?
-
面向过程编程的核心特点有哪些?适合用在什么场景?
-
面向对象编程的核心公式是什么?属性和方法分别对应现实事物的什么?
-
JavaScript 中创建对象有哪两种方式?各适合什么场景?
-
面向对象的封装、继承、多态分别是什么意思?
-
面向过程做学生成绩计算时,数据和函数分离会有什么问题?面向对象怎么解决?
-
登录验证功能中,面向过程和面向对象在数据与功能关系上有什么核心差异?
-
构造函数的作用是什么?命名有什么规则?
new关键字的作用? -
this关键字在面向对象代码里的作用?省略this会有什么问题? -
实际开发为什么要混合使用面向过程和面向对象,而非只用一种?
答案列表
-
编程范式是编写代码的核心思想和风格套路,是解决问题的不同思路。JavaScript支持的两种核心范式:面向过程关注步骤、流程和先后顺序;面向对象关注事物本身、特征和行为。
-
核心特点:代码线性执行、顺序清晰;数据和功能完全分离;上手简单。适用场景:简单工具函数、一次性脚本、小型页面效果。
-
核心公式:对象 = 属性 + 方法。属性对应现实事物的静态特征,方法对应现实事物的动态行为。
-
①对象字面量:适合创建单个对象;②构造函数:适合批量创建结构相同的多个对象。
-
①封装:将属性和方法打包,对外暴露调用接口,隐藏内部细节;②继承:让子类拥有父类的属性和方法,无需重复写代码;③多态:同一个方法名,不同对象执行产生不同结果。
-
问题:函数需传参才能使用数据,新增多个学生需复制多遍数据变量,维护成本高。解决:将学生的属性和方法封装到对象中,数据与方法直接关联;批量创建可用构造函数,降低维护成本。
-
面向过程:数据是全局独立变量,功能是独立函数,二者完全分离,函数需传参使用数据;面向对象:数据作为对象属性,功能作为对象方法,二者封装在一起,方法通过
this直接访问属性。 -
构造函数:作为对象模板,批量创建结构相同的对象;命名规则:首字母大写;
new关键字:调用构造函数,创建并返回新对象实例,让构造函数内的this指向该新对象。 -
this作用:指向当前对象本身,用于在对象方法中访问自身的属性和方法;省略this问题:方法无法访问对象属性,会将属性名识别为全局变量,导致逻辑执行错误。 -
两种范式各有适配场景,混合使用能兼顾开发效率和代码可维护性:小功能、简单需求用面向过程,开发快、写法简洁;复杂、需复用/扩展的核心模块用面向对象,便于维护和功能扩展。

1323

被折叠的 条评论
为什么被折叠?



