学习 React 需要具备的 JavaScript 知识

发布时间 2023-03-25 09:50:19作者: shadow_D

学习 React 需要具备的 JavaScript 知识

为什么要学习 React?

  • React 可以与任何其他库或框架无缝集成,因为 React 是一个仅视图库(它是 Model View C ontroler MVC 架构 UI 模式的视图部分),使用任何其他框架和库的自由使实验和创新成为可能
  • React 有一个相对简单的 API,因此没有陡峭的学习曲线
  • JSX 代表 Javascript Syntax Extension,它允许您在 JavaScript 中使用 HTML 和 XML
  • React 实现了单向数据流,这使得对您的应用程序的推理变得容易
  • React 是一个非常受欢迎且不断发展的库,这意味着 React 社区很大,而且还在不断增长

要学习 React 需要 JavaScript,当然建议直接 ECMAScript,关系参考 ECMAScript-know

入门知识

变量、运算符、表达式

在 2015 年之前,唯一可用于声明变量的关键字是 var 关键字,随着 JavaScript ES6 的引入,出现的两个新关键字是 constlet

  • const 声明一个只读的常量,一旦声明,常量的值就不能改变
  • let 声明一个块级作用域的本地变量,可选地可以初始化为一个值
  • var 声明一个函数作用域或全局作用域的变量,可选地可以初始化为一个值,实际上已经被 letconst 取代

怎样用它们

  • 不要使用 var,因为 let 和 const 更具体
  • 默认值使用 const,因为它不能被重新分配或重新声明
  • let 将来要重新分配变量时使用
  • 使用频率 const > let > var

JavaScript 运算符可以大致分为以下几类:

  • 算术运算符:+-*/%++--
  • 赋值运算符:=+=-=*=/=%=<<=>>=>>>=&=^=|=
  • 逻辑运算符:&&||!
  • 比较运算符:==!====!==>>=<<=
  • 位运算符:&|^~<<>>>>>
  • 条件运算符:? :
  • 字符串运算符:+
  • 逗号运算符:,
  • typeof 运算符:typeof
  • instanceof 运算符:instanceof
  • delete 运算符:delete
  • void 运算符:void
  • in 运算符:in
// 部分示例
console.log("2 + 3 = " + (2 + 3))           // 2 加上 3 的和
console.log("2 - 3 = " + (2 - 3))           // 2 减去 3 的差
console.log("2 * 3 = " + (2 * 3))           // 2 乘以 3 的积
console.log("6 / 3 = " + (6 / 3))           // 6 除以 3 的商
console.log("7 / 3 = " + (7 / 3))           // 7 除以 3 的商
console.log("7 % 3 = " + (7 % 3))           // 7 除以 3 的余数
console.log("2 ** 3 = " + (2 ** 3))         // 2 的 3 次方
console.log("1 OR 1 = " + (1 || 1))         // 1 OR 1
console.log("1 OR 0 = " + (1 || 0))         // 1 OR 0
console.log("0 OR 0 = " + (0 || 0))         // 0 OR 0
console.log("1 AND 1 = " + (1 && 1))        // 1 AND 1
console.log("1 AND 0 = " + (1 && 0))        // 1 AND 0
console.log("0 AND 0 = " + (0 && 0))        // 0 AND 0
console.log(!true)                          // NOT TRUE
console.log(!1)                             // NOT TRUE
console.log(!false)                         // NOT FALSE
console.log(!0)                             // NOT FALSE
console.log(1 > 2)                          //false
console.log(1 < 2)                          //true
console.log(1 == 1)                         //true
console.log(1 != 1)                         //false

表达式是一种计算值的 JavaScript 语法结构,表达式可以是字面量、变量、关键字、运算符、函数调用等,下面提到了 JavaScript 中使用的一些基本表达式和关键字:

  • this: 指向当前对象
  • super: 调用对象父对象的方法,例如调用父对象的构造函数
  • function: 用于定义函数
  • function*: 用于定义生成器函数
  • async function: 用于定义异步函数

