从栈队列数据结构到JS原型面向对象全解

目录

  • 一、数据结构基础:线性与非线性分类
    • [1.1 线性结构](#1.1 线性结构)
    • [1.2 非线性结构](#1.2 非线性结构)
  • 二、JS原型式面向对象:不靠Class也能实现面向对象
    • [2.1 函数两种调用形态,this完全不同](#2.1 函数两种调用形态,this完全不同)
      • [2.1.1 普通函数直接调用 `fn()`](#2.1.1 普通函数直接调用 fn())
      • [2.1.2 new + 构造函数调用 `new Fn()`](#2.1.2 new + 构造函数调用 new Fn())
    • [2.2 new关键字完整执行四步](#2.2 new关键字完整执行四步)
      • [实例属性 vs 原型属性对照表](#实例属性 vs 原型属性对照表)
    • [2.3 原型关键属性名词梳理](#2.3 原型关键属性名词梳理)
  • 三、JS设计哲学:一切皆对象,原型链查找规则
    • [3.1 顶层设计:Object是所有对象源头](#3.1 顶层设计:Object是所有对象源头)
    • [3.2 原型链查找规则](#3.2 原型链查找规则)
  • 四、全文总结
    • [4.1 核心知识点复盘](#4.1 核心知识点复盘)
    • [4.2 常见避坑指南](#4.2 常见避坑指南)

一、数据结构基础:线性与非线性分类

数据结构按照元素关联关系分为线性、非线性两大类,是算法与封装类的底层依托。

1.1 线性结构

元素一一前后对应,连续有序,包含三类:栈、队列、链表

  • 栈Stack:FILO ( first in last out )先进后出:后进元素先弹出,类比摞盘子,从顶部存取;调用栈、表达式求值依赖栈。
  • 队列Queue:FIFO(first in first out)先进先出:先入队的数据先出队,类比排队结账,事件循环任务队列底层就是队列。
  • 链表:节点依靠指针串联,无连续内存。

队列固定4个标准方法:

  1. push(x):元素追加至队列尾部(入队)
  2. pop():删除并取出队列头部元素(出队)
  3. peek():只读取队首,不移除元素
  4. empty():布尔返回,判断队列是否为空

1.2 非线性结构

元素一对多/多对多,无固定前后顺序:

  • 树:层级嵌套结构(DOM树、原型链本质树形结构)
  • 堆:特殊完全二叉树,用于优先队列、排序场景

二、JS原型式面向对象:不靠Class也能实现面向对象

JS核心特点:函数是一等对象、无传统类,全基于原型实现面向对象

2.1 函数两种调用形态,this完全不同

2.1.1 普通函数直接调用 fn()

  • this:浏览器指向window
  • 返回值:由return决定,无return默认undefined
  • 只执行函数逻辑,不生成实例
js 复制代码
function greeting() {
  console.log(this); // 普通执行this=全局
}
greeting.a = "1"; // 函数本身也是对象,可以挂载属性
console.log(greeting.a);// 1
greeting(); 

2.1.2 new + 构造函数调用 new Fn()

  • this自动绑定新创建的空实例对象,在构造函数内挂载实例自有属性
  • 默认自动返回新实例;若return引用类型(对象/数组),则以return内容作为返回值
  • 构造函数自带prototype原型对象,存放所有实例共享方法

注:构造函数和普通函数用首字母大小写区分(程序员之间的默认规则)

js 复制代码
// 约定:构造函数首字母大写
function Greeting(name) {
  console.log(this); // new后this=实例
  this.name = name; // 实例自有属性,把传入的参数给实例
}
// 原型挂载共享方法,所有实例指向一个内存空间
// 其中一个实例改了它,所有实例一起变
Greeting.prototype.say = function () {
  console.log(`我叫${this.name},很高兴认识你`);
};
Greeting.prototype.work = function () {
  console.log(`我叫${this.name},我正在工作`);
};
const zmt = new Greeting("zmt");// 创造一个实例
zmt.say();// 我叫zmt,我正在工作

2.2 new关键字完整执行四步

  1. JS在堆中创建一块全新空实例对象
  2. 将构造函数内部this指向这个新空对象
  3. 执行构造函数代码,通过this.属性给实例挂载私有属性
  4. 构造函数无手动return对象,默认自动返回新建实例;return对象则替换返回值

实例属性 vs 原型属性对照表

定义方式 存储位置 内存特点 修改范围 定义位置
this.xxx 单个实例自身 每个实例独立开辟内存,多实例多份数据 仅修改当前实例 构造函数内部
Fn.prototype.xxx 构造函数原型对象 全实例共用同一份内存,节省空间 全部实例同步生效 构造函数外部
js 复制代码
function Person(name) {
  this.name = name; // 实例私有
}
Person.prototype.say = function(){} // 原型共享方法
let p1 = new Person('张三')
let p2 = new Person('李四')
console.log(p1.say === p2.say) // true,共用同一个函数
console.log(p1.name === p2.name) // false,属性相互独立

2.3 原型关键属性名词梳理

  1. 构造函数.prototype:函数专属属性,指向原型对象,作为所有实例公共仓库
  2. 实例.__proto__:每个对象隐式属性,永远指向该实例的构造函数的prototype
  3. xxx.constructor:原型自带属性,指向原构造函数

三、JS设计哲学:一切皆对象,原型链查找规则

3.1 顶层设计:Object是所有对象源头

Object是顶层内置函数:

  • let obj = {} 等价于 new Object()
  • Array、Function、Date、RegExp全都是构造函数,继承自Object体系

3.2 原型链查找规则

  1. 实例读取属性/方法时:优先在自身实例查找,找到直接使用
  2. 自身无对应属性,顺着__proto__向上查找原型对象
  3. 原型没有,继续顺着原型的__proto__向上,直到Object.prototype
  4. Object.prototype.__proto__ = null,原型链查找终点为null,找不到返回undefined
html 复制代码
<!DOCTYPE html>
<html lang="en">
<body>
    <script>
        function Person(name,age){
            this.name=name;
            this.age=age;
        }
        // 原型挂载公共属性与方法
        Person.prototype.poem='仁义礼智信';
        Person.prototype.say=function(){
            console.log(`我叫${this.name}`);
        }
        Person.prototype.timeMF=function(){
            console.log(`时间管理魔法`);
        }
        const zs=new Person('zs',18);
        console.log(zs);
        // zs自身无toString,沿原型链找到Object.prototype.toString
        console.log(zs.toString());
    </script>
</body>
</html>

原链读取(查值,顺着原型链找)

实例自身没有该属性 → .proto 找原型

javascript 复制代码
function Person() {}
Person.prototype.name = "张三";
const p = new Person();

// 读取name
console.log(p.name);
// p自身没有name → 去原型找,拿到"张三"

2、赋值(改值,只改实例,不动原型)

javascript 复制代码
p.name = "李四";
console.log(p.name); // 李四(实例新增name)
console.log(Person.prototype.name); // 张三【原型丝毫没变】

赋值操作规则:

JS 不会跑到原型上修改原型属性;

直接在p 实例身上新建一个自身属性 name;

以后再读 p.name,优先读实例自己的,屏蔽原型同名属性。

总结

读沿原型链向上查,写只落在实例上;

基本类型改不动原型,引用类型方法能改原型里的数据(特殊)。

四、全文总结

4.1 核心知识点复盘

  1. 数据结构:栈FILO、队列FIFO,双栈可以逻辑模拟队列;线性一对一、非线性一对多。
  2. JS构造函数:普通调用this指向全局,new调用this指向新建实例,new分四步创建对象。
  3. 内存优化:实例属性this.xxx独占内存,原型prototype.xxx全实例共享,开发通用方法统一放原型。
  4. 原型链:实例自身→实例__proto__(构造原型)→Object.prototype→null,自上而下检索属性。
  5. JS无原生类,原型是JS面向对象原生实现方案。

4.2 常见避坑指南

  1. ❌ 避坑1:构造函数中通用方法不要写在this上,多实例会重复创建函数,内存冗余,统一挂载prototype。
  2. ❌ 避坑2:修改原型属性,所有已创建实例都会同步变更,原型尽量挂载方法,少存引用类型数据。
  3. ❌ 避坑3:new构造函数内若return数组/对象,会截断默认实例返回,极易引发bug
  4. ❌ 避坑4:原型链查找只做读取,赋值属性优先挂载到实例自身,不会修改原型原有属性。
相关推荐
MageGojo1 小时前
随机文案模块怎么做?从接口封装到前端展示的完整实现思路
javascript·前端开发·api接口·后端开发·随机文案
独特的螺狮粉1 小时前
篮球集训班器具管理系统 - 鸿蒙PC Electron框架完整技术实现指南
前端·javascript·华为·electron·前端框架·开源·鸿蒙
小妖6661 小时前
js 生成随机数技巧 Math.random().toString(36)
javascript·随机数
pusheng20251 小时前
IFSJ全英文专访:中国创新力量重塑先进气体感知技术,赋能全球关键基础设施安全
前端·网络·人工智能·物联网·安全
AI_零食2 小时前
番茄钟鸿蒙PC Electron框架完成:状态机、定时器管理与专注力工具设计
前端·javascript·华为·electron·开源·鸿蒙·鸿蒙系统
提子拌饭1332 小时前
逛三园游戏——基于鸿蒙PC Electron框架实现
前端·javascript·游戏·华为·electron·鸿蒙
llz_1122 小时前
web-第三次课后作业
前端·后端·web
遗憾随她而去.2 小时前
Web地图全体系深度梳理:引擎、数据源、图层、投影核心知识
前端
爱因斯坦乐3 小时前
Vue项目整合
前端·javascript·vue.js