日期:2024年10月18日

React预备知识

本章内容是对前面JS基础知识的复习,复习内容主要涉及到React中常用且容易犯错产生疑惑的点。

如果你已经对JS基础知识掌握的非常牢固了,完全可以跳过本章。

变量声明

var & let

JS中声明变量(常量)的方式有三种:var、let、const(常量)。

var是JS中最古老的声明变量的方式,可谓历史悠久。但是现在的前端项目中并不推荐使用var关键字来声明变量,这其中最重要的原因是var声明的变量没有块作用域,以下列代码为例:

代码1-1:

for(var i=0; i<4; i++){
   // 随便写点什么
}
console.log(i); // 输出 4

代码1-2:

if(true){
var a = 10;
}
console.log(a); // 输出10

代码1-3:

if(false){
var a = 10;
}
console.log(a); // undefined

由于var声明的变量没有块作用域,所以代码1-1中的变量i虽然声明在了for循环中,但在for循环的外部依然可以访问到。代码1-2和代码1-3将变量声明在了if语句中,同样由于没有块作用域,在if语句的外部可以访问到变量a。代码1-3的条件表达式是为false,赋值(a = 10)并不会执行,但由于var是没有块作用域的,所以并不影响变量的声明,在外部依然可以打印出undefined。

和var不同,let声明的变量都具有块作用域,如果将上述三个代码中的var换成let,则通通都会报找不到变量的错误。这是因为,使用let声明的变量只在当前代码块中有效,在代码块外部无法访问。let的出现有效的隔离了代码块内外的变量,使得代码的结构更清晰,维护起来更加容易。要知道,在没有let的日子里,我们只能通过闭包来模拟块作用域。

总之,对于let和var来说,能用let绝对不用var!

const

const在JS中用来声明常量,所谓常量就是只能赋值一次的变量。在JS中对const的使用是非常频繁的,当一个变量用来保存一个对象时(函数或其他对象),为了避免变量被修改通常会使用const来声明。

三种声明方式中,第一优先使用的是const,如果希望变量被改变则使用let,至于var最好不要在代码中出现!

块作用域可以重新赋值是否推荐使用
const×
let
var××

解构和展开

解构赋值

通过解构赋值可以直接将数组中的元素,对象中的属性赋值给其他的变量。

代码1-4(数组解构):

let a, b, rest;
[a, b] = [10, 20]; // 第一个元素给a,第二个元素给b
console.log(a); // 10
console.log(b); // 20

[a, , b] = [10, 20, 30]; // 第一个元素给a,第三个元素给b,中间空出一个
console.log(a); // 10
console.log(b); // 30

[a, b, ...rest] = [10, 20, 30, 40, 50]; // 第一个元素给a,第二个元素给b,剩下的给rest
console.log(a); // 10
console.log(b); // 20
console.log(rest); // [30, 40, 50]

const [d, e, f] = [40, 50, 60] // 可以在赋值时直接声明变量
console.log(d); // 40
console.log(e); // 50
console.log(f); // 60

[a=5, b=7] = [1]; // 赋值是可以指定默认值
console.log(a); // 1
console.log(b); // 7

代码1-5 (对象解构)

let a, b;
({ a, b } = { a: 10, b: 20 }); //属性a赋值给变量a,属性b赋值给变量b
console.log(a); // 10
console.log(b); // 20

({a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40}); //属性a赋值给变量a,属性b赋值给变量b,其余的保存到rest中
console.log(a); // 10
console.log(b); // 20
console.log(rest); // {c: 30, d: 40}

({a = 10, b = 5} = {a: 3}); // 可以指定默认值
console.log(a); // 3
console.log(b); // 5

const {name, age} = {name:'孙悟空', age:18}; // 对象解构时如果使用const、let、var开头,可以不加()
console.log(name); // 孙悟空
console.log(age); // 18

const {len: width} = {len:100}; // 将对象的len属性值赋值给变量width
console.log(width); // 100

const {inner:{size}} = {a: 10, b: 20, inner:{size: 5}}; // 将对象中inner.size赋值给变量size
console.log(size) // 5

const {inner:{size:mySize}} = {a: 10, b: 20, inner:{size: 5}}; // 将对象中inner.size赋值给变量mySize
console.log(mySize); // 5

代码1-6(交换变量值)

let a = 1;
let b = 3;

[a, b] = [b, a]; // 交换变量a和b的值
console.log(a); // 3
console.log(b); // 1

const arr = [1,2,3];
[arr[2], arr[1]] = [arr[1], arr[2]]; // 交换数组中两个元素的位置
console.log(arr); // [1,3,2]

代码1-7(解构函数返回值)