JavaScript 中引入的另一个语言特性称为对象解构,它允许您将对象的属性分配给变量,这样就可以使用变量而不是对象属性来访问属性值

const student = {
    ID: '21',
    NAME: 'Jhon',
    GPA: '3.0',
};

let id = student.ID;
let name = student.NAME;
let gpa = student.GPA;

console.log(id);
console.log(name);
console.log(gpa);

// 使用对象解构
const { id, name, gpa } = student;
console.log(id);
console.log(name);
console.log(gpa);

// 使用对象解构并重命名
const { id: studentId, name: studentName, gpa: studentGpa } = student;
console.log(studentId);
console.log(studentName);
console.log(studentGpa);

解构的另一个例子可能是:

// 正常写法
function Greeting(props) {
  return <h1>{props.greeting}</h1>;
}

// 使用解构
function Greeting({ greeting }) {
  return <h1>{greeting}</h1>;
}

解构也适用于 JavaScript 数组:

// 其余解构到 rest 数组
const { users, ...rest } = this.state;

在 React 中经常使用的另一个 JavaScript 特性是 Spread Operator,它允许您将数组或对象的内容展开,假设我们想要连接两个数组,我们要么通过使用 concat 函数来实现,或者我们可以使用 Spread Operator 做同样的事情

// 使用 concat 函数
a = [1,2,3];
b = [4,5,6];
c = a.concat(b);
console.log("c: " + c);

// 使用 Spread Operator
a = [1,2,3];
b = [4,5,6];
c = [...a, ...b];
console.log("c: " + c);

// 在 React 中,您可以使用 Spread Operator 组合两个对象,并向该对象添加额外的属性
const person = { name: "Jhon"};
const student = { ID: "21", GPA: "3.0"};

const new_object = { ...person, ...student, semester: '3'};
console.log(new_object);

函数

函数是 JavaScript 中的一等公民,这意味着函数可以像其他任何 JavaScript 对象一样被传递、存储和使用。函数是一种特殊的对象,它可以被调用,它可以有属性和方法,它可以被传递给其他函数,它可以从其他函数返回,它可以具有自己的属性和方法。

典型的 JavaScript 函数具有以下特征:

  • 关键字 function
  • 函数名
  • 参数列表
  • 函数体
  • 返回值

命名函数和匿名函数有什么区别?

  • 命名函数可以在函数体内部递归调用自身,而匿名函数则不行,因为匿名函数没有名称,因此无法在函数体内部递归调用自身
  • 命名函数可以在函数体外部调用自身,而匿名函数则不行,因为匿名函数没有名称,因此无法在函数体外部调用自身

声明 JavaScript 函数的方法

  • 函数声明:这是在 JavaScript 中声明函数的最典型方法

    使用此方法声明的所有函数都允许提升;意味着它们可以在声明之前使用

    function function_name(Arg1, Arg2..){}                      // 函数声明
    
  • 函数表达式:这是最常用的类型,它可以是命名函数表达式或匿名函数表达式,当您想将函数作为对象分配给变量时,它最适合使用

    var var_name = function function_name(Arg1,Arg2..){};       // 命名函数表达式
    var var_name = function(Arg1, Arg2..){};                    // 匿名函数表达式
    
  • 生成器函数:它用于声明一个生成器函数,该函数使用 yield 关键字返回一个 Generator-Iterator 对象,next 稍后可以调用该对象的方法

    function* function_name(Arg1, Arg2..){}                     // 生成器函数声明
    
  • 生成器函数表达式:这与我们上面刚刚讨论的类型非常相似,唯一的区别是它允许从函数中省略名称

    var var_name = function* function_name(Arg1, Arg2..){};     // 生成器函数表达式
    
  • 箭头函数:在 ES6 中引入此类函数

    为函数表达式编写更短的语法并摆脱 this 值,如果函数只接受一个参数,则可以排除函数括号;如果函数体内只有一个语句,您也可以删除大括号

    var var_name = (Arg1, Arg2..) => {};                        // 箭头函数表达式
    
  • 函数构造器:这是最不推荐的函数声明方式

    在这里,Function 关键字实际上是一个创建新函数的构造函数。传递给构造函数的参数成为新创建函数的参数,最后一个参数是一个字符串,它被转换成一个函数体。这可能会导致安全和引擎优化问题,这就是为什么始终不建议使用它的原因。

    var var_name = new Function("Arg1", "Arg2..", "function_body");  // 函数构造器
    

