⚡学习react前你需要知道的javascript基础! 我浅看一下
1. 前言
如果你想学习React,或者任何JavaScript框架,你首先需要理解基本的JavaScript方法和概念。不然的话,这就像是一个孩子在学会走路之前就试图跑步一样~~~
许多开发者在学习React时选择“边学边用”的方法。但这通常不会提高生产力,反而会加剧他们在JavaScript知识方面的空白。这种方法使得理解每个新特性变得更加困难(你可能开始混淆JavaScript和React)。
React是一个基于UI组件的JavaScript框架,所有的代码都是用JavaScript编写的,包括用JSX编写组件(这使得开发者能够轻松地将HTML和JavaScript写在一起)。
在本文中,我们将采用实用的方法,讨论在学习React之前你需要掌握的所有JavaScript思想和技术。
2. 回调函数
回调函数是在另一个函数执行完毕后执行的函数。它通常作为输入传递给另一个函数中。
理解回调函数至关重要,因为它们被广泛应用于数组方法(如 map()
、filter()
等)、setTimeout()
、事件监听器(如点击、滚动等)以及许多其他场景。
以下是一个点击事件监听器的例子,其中包含一个回调函数,每当按钮被点击时,这个函数就会执行:
//HTML
<button class="btn">Click Me</button>
//JavaScript
const btn = document.querySelector('.btn');
btn.addEventListener('click', () => {
let name = 'John doe';
console.log(name.toUpperCase())
})
注意:回调函数可以是普通函数,也可以是箭头函数。
3. Promise
Promise
是一种异步操作的解决方案,可以将异步代码以同步的方式进行编写。
如前所述,回调函数在原始函数执行后执行。
您现在可能开始考虑将如此多的回调函数堆叠在一起,因为您不希望在父函数完成运行或经过特定时间之前运行特定函数。 例如,让我们尝试显示每个 2 秒后控制台中出现 5 个名称 - 也就是说,第一个名称在 2 秒后出现,第二个名称在 4 秒后出现,依此类推...
setTimeout(() => {
console.log("Joel");
setTimeout(() => {
console.log("Victoria");
setTimeout(() => {
console.log("John");
setTimeout(() => {
console.log("Doe");
setTimeout(() => {
console.log("Sarah");
}, 2000);
}, 2000);
}, 2000);
}, 2000);
}, 2000);
上面的示例可以工作,但是很难理解、调试,甚至添加错误处理。这被称为回调地狱
。
回调地狱是由复杂的嵌套回调编码引起的一个大问题。 使用 Promise
的主要原因是为了防止回调地狱
。借助 Promise,我们可以以同步方式编写异步代码。
JavaScript Promise 语法:
const myPromise = new Promise((resolve, reject) => {
// condition
});
Promise 有两个参数,一个表示成功(resolve
),一个表示失败(reject
)。
每个 Promise 都有一个必须满足的条件,否则 Promise 将被reject:
const promise = new Promise((resolve, reject) => {
let condition;
if(condition is met) {
resolve('Promise is resolved successfully.');
} else {
reject('Promise is rejected');
}
});
Promise 对象有 3 种
状态:
-
Pending:默认情况下,这是 Promise 成功或失败之前的初始状态。
-
Resolved:完成的 Promise
-
Rejected:失败的 Promise
最后,让我们尝试将回调地狱
重新实现为 Promise
:
function addName (time, name){
return new Promise ((resolve, reject) => {
if(name){
setTimeout(()=>{
console.log(name)
resolve();
},time)
}else{
reject('No such name');
}
})
}
addName(2000, 'Joel')
.then(()=>addName(2000, 'Victoria'))
.then(()=>addName(2000, 'John'))
.then(()=>addName(2000, 'Doe'))
.then(()=>addName(2000, 'Sarah'))
.catch((err)=>console.log(err))
4. Map()
最常用的方法之一是 Array.map()
,它允许您迭代数组并使用回调函数修改其元素。
回调函数将在每个数组元素上运行。
假设我们有一个包含用户信息的用户数组。
let users = [
{ firstName: "Susan", lastName: "Steward", age: 14, hobby: "Singing" },
{ firstName: "Daniel", lastName: "Longbottom", age: 16, hobby: "Football" },
{ firstName: "Jacob", lastName: "Black", age: 15, hobby: "Singing" }
];
我们可以使用map循环并修改它的输出
let singleUser = users.map((user)=>{
//let's add the firstname and lastname together
let fullName = user.firstName + ' ' + user.lastName;
return `
<h3 class='name'>${fullName}</h3>
<p class="age">${user.age}</p>
`
});
-
map()
始终返回一个新数组
,即使它是一个空数组。 -
它不会改变原始数组。
-
在创建新数组时,它始终使用原始数组中的值。
map 方法的工作方式几乎与其他所有 JavaScript 迭代器(例如 forEach())一样,但每当您要返回值时,最好始终使用 map 方法。
5. Filter() and Find()
Filter()
根据某些条件提供一个新数组。与 map()
不同,它可以改变新数组的大小
find()
仅返回单个实例(这可能是一个对象或项)。如果存在多个匹配项,则返回第一个匹配项,否则返回未定义。
Filter()
假设您有一个包含不同年龄的注册用户的数组集合:
let users = [
{ firstName: "Susan", age: 14 },
{ firstName: "Daniel", age: 16 },
{ firstName: "Bruno", age: 56 },
{ firstName: "Jacob", age: 15 },
{ firstName: "Sam", age: 64 },
{ firstName: "Dave", age: 56 },
{ firstName: "Neils", age: 65 }
];
你可以选择按年龄段对这些数据进行排序,比如年轻人(1-15岁)、老年人(50-70岁)等等...
这种情况下,过滤功能就派上用场了因为它根据条件生成一个新数组。
// for young people
const youngPeople = users.filter((person) => {
return person.age <= 15;
});
//for senior people
const seniorPeople = users.filter((person) => person.age >= 50);
console.log(seniorPeople);
console.log(youngPeople);
这会生成一个新数组。如果不满足条件(不匹配),它会生成一个空数组。
Find()
find()
方法与 filter()
方法类似,会在数组中迭代查找满足指定条件的实例/项目。一旦找到它,它就会返回该特定的数组项并立即终止循环。
如果未发现匹配,该函数将返回undefined
。
const Bruno = users.find((person) => person.firstName === "Bruno");
console.log(Bruno);
6. 数组解构
解构是 ES6 中引入的 JavaScript 功能,它允许更快、更简单地访问和解包数组和对象中的变量。
在解构引入之前,如果我们有一个水果数组,并且想要获取第一个、第二个和分开第三个水果,我们最终会得到这样的结果:
let fruits= ["Mango", "Pineapple" , "Orange", "Lemon", "Apple"];
let fruit1 = fruits[0];
let fruit2 = fruits[1];
let fruit3 = fruits[2];
console.log(fruit1, fruit2, fruit3); //"Mango" "Pineapple" "Orange"
这就像一遍又一遍地重复同样的事情,这可能会变得很麻烦。
让我们看看如何对其进行解构以获得前 3 个水果
。
let [fruit1, fruit2, fruit3] = fruits;
console.log(fruit1, fruit2, fruit3); //"Mango" "Pineapple" "Orange"
小伙伴可能想知道如果您只想打印第一个
和最后一个
水果,或者第二个和第四个水果,如何跳过数据。
可以按如下方式使用逗号:
const [fruit1 ,,,, fruit5] = fruits;
const [,fruit2 ,, fruit4,] = fruits;
7. 对象解构
假设我们有一个用户对象,其中包含他们的名字、姓氏等等
const Susan = {
firstName: "Susan",
lastName: "Steward",
age: 14,
hobbies: {
hobby1: "singing",
hobby2: "dancing"
}
};
按照旧的方式,获取这些数据会充满重复的工作量~~~:
const firstName = Susan.firstName;
const age = Susan.age;
const hobby1 = Susan.hobbies.hobby1;
console.log(firstName, age, hobby1); //"Susan" 14 "singing"
但通过解构它就容易多了:
const {firstName, age, hobbies:{hobby1}} = Susan;
console.log(firstName, age, hobby1); //"Susan" 14 "singing"
我们也可以在函数中
执行此操作:
function individualData({firstName, age, hobbies:{hobby1}}){
console.log(firstName, age, hobby1); //"Susan" 14 "singing"
}
individualData(Susan);
休息一下~~~
8. 剩余运算符和扩展运算符
# 剩余运算符
和扩展运算符
使用三个点...
来表示
剩余运算符(rest)
rest
收集项目, 它将某些用户提供的值的“剩余”放入 数组/对象中。
假设您有一个水果数组:
let fruits= ["Mango", "Pineapple" , "Orange", "Lemon", "Apple"];
我们可以解构
以获得第一个
和第二个水果
,然后通过使用剩余运算符将其余水果
放入数组中。
const [firstFruit, secondFruit, ...rest] = fruits
console.log(firstFruit, secondFruit, rest); //"Mango" "Pineapple" ["Orange","Lemon","Apple"]
rest
必须始终放在最后(位置非常重要)。
我们刚刚处理过数组, 现在让我们处理对象吧~~~
假设我们有一个有名字、姓氏等信息的用户对象。我们可以对其进行解构,然后提取剩余的数据。
const Susan = {
firstName: "Susan",
lastName: "Steward",
age: 14,
hobbies: {
hobby1: "singing",
hobby2: "dancing"
}
};
const {age, ...rest} = Susan;
console.log(age, rest);
这将记录以下结果:
14
{
firstName: "Susan" ,
lastName: "Steward" ,
hobbies: {...}
}
扩展运算符
用于扩展数组项。它使我们能够从数组中获取参数列表。
扩展运算符
的语法与剩余运算符
类似,只是它的操作方向相反
。
注意:扩展运算符仅在数组、函数调用或初始化属性对象中使用时才有效。
例如,假设有不同类型动物的数组:
let pets= ["cat", "dog" , "rabbits"];
let carnivorous = ["lion", "wolf", "leopard", "tiger"];
想将这两个数组合并为一个动物数组。让我们尝试一下:
let animals = [pets, carnivorous];
console.log(animals); //[["cat", "dog" , "rabbits"], ["lion", "wolf", "leopard", "tiger"]]
这不是我们想要的——我们希望所有项目都位于一个数组中。我们可以使用展开运算符
来实现这一点:
let animals = [...pets, ...carnivorous];
console.log(animals); //["cat", "dog" , "rabbits", "lion", "wolf", "leopard", "tiger"]
这也适用于对象。需要注意的是,扩展运算符无法扩展不能迭代的对象。
let name = {firstName:"John", lastName:"Doe"};
let hobbies = { hobby1: "singing", hobby2: "dancing" }
let myInfo = {...name, ...hobbies};
console.log(myInfo); //{firstName:"John", lastName:"Doe", hobby1: "singing", hobby2: "dancing"}
9. Set()
简单说,就是用来去除重复的值。
let animals = [
{
name:'Lion',
category: 'carnivore'
},
{
name:'dog',
category:'pet'
},
{
name:'cat',
category:'pet'
},
{
name:'wolf',
category:'carnivore'
}
]
第一件事是循环遍历数组,但我得到了重复的值:
let category = animals.map((animal)=>animal.category);
console.log(category); //["carnivore" , "pet" , "pet" , "carnivore"]
这时,我们可以使用Set()
:
//wrap your iteration in the set method like this
let category = [...new Set(animals.map((animal)=>animal.category))];
console.log(category); ////["carnivore" , "pet"]
10. 对象的动态键
我们知道对象通常由属性/键和值组成,并且我们可以使用点符号
来添加、编辑或访问某些值。
举个例子:
let lion = {
category: "carnivore"
};
console.log(lion); // { category: "carnivore" }
lion.baby = 'cub';
console.log(lion.category); // carnivore
console.log(lion); // { category: "carnivore" , baby: "cub" }
标准命名约定只允许camelCase
和snake_case
,如果有些键没有使用标准命名怎么办呢?
可以使用方括号[]
表示法我们可以解决这个问题。
let lion = {
'lion-baby' : "cub"
};
// dot notation
console.log(lion.lion-baby); // error: ReferenceError: baby is not defined
// bracket notation
console.log(lion['lion-baby']); // "cub"
您可以看到点符号
和括号符号
之间的区别。再来点其他例子:
let category = 'carnivore';
let lion = {
'lion-baby' : "cub",
[category] : true,
};
console.log(lion); // { lion-baby: "cub" , carnivore: true }
甚至还可以加点运算~~~ 让我们玩出点花:
const number = 5;
const gavebirth = true;
let animal = {
name: 'lion',
age: 6,
[gavebirth && 'babies']: number
};
console.log(animal); // { name: "lion" , age: 6 , babies: 5 }
11. reduce()
reduce()方法用于对数组元素进行累加或合并操作,返回一个单一的值。
这可以说是最强大
的数组函数。它有时可以取代filter()
和find()
方法,并且在对大量数据执行map()
和filter()
方法时也非常方便。
第一个参数是所有计算的总和/总计,并且第二个是当前迭代值。
例如,假设我们有一个员工的工资列表:
let staffs = [
{ name: "Susan", age: 14, salary: 100 },
{ name: "Daniel", age: 16, salary: 120 },
{ name: "Bruno", age: 56, salary: 400 },
{ name: "Jacob", age: 15, salary: 110 },
{ name: "Sam", age: 64, salary: 500 },
{ name: "Dave", age: 56, salary: 380 },
{ name: "Neils", age: 65, salary: 540 }
];
我们想把所有员工的薪水加一起,用reduce()
非常容易实现
const totalSalary = staffs.reduce((total, staff) => {
total += staff.salary;
return total;
},0)
console.log(totalSalary); // 2150
我们传递了第二个参数0,即总数,初始值是0。它可以是任何东西,例如数字或对象。
现在让我们计算所有员工的 10%的税加一下并得出总数。
const salaryInfo = staffs.reduce(
(total, staff) => {
let staffTithe = staff.salary * 0.1;
total.totalTithe += staffTithe;
total['totalSalary'] += staff.salary;
return total;
},
{ totalSalary: 0, totalTithe: 0 }
);
console.log(salaryInfo); // { totalSalary: 2150 , totalTithe: 215 }
12. 可选链
可选链是一种在 JavaScript 中访问嵌套对象属性的安全方法,而不必在访问长链对象属性时执行多个 null 检查。 这是ES2020中引入的新功能。
let users = [
{
name: "Sam",
age: 64,
hobby: "cooking",
hobbies: {
hobb1: "cooking",
hobby2: "sleeping"
}
},
{ name: "Bruno", age: 56 },
{ name: "Dave", age: 56, hobby: "Football" },
{
name: "Jacob",
age: 65,
hobbies: {
hobb1: "driving",
hobby2: "sleeping"
}
}
];
假设您试图从上面的数组中获取hobbies
。让我们尝试一下:
users.forEach((user) => {
console.log(user.hobbies.hobby2);
});
当您查看控制台时,您会注意到第一次迭代已完成,但第二次迭代没有任何hobbies
。
因此它必须抛出一个错误并退出迭代——这意味着它无法从数组中的其他对象获取数据
"sleeping"
error: Uncaught TypeError: user.hobbies is undefined
尽管有多种方法可以修复此错误(例如,使用条件),但可以通过可选链接来修复此错误。
让我们看看如何使用条件和可选链来做到这一点:
条件:
users.forEach((user) => {
console.log(user.hobbies && user.hobbies.hobby2);
});
可选链:
users.forEach((user) => {
console.log(user?.hobbies?.hobby2);
});
输出
"sleeping"
undefined
undefined
"sleeping"
13. Fetch API
Fetch API用于在JavaScript中进行基本的AJAX请求,获取API数据。
它是由浏览器提供的,所以您可以使用它而无需安装或导入任何包或依赖项(例如轴)。
它的配置相当简单易懂。
fetch API
默认提供一个 Promise
(我在本文前面介绍过 Promise)。
fetch('https://type.fit/api/quotes')
.then(response => response.json())
.then(data => console.log(data))
接着,让我们看下如何处理接口请求时的错误
fetch("https://type.fit/api/quotes")
.then((response) => {
if (!response.ok) {
throw Error(response.statusText);
}
return response.json();
})
.then((data) => console.log(data))
.catch((err) => console.log(err));
14. Async/Await
Async/Await 允许我们以同步方式编写异步代码。这意味着我们不需要继续嵌套回调。
异步函数总是返回一个promise
。
小伙伴们可能想知道同步
和异步
之间的区别是什么?
简单地说:
-
同步意味着作业一个接一个地完成。
-
异步意味着任务是独立完成的。
请注意,我们总是在函数前面加上 async,并且只有在有 async 的情况下才可以使用await。
现在让我们使用 async/await 实现之前处理的 Fetch API 代码:
const fetchData = async () =>{
const quotes = await fetch("https://type.fit/api/quotes");
const response = await quotes.json();
console.log(response);
}
fetchData();
嗯,看起来这更容易阅读了~~~
小伙伴可能想知道我们如何使用 async/await 处理错误
const fetchData = async () => {
try {
const quotes = await fetch("https://type.fit/api/quotes");
const response = await quotes.json();
console.log(response);
} catch (error) {
console.log(error);
}
};
fetchData();
参考资料:Top JavaScript Concepts to Know Before Learning React
15. 总结
到这,学习react前的一些javascript基础就结束啦~~~
这些是学习React
之前必须掌握的JavaScript核心概念
。
通过理解和熟练掌握这些概念,你将为学习和使用React打下坚实的基础。
不知道坚持看下来的小伙伴有多少? 希望坚持看到这的小伙伴给博主点个赞支持一下吧~~~
欢迎转载,但请注明来源。 最后,希望小伙伴们给我个免费的点赞,祝大家心想事成,平安喜乐。
转载自:https://juejin.cn/post/7390188606256103424