function f() {
 return [1, 2];
}

let [a, b] = f();
console.log(a); // 1
console.log(b); // 2

代码1-8(嵌套解构)

const props = [
{ id: 1, name: '孙悟空'},
{ id: 2, name: '猪八戒'},
{ id: 3, name: '沙和尚'}
];

const [,, { name }] = props;

console.log(name); // "沙和尚"

展开

展开可以在函数调用时,将数组(或字符串)展开为函数的参数,也可以在通过字面量创建数组(或对象)时,直接将其他数组(或对象)在新数组(或对象)中展开(类似于浅拷贝)。

语法:

myFunction(...iterableObj); // 展开数组传参

[...iterableObj, '4', 'five', 6]; // 创建数组时展开其他数组

let objClone = { ...obj }; // 将一个对象中的所有键值对展开到一个新对象中(浅拷贝)

代码1-9(将数组中的元素解构为函数的参数)

function fn(x, y, z) {
   console.log('x =', x);
   console.log('y =', y);
   console.log('z =', z);
}

const numbers = [1, 2, 3];

fn(...numbers);
/*
输出:
x = 1
y = 2
z = 3
*/

function myFunction(v, w, x, y, z) { }
let args = [0, 1];
myFunction(-1, ...args, 2, ...[3]);

代码1-10 (创建数组时展开其他数组)

const arr = [0, 1, 2];
const newArr = [...arr, 12];
console.log(newArr); // [0, 1, 2, 12]

let parts = ['shoulders', 'knees'];
let lyrics = ['head', ...parts, 'and', 'toes'];
// ["head", "shoulders", "knees", "and", "toes"]

let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1 = [...arr1, ...arr2];

let obj1 = { name: '孙悟空', age: 18 };
let obj2 = { name: '猪八戒', address: '高老庄' };

let clonedObj = { ...obj1 }; // 浅拷贝对象
// Object { name: '孙悟空', age: 18 }

let mergedObj = { ...obj1, ...obj2 }; // 合并两个对象
// Object {name: '猪八戒', age: 18, address: '高老庄'}

箭头函数

箭头函数是传统函数表达式的简写方式,它简化了函数的编写,也带来了一些限制导致在一些场景下它无法使用。

特点:

  1. 箭头函数没有自己的this
  2. 箭头函数中没有arguments
  3. 不能作为构造函数调用
  4. 无法通过call、apply、bind指定函数的this

代码1-10 (箭头函数语法)

// 基本语法
param => expression

// 多个参数时,参数需要使用()括起来
(param1, paramN) => expression

// 多条语句时,语句需要使用{}括起来,同时使用return设置返回值
param => {
 let a = 1;
 return a + param;
}

// 返回值是一个对象时,对象需要加()
params => ({foo: "a"})

//多余参数、默认参数和传统函数无异
(a, b, ...r) => expression
(a=400, b=20, c) => expression

模块化

初期的JavaScript项目是非常小的,并不需要引入模块化来对其进行处理。但是随着前端项目复杂度的提高,项目中的代码越来越多,引入模块化成为一个迫在眉睫的问题。

所谓的模块化指将一个大的项目拆分成一个一个小的模块。拆分模块的好处有很多,比如使代码变得结构清晰、易于维护、提高复用度等。传统的JS中引入外部的脚本也可以当成是一种初级的模块化方式,但那种方式存在着很多不足,对我们开发人员来说并不十分友好。所以有一些前辈开始设计新的模块化方法来对JS进行扩展,像CommonJS、AMD、ReauireJS等模块系统都是很好的尝试。CommonJS现在依然是Node.js中默认的模块化方式。

第三方的模块化进行的如火如荼,ECMA官方当然不能袖手旁观,于是就有了ES6的模块化方案。ES6的模块化分成两个部分:export和import。

export(导出)

在创建JS模块时,我们通过export向模块外部暴露内容(函数、对象、原始值)。在其他模块中可以通过import引入这些内容。使用了export的模块会自动开启严格模式。

export导出的方式有两种:

  1. 默认导出
  2. 命名导出

代码1-11(export语法)

// 导出变量(命名导出)
export let name1, name2, …, nameN;
export let name1 = …, name2 = …, …, nameN;

// 导出函数(命名导出)
export function functionName(){...}

// 导出类(命名导出)
export class ClassName {...}

// 导出一组
export { name1, name2, …, nameN };

// 重命名导出
export { variable1 as name1, variable2 as name2, …, nameN };

// 解构赋值后导出
export const { name1, name2: bar } = o;

// 默认导出
export default expression;
export default function (…) { … } // also class, function*
export default function name1(…) { … } // also class, function*
export { name1 as default, … };

