普通视图

发现新文章,点击刷新页面。
昨天 — 2025年6月8日首页

小白也能懂:JavaScript 原型链和隐藏类的奇妙世界

作者 XXUZZWZ
2025年6月8日 18:23

小白也能懂:JavaScript 原型链和隐藏类的奇妙世界

大家好!今天我们来聊聊 JavaScript 里两个听起来高大上但其实很简单的概念:原型链隐藏类。别被名字吓到,我会用最生活化的方式解释给你听!


一、原型链:JavaScript 的"家族族谱" 🧬

想象你有一个玩具箱(对象),你想找某个玩具(属性):

const myToyBox = {
  car: "红色小汽车", // 自己的玩具
};

当你直接找 myToyBox.car,立刻就能找到。但如果想找一个不存在的玩具呢?

console.log(myToyBox.robot); // 自己箱子里没有!

这时 JavaScript 会去"爸爸的玩具箱"里找 → 这就是原型链

// 爸爸的玩具箱
const dadsToyBox = {
  robot: "变形金刚"
};

// 设置爸爸的箱子为你的原型
Object.setPrototypeOf(myToyBox, dadsToyBox);

console.log(myToyBox.robot); // ✅ 找到了变形金刚!

查找过程就像寻宝游戏

  1. 先翻自己的箱子(对象自身)
  2. 找不到就去爸爸的箱子(原型对象)
  3. 还找不到就去爷爷的箱子(原型的原型)
  4. 直到找到或家族尽头(null)


(示意图:对象 → 原型 → 原型的原型 → null)

deepseek_mermaid_20250608_1e91d8.png

🔑 关键点:当你用 对象.属性 时,JavaScript 会沿着这条"家族链"层层查找!


二、原型链是链表吗?底层揭秘 🔍

很多教程说原型链像"链表",对也不对

  • 行为像链表:通过指针连接,顺序查找
  • 实现非链表:引擎用更高级方式优化

看个底层伪代码(V8引擎简化版):

// 当访问 obj.property 时
Object* current = obj;
while (current != null) {
  if (current->HasProperty("property")) {
    return value; // 找到就返回
  }
  current = current->prototype; // 跳转到下一个原型
}
return undefined; // 找不到

就像你按地址串门找人:

你家(对象)→ 爸爸家(原型)→ 爷爷家(Object.prototype)→ 终点(null

三、隐藏类:引擎的"超强记忆术" 🧠

如果每次查属性都要遍历整条链,岂不慢死?别怕!JavaScript 引擎有个秘密武器——隐藏类

什么是隐藏类?

想象你去学校图书馆:

  • 无隐藏类:每次借书都从头找书名(超慢!)
  • 有隐藏类:管理员记住"科幻区第3书架"(直接拿书)

代码示例

// 创建两个相同结构的对象
const obj1 = {};
obj1.name = "小明"; // 触发隐藏类 C1
obj1.age = 12;     // 转换到隐藏类 C2

const obj2 = {};
obj2.name = "小红"; // 同样触发 C1 → C2 转换

这时引擎会:

  1. {name, age} 结构创建共享的隐藏类
  2. 记录 nameage 在内存中的固定位置
  3. 后续访问时直接跳转地址(比查字典快10倍!)

什么时候会"失忆"?

// ❌ 错误示范:打乱属性顺序
const obj3 = {};
obj3.age = 12;     // 创建新隐藏类 C3
obj3.name = "小刚"; // 转换到 C4(与 obj1/obj2 不同!)

📝 最佳实践
✅ 按固定顺序初始化属性
✅ 尽量在构造函数中赋值
❌ 避免动态增删属性


总结:三句话掌握精髓 💡

  1. 原型链是 JS 的属性查找机制——像家族寻亲,层层向上找
  2. 点操作符(.) 触发原型链遍历——从自己到原型链尽头
  3. 隐藏类是引擎的优化术——用固定内存布局加速访问

下次看到 obj.property,你就知道背后有一段精彩的"寻亲之旅"啦!建议写个简单例子体验一下,代码是最好的老师哦~

// 动手实验!
const grandpa = { hobby: "钓鱼" };
const father = { job: "工程师" };
Object.setPrototypeOf(father, grandpa);

const me = {};
Object.setPrototypeOf(me, father);

console.log(me.hobby); // 试试输出什么?

一起来学习React哲学,理解数据驱动的现代框架

作者 XXUZZWZ
2025年6月8日 02:23

React 想给我们带来什么不同的思考?

官方文档说法

React 可以改变你对可见设计和应用构建的思考。当你使用 React 构建用户界面时,你首先会把它分解成一个个 组件,然后,你需要把这些组件连接在一起,使数据 流经 它们。 React官方链接 : React 哲学 – React 中文文档

怎么理解呢?

组件的思想

  1. 面对一个要实现的界面,React希望把界面划分为若干组件,然后再组合在一起。
  2. 每个组件都是独立的,有独立的样式交互逻辑

让数据在组件之间流动的思想

  1. 界面需要的数据可以在组件中传递。
  2. 传递有两个方向,正向数据流,父向子流动;子向父流动,被称为反向数据流。
  3. 在React中要求区别两种数据,简单来说就是静态的和动态的数据。
  4. 对于静态的数据只要在需要的组件内简单声明就可以,对于动态数据我们则需要React的特殊声明。

让我们结合一个简单例子来理解

假如我们接到一个开发这样一个页面的任务。

原型图: image.png

要渲染的静态数据:

[  


{ category: "Fruits", price: "$1", stocked: true, name: "Apple" },  


{ category: "Fruits", price: "$1", stocked: true, name: "Dragonfruit" },  


{ category: "Fruits", price: "$2", stocked: false, name: "Passionfruit" },  


{ category: "Vegetables", price: "$2", stocked: true, name: "Spinach" },  


{ category: "Vegetables", price: "$4", stocked: false, name: "Pumpkin" },  


{ category: "Vegetables", price: "$1", stocked: true, name: "Peas" }  


]

第一步我们该做什么呢?

将界面划分为若干组件,想好我们要写几个组件,以及组件之间的嵌套关系

像这样:

image.png

  1. FilterableProductTable(灰色)包含完整的应用。
  2. SearchBar(蓝色)获取用户输入。
  3. ProductTable(淡紫色)根据用户输入,展示和过滤清单。
  4. ProductCategoryRow(绿色)展示每个类别的表头。
  5. ProductRow(黄色)展示每个产品的行。

我们可以看到整个FilterableProductTable是最大的组件,容纳了 SearchBarProductTable两个子组件,ProductTable也包含了 ProductCategoryRowProductRow组件。

第二步我们该干什么呢?

我们应该先写出设计图的静态页面,那我们先写那个组件呢?从大到小还是从小到大呢? 一般来说面对小项目一般从大到小,中大项目一般从小到大。 我们这里采取从子

// 产品目录行 组件
function ProductCategoryRow({ category }) {
  return (
    <tr>
      <th colSpan="2">
        {category}
      </th>
    </tr>
  );
}
// 产品行 组件
function ProductRow({ product }) {
  const name = product.stocked ? product.name :
    <span style={{ color: 'red' }}>
      {product.name}
    </span>;

  return (
    <tr>
      <td>{name}</td>
      <td>{product.price}</td>
    </tr>
  );
}
// 产品表 组件
function ProductTable({ products }) {
  const rows = [];
  let lastCategory = null;

  products.forEach((product) => {
    if (product.category !== lastCategory) {
      rows.push(
        <ProductCategoryRow
          category={product.category}
          key={product.category} />
      );
    }
    rows.push(
      <ProductRow
        product={product}
        key={product.name} />
    );
    lastCategory = product.category;
  });

  return (
    <table>
      <thead>
        <tr>
          <th>Name</th>
          <th>Price</th>
        </tr>
      </thead>
      <tbody>{rows}</tbody>
    </table>
  );
}
// 搜索栏组件
function SearchBar() {
  return (
    <form>
      <input type="text" placeholder="Search..." />
      <label>
        <input type="checkbox" />
        {' '}
        Only show products in stock
      </label>
    </form>
  );
}
// 要实现的筛选表界面组件 最终结果
function FilterableProductTable({ products }) {
  return (
    <div>
      <SearchBar />
      <ProductTable products={products} />
    </div>
  );
}
// 静态 数据
const PRODUCTS = [
  {category: "Fruits", price: "$1", stocked: true, name: "Apple"},
  {category: "Fruits", price: "$1", stocked: true, name: "Dragonfruit"},
  {category: "Fruits", price: "$2", stocked: false, name: "Passionfruit"},
  {category: "Vegetables", price: "$2", stocked: true, name: "Spinach"},
  {category: "Vegetables", price: "$4", stocked: false, name: "Pumpkin"},
  {category: "Vegetables", price: "$1", stocked: true, name: "Peas"}
];
// 导出可以在外部引用
export default function App() {
  return <FilterableProductTable products={PRODUCTS} />;
}

第三步我们该干什么呢?

为了让界面实现交互,我们找出必须要用state表示的数据。

什么数据要用state表示呢? 这是React官网给出的建议:“考虑将 state 作为应用程序需要记住改变数据的最小集合。” 我们一起来看它的分析:

“ 现在考虑示例应用程序中的每一条数据:

  1. 产品原始列表
  2. 搜索用户键入的文本
  3. 复选框的值
  4. 过滤后的产品列表

其中哪些是 state 呢?标记出那些不是的:

  • 随着时间推移 保持不变?如此,便不是 state。
  • 通过 props 从父组件传递?如此,便不是 state。
  • 是否可以基于已存在于组件中的 state 或者 props 进行计算?如此,它肯定不是state!

剩下的可能是 state。

让我们再次一条条验证它们:

  1. 原始列表中的产品 被作为 props 传递,所以不是 state
  2. 搜索文本似乎应该是 state,因为它会随着时间的推移而变化,并且无法从任何东西中计算出来。
  3. 复选框的值似乎是 state,因为它会随着时间的推移而变化,并且无法从任何东西中计算出来。
  4. 过滤后列表中的产品 不是 state,因为可以通过被原始列表中的产品,根据搜索框文本和复选框的值进行计算

这就意味着只有搜索文本和复选框的值是 state!非常好!” 简要来说就是数据在和用户交互过程中产生的变化的数据,要用state 来管理。

第四步

验证 state 应该被放置在哪里

我们现在知道了有两个数据,一个是输入框的值,一个是一个复选框的值, ProductTable这个组件需要根据这两个值来筛选出要渲染的动态页面。SearchBar这个组件要根据一个是输入框的值来渲染输入的数据。这两个state要放在ProductTableSearchBar的父组件,所以我们要把这两个state放到FilterableProductTable,使得ProductTableSearchBar都能得到通过state方式产生的数据

function FilterableProductTable({ products }) {  


const [filterText, setFilterText] = useState('');  


const [inStockOnly, setInStockOnly] = useState(false);
//  ······ 其他省略
}

然后,filterTextinStockOnly 作为 props 传递至 ProductTableSearchBar

<div>

  <SearchBar

    filterText={filterText}

    inStockOnly={inStockOnly} />

  <ProductTable

    products={products}

    filterText={filterText}

    inStockOnly={inStockOnly} />

</div>

我们一起看看组件内是怎么使用传入的参数:

function SearchBar({ filterText, inStockOnly }) {  


return (  


<form>  

<input  
type="text"  
value={filterText}  
placeholder="Search..."/>

</form> )
}

第五步

步骤五:添加反向数据流

当state的数据和组件里的值绑定后,比如 SearchBar里的

 <input  
type="text"  
value={filterText}  
placeholder="Search..."/>

当input的value和{filterText} 绑定,如果我们不主动添加响应value变化的函数的话,React会忽视你的输入。效果如下

录屏_20250608_021217.gif 敲多少回车都没用。

SearchBar 中,添加一个 onChange 事件处理器,使用其设置父组件的 state:

function SearchBar({

  filterText,

  inStockOnly,

  onFilterTextChange,

  onInStockOnlyChange

}) {

  return (

    <form>

      <input

        type="text"

        value={filterText}

        placeholder="搜索"

        onChange={(e) => onFilterTextChange(e.target.value)}

      />

      <label>

        <input

          type="checkbox"

          checked={inStockOnly}

          onChange={(e) => onInStockOnlyChange(e.target.checked)}

现在应用程序可以完整工作了!

录屏_20250608_021511.gif 最后附上完整源码App.jsx文件里的所有内容

import { useState } from 'react';

function FilterableProductTable({ products }) {
  const [filterText, setFilterText] = useState('');
  const [inStockOnly, setInStockOnly] = useState(false);

  return (
    <div>
      <SearchBar
        filterText={filterText}
        inStockOnly={inStockOnly}
        onFilterTextChange={setFilterText}
        onInStockOnlyChange={setInStockOnly} />
      <ProductTable
        products={products}
        filterText={filterText}
        inStockOnly={inStockOnly} />
    </div>
  );
}

function ProductCategoryRow({ category }) {
  return (
    <tr>
      <th colSpan="2">
        {category}
      </th>
    </tr>
  );
}

function ProductRow({ product }) {
  const name = product.stocked ? product.name :
    <span style={{ color: 'red' }}>
      {product.name}
    </span>;

  return (
    <tr>
      <td>{name}</td>
      <td>{product.price}</td>
    </tr>
  );
}

function ProductTable({ products, filterText, inStockOnly }) {
  const rows = [];
  let lastCategory = null;

  products.forEach((product) => {
    if (
      product.name.toLowerCase().indexOf(
        filterText.toLowerCase()
      ) === -1
    ) {
      return;
    }
    if (inStockOnly && !product.stocked) {
      return;
    }
    if (product.category !== lastCategory) {
      rows.push(
        <ProductCategoryRow
          category={product.category}
          key={product.category} />
      );
    }
    rows.push(
      <ProductRow
        product={product}
        key={product.name} />
    );
    lastCategory = product.category;
  });

  return (
    <table>
      <thead>
        <tr>
          <th>Name</th>
          <th>Price</th>
        </tr>
      </thead>
      <tbody>{rows}</tbody>
    </table>
  );
}

function SearchBar({
  filterText,
  inStockOnly,
  onFilterTextChange,
  onInStockOnlyChange
}) {
  return (
    <form>
      <input
        type="text"
        value={filterText} placeholder="Search..."
        onChange={(e) => onFilterTextChange(e.target.value)} />
      <label>
        <input
          type="checkbox"
          checked={inStockOnly}
          onChange={(e) => onInStockOnlyChange(e.target.checked)} />
        {' '}
        Only show products in stock
      </label>
    </form>
  );
}

const PRODUCTS = [
  {category: "Fruits", price: "$1", stocked: true, name: "Apple"},
  {category: "Fruits", price: "$1", stocked: true, name: "Dragonfruit"},
  {category: "Fruits", price: "$2", stocked: false, name: "Passionfruit"},
  {category: "Vegetables", price: "$2", stocked: true, name: "Spinach"},
  {category: "Vegetables", price: "$4", stocked: false, name: "Pumpkin"},
  {category: "Vegetables", price: "$1", stocked: true, name: "Peas"}
];

export default function App() {
  return <FilterableProductTable products={PRODUCTS} />;
}

如果有有不理解的地方,可以去React 哲学 – React 中文文档看看,这篇博客基本就是基于这个文档写的。

❌
❌