JavaScript 面向对象编程:从原型到 Class 的演进

JavaScript 面向对象编程:从原型到 Class 的演进

JavaScript 作为一门灵活的脚本语言,其面向对象编程(OOP)体系与传统的类式语言(如 Java、C++)有着本质区别 ------ 它基于原型而非类。本文将从基础的对象创建出发,逐步拆解原型机制、原型链,以及 ES6 Class 语法糖,最后讲解 JavaScript 中的继承实现,带你完整理解 JS 面向对象的核心逻辑。

一、从简单对象到构造函数:封装的起步

在 JavaScript 中,最基础的对象创建方式是直接字面量声明,但这种方式在创建多个同类型对象时会产生大量重复代码:

javascript

Run

ini 复制代码
// 原始方式:重复创建相似对象,代码冗余
var Cat = {
    name: "",
    color: ""
}
var cat1 = {};
cat1.name = '加菲猫';
cat1.color = '橘色';
var cat2 = {};
cat2.name = '黑猫警长';
cat2.color = '黑色';

为了解决代码重复问题,我们可以用构造函数 封装对象的实例化过程。构造函数的核心是this关键字 ------ 它的指向由调用方式决定:

javascript

Run

javascript 复制代码
function Cat(name, color) {
    console.log(this); // 调用方式不同,this指向不同
    this.name = name; // 给实例挂载属性
    this.color = color;
}

// 1. 普通函数调用:this指向window(非严格模式)
Cat('黑猫警长', '黑色'); 

// 2. new关键字调用:this指向新创建的空对象(实例)
const cat1 = new Cat('加菲猫', '橘色');
const cat2 = new Cat('黑猫警长', '黑色');

// 验证实例归属
console.log(cat1.constructor === cat2.constructor); // true
console.log(cat1 instanceof Cat); // true

构造函数解决了 "重复创建对象" 的问题,但如果在构造函数内定义方法,每个实例都会生成独立的方法副本,造成内存浪费:

javascript

Run

javascript 复制代码
function Cat(name,color){
    this.name = name
    this.color = color
    // 每个实例都会创建一个新的eat方法,浪费内存
    this.eat = function(){alert('喜欢Jerry')}
}

二、原型(prototype):共享属性与方法的核心

JavaScript 为每个函数都提供了prototype属性(原型对象),挂载在原型上的属性 / 方法会被所有实例共享,从根本上解决内存浪费问题。

1. 原型的基本使用

javascript

Run

ini 复制代码
function Cat(name,color){
    this.name = name
    this.color = color
}

// 原型上挂载共享属性和方法
Cat.prototype.type = '猫科动物';
Cat.prototype.eat = function(){
    console.log('eat Jerry');
}

const cat1 = new Cat('tom','黑色');
const cat2 = new Cat('加菲猫','橘色');

// 所有实例共享原型属性
console.log(cat1.type, cat2.type); // 猫科动物 猫科动物

// 实例可重写原型属性(仅影响自身,不改变原型)
cat1.type = '铲屎官的主人';
console.log(cat1.type, cat2.type); // 铲屎官的主人 猫科动物

2. 原型的核心检测方法

  • prototype.isPrototypeOf():判断原型是否属于某个实例
  • hasOwnProperty():判断属性是否是实例自身属性(非原型继承)
  • in 运算符:判断属性是否存在(包括实例和原型)
  • for...in:遍历实例自身 + 原型的可枚举属性

javascript

Run

javascript 复制代码
console.log(Cat.prototype.isPrototypeOf(cat1)); // true
console.log(cat1.hasOwnProperty('type')); // false(原型属性)
console.log(cat1.hasOwnProperty('name')); // true(实例属性)
console.log("name" in cat1); // true
console.log("type" in cat1); // true

// 遍历所有可枚举属性(实例+原型)
for(var prop in cat1){
    console.log(prop, cat1[prop]); 
    // name tom, color 黑色, type 猫科动物, eat [Function]
}

三、ES6 Class:原型的语法糖

ES6 引入class关键字,让 JavaScript 的面向对象写法更接近传统类式语言,但底层仍然基于原型机制,本质是 "语法糖"。

1. Class 的基本使用

javascript