在 React 中经常使用它们来使事情变得更高效和更简单(事件处理、防止错误等)

// 不使用箭头函数
const students = [
  { ID: 1, present: true},
  { ID: 2, present: true},
  { ID: 3, present: false}, 
];

const presentStudents = students.filter(function(student){return student.present;});
console.log(presentStudents);

// 使用箭头函数
const students = [
  { ID: 1, present: true},
  { ID: 2, present: true},
  { ID: 3, present: false}, 
];
const presentStudents = students.filter((student) => student.present);
console.log(presentStudents);

在编写箭头函数或将现有函数转换为箭头函数时,必须牢记以下几点:

  • 如果函数体只有一行,则可以省略 return 关键字和大括号
  • 如果函数只有一个参数,则可以省略括号
  • 如果函数没有参数,则必须使用空括号

React 中的高阶函数

import React from 'react';

export default class App extends React.Component {

  constructor(props){
    super(props);  
    
    this.state = {
    query: '',
    };
    
    this.onChange=this.onChange.bind(this);
  }
  
  onChange(event) {
    this.setState({ query: event.target.value });
  }
  
  render() {
  const users = [
      { name: 'Robin' },
      { name: 'Markus' },
    
    ];
    return (
      <div>
        <ul>
          { users
            .filter(user => this.state.query === user.name)
            .map(myuser => <li>{myuser.name}</li>)
          }
        </ul>
        <input
          type="text"
          onChange={this.onChange}
        />
      </div>
    );
  }
}

并不总是希望提取函数,因为它会增加不必要的复杂性,但另一方面,它可以对 JavaScript 产生有益的效果

import React from 'react';

function doFilter(user) {
  return query === user.name;
}

export default class App extends React.Component {

  constructor(props){
    super(props);  
    
    this.state = {
    query: '',
    };
    
    this.onChange=this.onChange.bind(this);
  }
  
  onChange(event) {
    this.setState({ query: event.target.value });
  }
  
  render() {
  const users = [
      { name: 'Robin' },
      { name: 'Markus' },
    
    ];
    return (
      <div>
        <ul>
          { users
            .filter(doFilter)
            .map(myuser => <li>{myuser.name}</li>)
          }
        </ul>
        <input
          type="text"
          onChange={this.onChange}
        />
      </div>
    );
  }
}

之前的实现不起作用,因为该 doFilter() 函数需要了解来自状态的查询属性,因此您可以通过将它与另一个导致高阶函数的函数包装起来,将其传递给该函数

import React from 'react';

function doFilter(query) {
  return function (user) {
    return query === user.name;
  }
}

export default class App extends React.Component {

  constructor(props){
    super(props);  
    
    this.state = {
    query: '',
    };
    
    this.onChange=this.onChange.bind(this);
  }
  
  onChange(event) {
    this.setState({ query: event.target.value });
  }
  
  render() {
  const users = [
      { name: 'Robin' },
      { name: 'Markus' },
    
    ];
    return (
      <div>
        <ul>
          { users
            .filter(doFilter(this.state.query))
            .map(myuser => <li>{myuser.name}</li>)
          }
        </ul>
        <input
          type="text"
          onChange={this.onChange}
        />
      </div>
    );
  }
}