// 聚合模块
export * from …; // 将其他模块中的全部内容导出(除了default)
export * as name1 from …; // ECMAScript® 2O20 将其他模块中的全部内容以指定别名导出
export { name1, name2, …, nameN } from …; // 将其他模块中的指定内容导出
export { import1 as name1, import2 as name2, …, nameN } from …; // 将其他模块中的指定内容重命名导出
export { default, … } from …;

import(引入)

import用来引入其他模块中导出的内容,注意!只有通过export导出的内容才能够通过import引入。和export一样,使用了import的模块会自动启用严格模式。

代码1-12(import语法)

// 引入默认导出
import defaultExport from "module-name";

// 将所有模块导入到指定命名空间中
import * as name from "module-name";

// 引入模块中的指定内容
import { export1 } from "module-name";
import { export1 , export2 } from "module-name";

// 以指定别名引入模块中的指定内容
import { export1 as alias1 } from "module-name";
import { export1 , export2 as alias2 , [...] } from "module-name";

// 引入默认和其他内容
import defaultExport, { export1 [ , [...] ] } from "module-name";
import defaultExport, * as name from "module-name";

// 引入模块
import "module-name";

类(class)

类是对象的模板,类中定义了对象中包含了哪些属性和方法。也可以直接通过function来定义类,但这两种定义方式并不是完全通用。

代码1-13(简单的类)

class Person {
   //属性
   name = "孙悟空";
   // 属性
   age = 18;
   // 方法
   sayHello() {
       console.log(`大家好,我是${this.name}`);
  }
}
const p = new Person(); // 创建对象
p.sayHello(); // 调用方法

代码1-14(在构造函数中初始化属性)

class Person {
   constructor(name, age) {
       this.name = name;
       this.age = age;
  }
   
   // 使用箭头函数定义方法
   say = ()=>{
       console.log(this.name);  
  };
}

代码1-15(继承)

class Animal {
   constructor(name, age) {
       this.name = name;
       this.age = age;
  }

   say() {
       console.log('哈哈,我是' + this.name);
  }
}

// Dog类继承Animal类
class Dog extends Animal {
   constructor(name, age) {
       // 调用父类构造函数
       super(name, age);
       this.legs = 4;
  }
   // 重写父类方法
   say() {
       console.log('汪汪汪!我是' + this.name);
  }
}

// Snake继承Animal类
class Snake extends Animal{
   constructor(name, age, len) {
       // 调用父类构造函数
       super(name, age);
       this.len = len;
  }
// 重写父类方法
   say(){
       console.log('嘶嘶嘶~ 我是' + this.name);
  }
}

数组的方法

map(),可以根据已有数组返回一个新的数组。map需要一个函数作为参数,并且会对数组中的每一个元素调用该函数,并将函数每次执行时所返回的结果作为新数组中的元素。

// Arrow function
map((element) => { /* ... */ })
map((element, index) => { /* ... */ })
map((element, index, array) => { /* ... */ })

const array1 = [1, 4, 9, 16];
// pass a function to map
const map1 = array1.map(x => x * 2);
console.log(map1);
// expected output: Array [2, 8, 18, 32]

filter(),可以将数组中符合条件的元素返回。filter同样需要一个函数作为参数,它会对数组中的每一个元素调用函数,如果函数返回true,则该元素会放入到新数组中。如果返回false,则新数组中不会出现该元素。

// Arrow function
filter((element) => { /* ... */ } )
filter((element, index) => { /* ... */ } )
filter((element, index, array) => { /* ... */ } )

const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];
const result = words.filter(word => word.length > 6);
console.log(result);
// expected output: Array ["exuberant", "destruction", "present"]

reduce(),用来整合数组。它可以对数组中的值进行计算,最终将数组中的所有元素合并为一个值。

// Arrow function
reduce((previousValue, currentValue) => { /* ... */ } )
reduce((previousValue, currentValue, currentIndex) => { /* ... */ } )
reduce((previousValue, currentValue, currentIndex, array) => { /* ... */ } )
reduce((previousValue, currentValue, currentIndex, array) => { /* ... */ }, initialValue)

const array1 = [1, 2, 3, 4];
// 0 + 1 + 2 + 3 + 4
const initialValue = 0;
const sumWithInitial = array1.reduce(
(previousValue, currentValue) => previousValue + currentValue,
 initialValue
);
console.log(sumWithInitial);
// expected output: 10
4.9 15 投票数
文章评分
订阅评论
提醒
guest

1 评论
最旧
最新 最多投票
内联反馈
查看所有评论
Kyle
Kyle
1 年 前

2023/2/13打卡

1
0
希望看到您的想法,请您发表评论x