普通视图

发现新文章,点击刷新页面。
昨天 — 2026年4月20日首页

React新手小白:如何入门 React 响应式交互与 JSX 艺术

作者 暗不需求
2026年4月19日 21:04

一 什么是React?? 他是基于什么的? 学了它有什么用呢??

1. 核心定义:声明式与组件化

React 的核心定位是 “用于构建用户界面的 JavaScript 库” 。它主要关注 MVC 架构中的 V(View,视图层)

  • 声明式编程 (Declarative): 在 React 中,你只需要描述界面在某种“状态”下应该长什么样,而不需要手动操作 DOM 去更新界面。当数据变动时,React 会自动处理界面的高效更新。
  • 组件化 (Component-Based): 这是 React 的灵魂。你可以将复杂的 UI 拆分成一个个独立、可复用的“组件”(Component)。每个组件拥有自己的逻辑和样式,最终像搭积木一样拼成完整的应用。

完整项目链接:gitee.com/hong-strong…


2. 三大核心技术支柱

虚拟 DOM (Virtual DOM)

传统的网页操作(真实 DOM)非常昂贵且缓慢。React 在内存中维护了一份 UI 的轻量级副本,即“虚拟 DOM”。

  1. 当状态发生变化时,React 先更新虚拟 DOM。
  2. 通过 Diff 算法 对比新旧虚拟 DOM 的差异。
  3. 仅将真正发生变化的部分更新到真实网页上(这一过程称为 Reconciliation)。

JSX 语法

React 引入了 JSX(JavaScript XML),允许你在 JavaScript 代码中直接编写类似 HTML 的结构。这使得 UI 逻辑与标记语言高度耦合,代码直观且易于维护。

JavaScript

function Welcome() {
  return <h1>Hello, React!</h1>;
}

单向数据流 (One-Way Data Flow)

在 React 中,数据总是从父组件通过 props 流向子组件。这种单向的数据流动让应用的逻辑变得可预测,调试时也更容易追踪数据的源头。


3. 为什么 React 如此受欢迎?

  • 极高的性能: 得益于虚拟 DOM 和优秀的渲染机制。
  • 强大的生态: 拥有庞大的开源社区,无论是状态管理(Redux, Zustand)、路由(React Router),还是 UI 组件库(Ant Design, MUI),都能找到成熟的方案。
  • 跨平台能力: 学习了 React 之后,你可以通过 React Native 构建原生移动应用(iOS/Android),实现“一次学习,随处编写”。
  • Hooks 革命: 自 React 16.8 引入 Hooks 以来,函数式组件(Functional Components)成为了主流,极大地简化了状态管理和副作用处理的复杂性。

二 那作为一个小白 如何初始化一个react项目呢?

我这边选择使用的是Vite,因为 Vite 是目前前端工程化的首选工具。它启动极快,热更新(HMR)几乎是瞬间完成。

步骤:

  1. 打开终端,输入以下命令:

    npm create vite
    
  2. 按照提示进行选择:

    • Select a framework: 选择 React
    • Select a variant: 选择 JavaScriptTypeScript(根据情况选择语言)
  3. 进入目录并启动:

    cd my-react-app
    npm install
    npm run dev
    
  4. 得到网址: 运行上述代码后,你会在终端得到一个类似于http://localhost:5173 网址,这样你就成功运行了你的第一个React项目 项目结构如下图所展示:

df1b4f4bbd30dbe57ece32553b1a07d5.png

三: React 的核心用法。

1:理解“挂载” —— 应用的起点

每个 React 应用都有一个入口文件(通常是 main.jsx),它的任务是将我们写的 React 组件“挂载”到真实的 HTML 页面上。

import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.jsx'

// 使用 createRoot 找到 HTML 中的 root 节点,并将根组件 <App /> 渲染进去
createRoot(document.getElementById('root')).render(
  <StrictMode>
    <App />
  </StrictMode>,
)

2:组件化开发 —— 像搭积木一样写网页

在 React 中,函数就是组件。组件是开发的基本单位,它将 HTML、CSS 和 JS 逻辑封装在一起,完成独立的功能。