通过使用 JavaScript ES6 箭头函数,可以使高阶函数更加简洁

import React from 'react';

const doFilter = query => user =>
   query === user.name;

export default class App extends React.Component {

  constructor(props){
    super(props);  
    
    this.state = {
    query: '',
    };
    
    this.onChange=this.onChange.bind(this);
  }
  
  onChange(event) {
    this.setState({ query: event.target.value });
  }
  
  render() {
  const users = [
      { name: 'Robin' },
      { name: 'Markus' },
    
    ];
    return (
      <div>
        <ul>
          { users
            .filter(doFilter(this.state.query))
            .map(myuser => <li>{myuser.name}</li>)
          }
        </ul>
        <input
          type="text"
          onChange={this.onChange}
        />
      </div>
    );
  }
}

将这些函数提取到 React 组件外部的(高阶)函数中也有利于单独测试 React 的本地状态管理

import React from 'react';

export const doIncrement = state =>
  ({ counter: state.counter + 1 });

export const doDecrement = state =>
  ({ counter: state.counter - 1 });

export default class Counter extends React.Component {
  state = {
    counter: 0,
  };

  onIncrement = () => {
    this.setState(doIncrement);
  }

  onDecrement = () => {
    this.setState(doDecrement);
  }

  render() {
    return (
      <div>
        <p>{this.state.counter}</p>

        <button onClick={this.onIncrement} type="button">Increment</button>
        <button onClick={this.onDecrement} type="button">Decrement</button>
      </div>
    );
  }
}

在 React 中 Map, Reduce, Filter

  • Map - 用于遍历数组,返回一个新数组
  • Reduce - 用于遍历数组,返回一个值
  • Filter - 用于过滤数组,返回一个新数组

JavaScript 类和对象

类对于 JavaScript 来说相对较新,因为以前也只有 JavaScript 的原型链可以用于继承,JavaScript 中的类是原型链上的语法糖

类是一种独立的语法结构,它可以用于创建对象【它们可以包含构造函数、属性、方法】具有自己的上下文——方法(函数)和属性(变量)的集合

// 创建一个类,使用关键字 class
class Developer {

  // 构造函数,使用关键字 constructor
  constructor(firstname, lastname) {
    this.firstname = firstname;
    this.lastname = lastname;
  }

  getName() {
    return `${this.firstname} ${this.lastname}`;
  }
}

// 可以使用关键字创建一个新对象 new
var me = new Developer('Robin', 'Wieruch');

console.log(me.getName());

类——如前所述——对于 Javascript 来说是相对较新的,对象很像类

let computer = { brand : 'HP', RAM : '8 GB', clockspeed : "2 GHz"};

// 对象定义有空格和换行
let computer2 = { 
  brand : 'HP',
  RAM : '8 GB',
  clockspeed : "2 GHz"
};

// 对象也可以使用方法
let computer3 = {
  brand : 'HP',
  RAM : '8 GB',
  clockspeed : "2 GHz",
  
  printRam() {
    console.log(this.RAM)
  }
}

我们将研究臭名昭著的 this 关键字在显式、隐式、new 和全局绑定的上下文中如何在 JavaScript 中发挥作用

  • call() - 用于调用一个对象的一个方法,以另一个对象替换当前对象
  • apply() - 与 call() 方法类似,区别是接受的是参数数组
  • bind() - 会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入 bind() 方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数
// call() 方法示例
let obj = {num: 2};
let addToThis = function(a, b, c) {
  return this.num + a + b + c;
};
addToThis.call(obj, 1, 2, 3); // 8

// apply() 方法示例
let arr = [1, 2, 3];
let sum = function(a, b, c) {
  return a + b + c;
};
sum.apply(null, arr); // 6

// bind() 方法示例
let module = {
  x: 42,
  getX: function() {
    return this.x;
  }
};
let getX = module.getX.bind(module);  // 42