Run

javascript 复制代码
class Cat{
    // 构造方法:对应构造函数的核心逻辑
    constructor(name,color){
        this.name = name
        this.color = color
    }
    // 方法会自动挂载到原型上
    eat(){
        console.log('eat Jerry');
    }
}

const cat1 = new Cat('tom','黑色');
cat1.eat(); // eat Jerry

// 拆解原型链(Class的底层逻辑)
console.log(
    cat1.__proto__, // Cat.prototype(包含eat方法)
    cat1.__proto__.constructor, // Cat类本身
    cat1.__proto__.__proto__, // Object.prototype
    cat1.__proto__.__proto__.__proto__ // null(原型链终点)
);

2. Class 的核心特性

  • constructor:构造方法,对应传统构造函数
  • 类内定义的方法自动挂载到prototype,无需手动赋值
  • 类的所有方法不可枚举(区别于手动挂载的原型方法)
  • 必须用new调用,普通调用会报错(避免 this 指向混乱)

四、JavaScript 的继承实现

继承是面向对象的核心特性之一,JavaScript 没有原生的 "继承关键字",但可以通过原型和this绑定实现继承。

1. 构造函数继承(继承属性)

通过apply/call绑定父类构造函数的this,让子类实例继承父类的实例属性:

javascript

Run

javascript 复制代码
// 父类:Animal
function Animal(){
    this.species = '动物';
}

// 子类:Cat
function Cat(name,color){
    // 绑定父类的this到子类实例,继承父类属性
    Animal.apply(this); 
    this.name = name;
    this.color = color;
}

const cat = new Cat('加菲猫','橘色');
console.log(cat.species); // 动物(继承自Animal)

2. 原型继承(继承方法)

仅靠构造函数继承无法获取父类原型上的方法,需要结合原型链实现完整继承:

javascript

Run

javascript 复制代码
function Animal(){
    this.species = '动物';
}
// 父类原型挂载方法
Animal.prototype.sayHi = function(){
    console.log('啦啦啦啦啦');
}

function Cat(name,color){
    Animal.apply(this); // 继承属性
    this.name = name;
    this.color = color;
}

// 子类原型指向父类实例,继承父类原型方法
Cat.prototype = new Animal();

const cat = new Cat('加菲猫','橘色');
cat.sayHi(); // 啦啦啦啦啦(继承自Animal原型)

五、核心总结

  1. JS 面向对象的本质:基于原型而非类,ES6 Class 是原型的语法糖;
  2. this 的指向规则:普通函数调用指向 window,new 调用指向实例,apply/call 可手动绑定;
  3. 原型的作用:共享属性 / 方法,减少内存消耗,是原型链的基础;
  4. 继承的实现:构造函数继承(属性)+ 原型继承(方法)结合,是 JS 继承的经典方案;
  5. 原型链 :实例__proto__指向构造函数prototype,最终指向null,是属性查找的核心逻辑。

理解原型和原型链,是掌握 JavaScript 面向对象的关键。无论是传统的构造函数 + 原型写法,还是 ES6 的 Class 写法,底层逻辑始终围绕 "原型" 展开 ------ 这也是 JavaScript 区别于其他类式语言的核心特征。

相关推荐
z***D6481 小时前
SpringBoot3+Springdoc:v3api-docs可以访问,html无法访问的解决方法
前端·html
海云前端11 小时前
国产前端神器 @qnvip/core 一站式搞定 90% 业务痛点
前端
用户4445543654261 小时前
TooltipBox在Compose里
前端
gustt1 小时前
JavaScript 面向对象编程:从对象字面量到原型链继承,全链路彻底讲透
前端·javascript·面试
liberty8881 小时前
dppt如何找到弹框
java·服务器·前端
宁雨桥1 小时前
使用pnpm构建高效Monorepo:从零到一的完整指南
前端·pnpm·项目架构
chéng ௹1 小时前
uniapp vue3 unipush2.0 调用系统通知功能流程
前端·vue.js·uni-app
小菜今天没吃饱1 小时前
DVWA-XSS(DOM)
前端·javascript·xss·dvwa
q***04631 小时前
Spring Cloud Alibaba 组件版本选择
android·前端·后端