我们可以将页面拆分成多个子组件,然后在根组件中组合它们:

// 定义子组件:头部
function JuejinHeader() {
  return (
    <header><h1>掘金首页</h1></header>
  )
}

// 定义子组件:列表
const Articles = () => <div>文章列表内容</div>;

// 在 App 根组件中组合它们
function App() {
  return (
    <div>
      <JuejinHeader />
      <main>
        <Articles />
      </main>
    </div>
  )
}

3:掌握 JSX —— 在 JS 中书写 UI

JSX(XML in JS)是 React 的模板语法,它让我们能在 JavaScript 里直接写 HTML 结构。

  • 语法糖:JSX 最终会被转化为 createElement 渲染函数。
  • 规则:JSX 最外层只能有一个根元素(可以使用空标签 <></> 作为文档碎片)。
  • 属性名:由于 class 是 JS 关键字,在 JSX 中定义类名要使用 className

第四步:响应式状态 —— 让页面“动”起来

React 的核心特性之一是响应式(Reactive) 。我们使用 useState 来定义数据状态,当状态改变时,React 会自动更新 UI。

1. 定义与更新状态

import { useState } from 'react';

function App() {
  // name 是状态值,setName 是更新它的函数
  const [name, setName] = useState("vue");

  // 3秒后自动将 "vue" 改为 "react"
  setTimeout(() => {
    setName("react"); 
  }, 3000);

  return <h1>Hello {name}!</h1>;
}

2. 条件渲染与列表渲染

你可以利用原生 JS 的逻辑(如三元运算符或 map 函数)来控制界面的显示:

{/* 列表渲染:记得给每个子项添加唯一的 key */}
<ul>
  {todos.map(todo => (
    <li key={todo.id}>{todo.title}</li>
  ))}
</ul>

{/* 条件渲染:登录逻辑切换 */}
{isLoggedIn ? <div>已登录</div> : <div>未登录</div>}
<button onClick={() => setIsLoggedIn(!isLoggedIn)}>
  {isLoggedIn ? "退出" : "登录"}
</button>

四 React基本知识总结

核心维度 知识要点 代码示例 / 实现细节
组件定义 组件是 React 的基本开发单位,通常表现为返回 JSX 的 JavaScript 函数 function App() { return <div>...</div> }
JSX 语法 XML in JS。一种在 JS 中描述 UI 结构的语法扩展,本质是 createElement 的语法糖。 const element = <h2>JSX 语法扩展</h2>;
JSX 约束 1. 必须有且仅有一个根元素; 2. 标签名大写为组件,小写为原生 HTML。 return (<> ... </>) (使用 Fragments 文档碎片)
属性命名 由于 JS 关键字限制,HTML 的 class 属性需写作 className <span className="title">...</span>
组件化思维 像“搭积木”一样。通过组件树嵌套子组件来构建复杂页面,代替传统的 DOM 树。 <main> <Articles /> <aside><Checkin /></aside> </main>
响应式状态 使用 useState 定义数据。当状态改变时,React 会自动触发界面更新(数据驱动视图)。 const [name, setName] = useState("vue");
列表渲染 使用原生 JS 的 .map() 方法循环数据,且每个子项必须提供唯一的 key todos.map(todo => <li key={todo.id}>{todo.title}</li>)
条件渲染 在 JSX 中使用 三元运算符 或逻辑运算符根据状态显示不同的内容。 {isLoggedIn ? <div>已登录</div> : <div>未登录</div>}
事件处理 使用驼峰式命名的属性绑定交互函数(如 onClick)。 <button onClick={toggleLogin}>登录</button>
项目挂载 应用的入口。使用 createRoot 找到容器并调用 render 挂载根组件。 createRoot(document.getElementById('root')).render(<App />)

总结:欢迎来到 React 的世界

学习React其实不难,只要你登上了这几个台阶,一步一个脚印,视野就会豁然开朗:

“数据是灵魂,组件是肉体,JSX 是灵魂与肉体对话的诗篇。”

你已经掌握了现代前端最强大的武器,请记住这三条锦囊:


1. 核心心法的内化

  • 状态即真相:通过 useState 让数据驱动视图,数组返回的状态值与更新函数是你操控页面的唯一魔法。
  • 组件即模块:像搭积木一样,将复杂的页面拆解成 HeaderArticlesCheckin 等独立单元,这会让你从“搬砖工”晋升为“包工头”。
  • JSX 即桥梁:这种将 XML 融入 JS 的语法,是你描述用户界面最直观、最高效的方式。

2. 给新手的进阶建议

  • 拥抱 Vite 的速度:不要在环境配置上浪费太多时间,利用 Vite 的极速热更新去快速验证你的每一个奇思妙想。
  • 尊重单向数据流:数据总是从父组件流向子组件,这种“长幼有序”的传递方式会让你的代码逻辑极其清晰。
  • 报错是你的导师:React 的报错信息往往非常直观,它们不是阻碍,而是指引你优化代码的地图。

3. 最后的行动指南

与其在文档里反复徘徊,不如在编辑器里反复横跳。

  • 多拆分:如果一个组件超过了 100 行,试着把它拆成两个。
  • 多联想:看到任何一个网站,试着在脑海中用组件树去拆解它。
  • 多实践:React 的魅力不在于“看懂”,而在于当你写下 setName 时,页面如你所愿跳动的那一瞬间。

多敲代码 多学知识 多上手实践 我相信你我都能做得更好!

昨天以前首页

手写 instanceof:从原型链聊聊 JS 的实例判断

作者 暗不需求
2026年4月14日 19:10

大家好,我是平时爱折腾前端JavaScript的小伙。最近在看 JS 继承和原型相关的东西并且进行学习,发现我身边的人学习(包括我自己以前) instanceof 的理解还停留在“能判断对象是不是某个类的实例”这个表面。根据师兄的口述,仅仅了解这些是不够面试的。今天就借着这个机会,结合实际代码,一步步手写一个 instanceof,顺便把原型链、继承这些概念也捋清楚。

先说说为什么需要 instanceof

在大项目里,尤其是多人协作的时候,你经常会拿到一个对象,却不知道它到底是从哪个构造函数来的,有哪些方法和属性可用。这时候 instanceof 就特别实用——它就像其他面向对象语言里的“类型检查”运算符,能快速告诉你“这个对象是不是某个类的实例”。

简单说,A instanceof B 的本质就是:A 的原型链上有没有 B 的 prototype。如果有,就返回 true;没有,就 false

这不是 JS 独有的概念,很多 OOP 语言都有类似的机制,但 JS 是基于原型的,所以它的实现特别“接地气”——全靠那条 __proto__ 链。

原型和原型链是什么?

先用一个最常见的例子感受一下(来自 Array):

<script>
const arr = []; // 其实就是 new Array()
console.log(arr.__proto__, arr.__proto__.constructor, arr.constructor);
console.log(arr.__proto__.__proto__,
  arr.__proto__.__proto__.constructor,
  arr.__proto__.__proto__.__proto__,
  arr.__proto__.__proto__.__proto__.__proto__);
</script>

你会看到:

  • arr.__proto__ 指向 Array.prototype
  • Array.prototype.__proto__ 又指向 Object.prototype
  • 最后 Object.prototype.__proto__null,链条结束

这就是原型链:每个对象都有一个 __proto__ 属性(隐式原型),它指向自己构造函数的 prototype(显式原型)。沿着这条链一直往上找,就能找到所有能用的属性和方法(包括 toStringhasOwnProperty 这些)。

理解了这条链,instanceof 就很好解释了。

原生的 instanceof 是怎么工作的?

看下面这个经典的继承例子:

function Animal() {}
function Person() {}

Person.prototype = new Animal();
const p = new Person();

console.log(p instanceof Person);  // true
console.log(p instanceof Animal);  // true

pPerson 的实例,它的原型链上是 Person.prototype → Animal.prototype → Object.prototype → null,所以它既是 Person 的实例,也是 Animal 的实例。

手写一个 isInstanceOf