类继承

  • 类用于面向对象编程中的继承:

    • 继承使新类能够继承现有类的属性和方法
    • 另一个类继承的类称为 超类或基类
    • 从超类继承的类称为 子类或派生类
  • 在 JavaScript 中,extends 关键字可用于从一个类继承另一个类

随着您在 React 之旅中的进步,您将意识到与类组合相比,继承在某种程度上是有限的,如果想了解为什么可以参考 Gorilla/Banana Problem

  • 继承遵循 IS-A 原则,这意味着子类是父类
  • 组合使用 HAS-A 原则,因此类可以包含其他类的实例

组合相对于继承的优势

  • 类别分类法不必预先定义,这使代码动态并适应变化
  • 由于必须对代码进行较少的更改,因此引入的错误也会减少
  • 代码更可重用

模块化

模块化是一种将代码分解为可重用单元的方法,这些单元可以独立开发和维护

  • 默认情况下,模块中定义的类是私有的,不能被项目中存在的其他文件访问,但是可以通过导出类来使其公开
  • 始终建议为每个文件只定义一个类,以使其具有很强的内聚性

export 和 import 语句作用

  • export 语句用于从模块中导出函数、对象或原始值
  • import 语句用于从其他模块导入函数、对象或原始值

模块命名和默认导出

  • 模块名称是模块的文件名,但是可以在模块中使用 export default 语句来指定默认导出的名称

    export default class_name {....}
    
  • 从模块导入默认导出类时,可以使用任何名称,但是建议使用与模块名称相同的名称

    import class_name from module_name;
    

React 中的库

  • React Router - 用于管理 React 应用程序中的路由
  • Redux - 用于管理 React 应用程序中的状态
  • React Bootstrap - 用于创建 React 应用程序的 Bootstrap 组件
  • React Native - 用于创建原生移动应用程序的 React 库
  • React Native Web - 用于在 Web 上创建 React Native 应用程序的库
  • React Native Elements - 用于创建 React Native 应用程序的 UI 组件
  • ...

React

React 是一个用于构建用户界面的 JavaScript 库

  • 组件是 React 应用程序的基本构建块
  • 组件是一个独立的、可重用的代码块,它可以用于构建复杂的 UI
  • 组件可以包含其他组件,这使得构建复杂的 UI 变得更容易

学习修改我们的组件并使用 props 使它们更加动态

  • 组件可以接受输入,这些输入称为 props
  • props 是从父组件传递给子组件的数据
  • props 是只读的,这意味着子组件不能修改它们

React 中的状态

  • 组件可以拥有自己的状态
  • 组件的状态是一个对象,它包含组件的数据
  • 组件的状态是私有的,组件可以通过 setState() 方法来更新它

React 中的生命周期

  • React 组件有三种状态:挂载、更新、卸载
  • 挂载是指将组件插入 DOM 的过程,通过 componentWillMount() 实现挂载
  • 更新是指重新渲染组件以更新 UI 的过程,通过 componentDidUpdate() 实现更新
  • 卸载是指将组件从 DOM 中删除的过程,通过 componentWillUnmount() 实现卸载
  • 每个组件都有一个生命周期,它包含了组件从创建到销毁的过程

React 组件 Syntax

  • React 组件可以使用 ES6 类语法或 ES5 React.createClass() 方法来创建
  • 在早期阶段,React.createClass() 方法是创建 React 类组件的默认方式
  • 如今,它不再被使用了,因为随着 JavaScript ES6 的兴起,之前使用的 React 类组件语法成为了默认语法

Functions 在 React 中最为组件

  • 函数组件是一种简单的组件,它只接受 props 作为输入并返回 React 元素作为输出
  • 函数组件是无状态的,这意味着它们不会存储状态
  • 函数组件是纯函数,这意味着它们不会修改输入并且具有相同的输入总是返回相同的输出

当然此处对 React 的学习还有很多,但是我觉得这些是最基础的,如果你想要深入学习 React,可以参考官方文档,网址:<React>