现在我们来自己实现一个。核心思路就一句话:从 left 的 __proto__ 开始,一路往上找,看能不能找到 right.prototype

完整代码如下(直接复制就能跑):

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>手写 instanceof</title>
</head>
<body>
<script>
// B 是否出现在 A 的原型链上
function isInstanceOf(left, right) {
  // 防止 right 不是函数
  if (typeof right !== 'function') {
    return false;
  }
  
  let proto = left.__proto__;
  while (proto) {
    if (proto === right.prototype) {
      return true;
    }
    proto = proto.__proto__; // 继续往上找,直到 null
  }
  return false;
}

function Animal() {}
function Cat() {}
Cat.prototype = new Animal();
function Dog() {}
Dog.prototype = new Animal();

const dog = new Dog();

console.log(isInstanceOf(dog, Dog));     // true
console.log(isInstanceOf(dog, Animal));  // true
console.log(isInstanceOf(dog, Object));  // true
console.log(isInstanceOf(dog, Cat));     // false
</script>  
</body>
</html>

这个函数和原生的 instanceof 行为几乎一致。注意两点小细节:

  1. 我们加了个 typeof right !== 'function' 的防护,防止传进来奇怪的东西报错。
  2. 循环结束条件是 proto 变成 null,这正是原型链的终点。

结合继承方式再看 instanceof

instanceof 最常出现在继承场景里。我们来对比几种常见的继承写法,看看它在每种方式下的表现。

1. 构造函数绑定继承(call/apply)

function Animal() {
  this.species = '动物';
} 
function Cat(name, color) {
  Animal.apply(this);  // 把 Animal 的属性绑到 this 上
  this.name = name;
  this.color = color;
}

const cat = new Cat('小黑', '黑色');
console.log(cat.species); // 动物

这种方式只继承了属性,没有把原型链连起来。所以 cat instanceof Animal 会是 false。如果你需要原型方法,就得配合后面两种方式用。

2. prototype 模式(推荐)

function Animal() {
  this.species = '动物';
}
function Cat(name, color) {
  this.name = name;
  this.color = color;
}

// 关键两步
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat; // 修复 constructor 指向

const cat = new Cat('小黑', '黑色');
console.log(cat instanceof Cat);    // true
console.log(cat instanceof Animal); // true

这里 Cat.prototype 直接指向一个 Animal 实例,原型链就连上了。记得一定要把 constructor 指回来,不然 cat.constructor 会指向 Animal,容易出 bug。

3. 直接继承 prototype(有坑)

function Animal() {}
Animal.prototype.species = '动物';

function Cat(name, color) {
  Animal.call(this);
  this.name = name;
  this.color = color;
}

Cat.prototype = Animal.prototype; // 直接引用
Cat.prototype.constructor = Cat;
Cat.prototype.sayHello = function() {
  console.log('hello');
};

const cat = new Cat('小黑', '黑色');
console.log(cat instanceof Cat);    // true
console.log(cat instanceof Animal); // true
console.log(Animal.prototype.constructor); // 变成了 Cat(副作用!)

这种写法性能好(不用 new 一个 Animal 实例),但会污染父类的 prototype。如果你在 Cat.prototype 上加方法,Animal 也能拿到,容易出意外。实际项目里还是推荐用第 2 种,或者用 Object.create(Animal.prototype) 做中介(空对象继承)。

结尾

手写 instanceof 其实就这么简单,核心就是遍历原型链。写完之后,你会对 JS 对象“到底是谁生的”这件事有更直观的理解。在大型项目里,它能帮你快速做类型守护、写工具函数,或者在框架里判断组件类型。

当然,原生 instanceof 已经够用了,我们手写主要是为了加深理解。下次再遇到“这个对象为啥有这个方法”“继承关系乱了”的时候,你就可以顺着 __proto__ 链自己排查了。

如果你也正在看原型链和继承,欢迎评论区一起讨论~代码我都放上去了,直接复制就能跑。希望这篇文章能让你少踩几个坑!并且希望你在面试的时候能拿下instanceof这一难点。早日拿下offer!

❌
❌