作者:FL
初稿日期:2024年1月18日
版本:2
主要内容:
- 前端开发与前端开发工具
- Node.js安装与入门
- Es6新语法糖
- Npm包管理工具
- Babel
- 模块化管理
- webpack
简介
- 本笔记旨在打通html+css+js ——> vue.js/react ,补充中间的知识,利于更好的学习vue.js和react
- 本笔记知识只补充各个大前端知识的基础,具体细节参考各个技术的官网
- 本笔记以狂神的大前端技能的视频为大纲,各个知识通过查阅相关资料和询问ai,知识多数比视频更详尽、更好操作。
- 如有遗漏或问题,欢迎各位读者批判指正。
- 看完本篇,请继续看vue3教程
一、Node.js
1.安装与配置
2.介绍
简单的说 Node.js 就是运行在服务端的 JavaScript,如果你熟悉 Javascript,那么你将会很容易的学会 Node.js。
如果你是一个前端程序员,Node.js可以使用js开发后端,无需额外学习java等后端语言。
当然,如果你是后端程序员,想部署一些高性能的服务,那么学习 Node.js 也是一个非常好的选择。
前端技术流:HTML+CSS -> JS -> node.js -> webpack ->react -> vue -> uniapp
用node.js运行js
// 输出Hello World!
console.log("Hello World!");
$node hello.js
> Hello World!
3.Node.js实现请求响应
- node.js将js变为了服务端语言
编写app.js
// 导入内置的 http 模块
const http = require('http');
// 创建一个名为 httpServer 的 Node.js 服务器实例
const httpServer = http.createServer();
// 定义一个名为 on 的回调函数,该函数将在服务器启动时被调用
// 监听请求事件,对所有请求做出响应
httpServer.on('request', (req, res) => {
// 设置 HTTP 响应状态码为 200,表示请求成功
res.statusCode = 200;
// 设置响应的 Content-Type 为 text/plain,表示响应的内容是纯文本
res.setHeader('Content-Type', 'text/plain');
// 使用 res.end 方法发送响应正文 "Hello World"
res.end('Hello World\n');
});
// 使用 http 模块的 listen 方法启动 HTTP 服务器,监听在 3000 端口上
httpServer.listen(3000, () => {
// 打印出服务器正在端口 3000 上监听的消息
console.log('Server is running and listening on port 3000');
});
使用Node.js运行
node app.js
浏览器效果:
//在http://localhost:3000/
Hello World
4.Node.js连接Mysql
在 Node.js 中操作 MySQL 数据库通常需要使用一个外部库,例如 mysql
或 mysql2
。以下是使用这些库操作 MySQL 数据库的完整指南:
1. 安装 Node.js 包
准备工作:
1.再vscode中新建文件夹,右键文件夹选择集成终端中打开(需要用管理员权限打开)
2.输入初始化命令:npm init -y
3.安装express 模块:npm i express
4.安装mysql模块:npm i mysql/mysql2
使用 mysql
:
npm install mysql
使用 mysql2
:
在 Node.js 环境中,有一个名为 mysql2 的库,它是对 MySQL 官方库的一个封装,提供了一些额外的功能和改进,如异步查询支持。这个库并不是 MySQL 或 MySQL2 的官方 Node.js 绑定,而是一个第三方库,它使得在 Node.js 应用程序中使用 MySQL 变得更加容易。
npm install mysql2
2. 创建 MySQL 数据库和表
在你开始之前,确保你的 MySQL 服务器上已经创建了数据库,并且需要操作的表也已经存在。例如,你可以使用以下 SQL 命令来创建一个名为 students
的表:
CREATE TABLE students (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
age INT NOT NULL
);
3. 编写 Node.js 代码来操作数据库
以下是使用 mysql
和 mysql2
库的 Node.js 示例代码。
使用 mysql
:
const express = require('express'); //引入express 模块
const app = express(); //创建实例
const mysql = require('mysql'); //引入mysql 模块
// 创建数据库连接 填入数据库信息
//填自己数据库的信息!!!!!!!!!!!
const conn = mysql.createConnection({
host: 'localhost', // MySQL服务器地址
user: 'root', // MySQL用户名
password: '123456', // MySQL密码
database: 'test_db', // 要连接的数据库
});
// 测试连接
conn.connect(err => {
if (err) throw err;
console.log('连接成功');
});
// 开启服务器
app.listen(3000, () => {
console.log('服务器在3000端口开启...');
});
- 具体对数据库的操作见mysql2,mysql和mysql2的操作是类似的
使用 mysql2
:
const express = require('express'); //引入express 模块
const app = express(); //创建实例
const mysql2 = require('mysql2'); //引入mysql 模块
// 创建数据库连接 填入数据库信息
//填自己数据库的信息!!!!!!!!!!!
const conn = mysql2.createConnection({
host: 'localhost', // MySQL服务器地址
user: 'root', // MySQL用户名
password: '123456', // MySQL密码
database: 'test_db', // 要连接的数据库
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0,
});
// 测试连接
conn.connect(err => {
if (err) throw err;
console.log('连接成功');
});
//进行数据库操作
// 开启服务器
app.listen(3000, () => {
console.log('服务器在3000端口开启...');
});
插入:
- 运行后可以在mysql中或
http://localhost:3000/insert
查看
// 定义路由(说白了就是网址)
app.get('/insert', (req, res) => {
let sqlStr = "INSERT INTO students ( name, age )VALUES('张三',18)";
//执行mysql 语句
conn.query(sqlStr, err => {
if (err) throw err;
console.log('插入成功');
});
//成功后的页面显示
res.send('插入成功');
});
修改:
// // 更新操作
app.get('/update', (req, res) => {
let sqlStr = "UPDATE students SET name = '李四', age = 20 WHERE id = 1";
conn.query(sqlStr, err => {
if (err) throw err;
console.log('更新成功');
});
res.send('更新成功');
});
删除:
// 删除操作
app.get('/delete', (req, res) => {
let sqlStr = "DELETE FROM students WHERE id = 1";
conn.query(sqlStr, err => {
if (err) throw err;
console.log('删除成功');
});
res.send('删除成功');
});
- 注意:以上对数据库的操作只有在访问对应路由才会执行,且每访问一次就会执行一次
每次访问这些路由时,都会独立地执行相应的SQL操作。这对于测试和小型应用程序可能是可行的,但如果你正在构建一个高流量的应用程序,你可能需要考虑更高效的方法来处理这些操作,比如使用队列系统或者在后台进行数据操作,以避免对数据库的频繁请求可能导致性能问题。
此外,对于生产环境,还需要考虑事务处理、错误处理、安全性(如使用预编译语句来防止SQL注入)以及数据的完整性和一致性。
二、ES6新语法
1.介绍
CMAScript 6(ES6),也称为ECMAScript 2015,是JavaScript语言的一个重要版本,于2015年6月正式发布。ES6在语言的核心中引入了许多新特性,这些特性旨在改善代码的清晰度、性能,以及开发体验。
2.let 、const和var
在ES6(ECMAScript 2015)中,let
、const
和var
是用于变量声明的关键字,它们之间有几个关键的区别:
2.1 作用域(Scope)
var
声明的变量具有函数作用域(function scope),这意味着如果var
变量在一个函数内部声明,它只能在这个函数内部被访问。let
声明的变量具有块作用域(block scope),它仅在声明它的块(如一个if语句或for循环)内部有效。const
声明的变量也是块作用域的,但其值在声明后不能被重新赋值,这意味着它等同于最终的常量。
2.2 提升(Hoisting)
var
声明的变量会被提升到其作用域的顶部,但在初始化之前不能使用,访问未初始化的变量会得到undefined
。let
和const
声明的变量也会被提升,但它们在声明之前不可用,如果尝试访问未声明的变量,将会导致一个引用错误(ReferenceError)。
2.3 数据类型
var
、let
和const
都可以用来声明任何数据类型,包括基本类型(如number、string、boolean)和对象。const
特别之处在于,一旦声明,它所引用的对象或数组不能被重新分配,但是其引用的对象或数组内部的属性可以被修改。
// 基本数据类型的不可变性
const number = 42;
// 错误,不能重新赋值
// number = 24;
// 对象引用的不可变性
const person = { name: "Alice" };
// 错误,不能重新分配到一个新的对象
// person = { name: "Bob" };
// 但是可以修改对象的属性
person.name = "Bob"; // 允许
// 数组引用的不可变性
const numbers = [1, 2, 3];
// 错误,不能重新分配到一个新的数组
// numbers = [4, 5, 6];
// 但是可以修改数组的元素
numbers.push(4); // 允许
2.4 重新赋值
var
声明的变量可以被重新赋值。let
声明的变量也可以被重新赋值,但只能在它声明的块内部。const
声明的变量不能被重新赋值,它的值在声明后是固定的。
示例:
// 使用var
function exampleVar() {
if (true) {
var x = 10;
}
console.log(x); // 输出10,x被提升了
}
exampleVar();
// 使用let
function exampleLet() {
if (true) {
let x = 10;
}
console.log(x); // 报错,x没有在当前作用域外可用
}
// exampleLet(); // ReferenceError: x is not defined
// 使用const
function exampleConst() {
if (true) {
const x = 10;
}
console.log(x); // 报错,x没有在当前作用域外可用
}
// exampleConst(); // ReferenceError: x is not defined
// 另一个例子,展示const不可重新赋值
const y = 20;
y = 30; // 报错,不能重新赋值
2.5性能影响
- 在性能上,
var
、let
和const
声明的变量在内存中没有显著差异。它们的主要区别在于作用域和提升行为,而不是性能。 - 然而,由于
let
和const
避免了变量提升,它们在处理复杂代码时可以减少错误和意外行为,从而可能提高代码的可预测性和可维护性。
在现代JavaScript开发中,推荐尽可能使用let
和const
,因为它们提供了更好的作用域管理和数据安全,有助于编写更清晰、更可靠的代码。
3.模板字符串
在ES6(ECMAScript 2015)中,模板字符串(template strings)是一种增强版的字符串字面量,允许你创建包含变量、表达式和多行字符串的字符串。模板字符串使用反引号(`)来包裹字符串内容,而不是使用单引号(')或双引号(")。
基本语法
模板字符串的基本语法如下:
`模板字符串 ${表达式}`
${expression}
表示一个模板表达式,它可以是任何有效的JavaScript表达式,包括变量、函数调用、运算符等。- 模板字符串中的变量会被JavaScript引擎替换为对应表达式的结果。
- 如果模板表达式包含空格或换行符,这些空白字符会被保留在生成的字符串中。
使用示例
// 声明变量
let name = "Alice";
let age = 25;
// 使用模板字符串插入变量
console.log(`Hello, ${name}! You are ${age} years old.`); // 输出: Hello, Alice! You are 25 years old.
// 嵌入表达式
console.log(`The square of ${name.toUpperCase()} is ${age * age}.`); // 输出: The square of ALICE is 625.
// 插入多行字符串
let introduction = `Hello,
my name is ${name}
and I am ${age} years old.`;
console.log(introduction); // 输出:
// Hello,
// my name is Alice
// and I am 25 years old.
// 使用模板字符串构造函数
function greet(person) {
return `Hello, ${person}!`;
}
console.log(greet("Alice")); // 输出: Hello, Alice!
// 嵌套模板字符串
console.log(`${greet(name)}. Welcome to the party!`); // 输出: Hello, Alice. Welcome to the party!
4.默认参数
在ES6中,如果定义一个函数时没有提供任何显式参数,那么函数将使用提供的默认参数。默认参数允许你为函数的参数指定一个默认值,这样在调用函数时,如果传入的参数缺失,函数将使用默认值。
下面是一个定义了默认参数的greet
函数的例子:
function greet(name = "Alice", greeting = "Hello") {
console.log(`${greeting}, ${name}!`);
}
在这个例子中,greet
函数有两个参数:name
和greeting
。每个参数都有一个默认值:
name
参数的默认值是"Alice"
。如果调用函数时没有提供name
参数,函数将使用默认值"Alice"
。greeting
参数的默认值是"Hello"
。如果调用函数时没有提供greeting
参数,函数将使用默认值"Hello"
。
现在,我们可以调用greet
函数,无论是否提供参数:
-
如果没有提供任何参数,
greet
函数将使用默认参数"Alice"
和"Hello"
,输出"Hello, Alice!"
。 -
如果只提供了
name
参数,例如greet("Bob")
,函数将使用"Bob"
作为name
的值,"Hello"
作为greeting
的值,输出"Hello, Bob!"
。 -
如果提供了
greeting
参数,例如greet("Charlie", "Hi")
,函数将使用"Charlie"
作为name
的值,"Hi"
作为greeting
的值,输出"Hi, Charlie!"
。 -
如果提供了所有参数,例如
greet("David", "Hey")
,函数将使用提供的值,输出"Hey, David!"
。 -
需要注意的是,如果函数的参数列表中有默认参数,那么在调用函数时,必须从左到右按顺序提供非默认参数,否则会导致
undefined
值的错误。例如:
greet("David", "Hey", "How are you?") // 错误:因为"How are you?"没有对应的名字参数
在上面的错误示例中,我们试图跳过name
参数并直接提供greeting
和第三个参数,这将导致一个TypeError
,因为greet
函数只有两个参数,而且它们都有默认值。
5.箭头函数
- ES6(ECMAScript 2015)引入了箭头函数(Arrow Functions),这是一种写法更简洁、表达性更强的函数表示形式。箭头函数主要用于回调函数、局部函数和设置对象属性的函数值。
- 当使用箭头函数时,它会捕获外部作用域的
this
值,并将其绑定到函数体内部。这意味着箭头函数中的this
始终与包含它的外部作用域的this
值相同,而不受函数调用方式的影响。
5.1定义与语法结构
箭头函数的语法结构如下:
(参数1, 参数2, ..., 参数N) => { 函数声明 }
或者如果只有一个参数并且不使用大括号编写函数体(单个表达式):
参数 => 表达式
如果函数直接返回一个表达式的结果,无需写 return
语句,箭头函数会自动返回该表达式的结果。
5.2与传统函数的不同之处
- 没有自己的
this
:箭头函数不绑定自己的this
,它继承自父作用域。在对象方法和回调函数中使用时,其this
值将根据函数的词法作用域来确定,而不是调用时所在的作用域。
const obj = {
method: function() {
setTimeout(() => { // 箭头函数不绑定自己的this,而是继承自父作用域(这里指向obj)
console.log(this); // this 指向 obj
}, 1000);
}
};
obj.method();
- 不适用
arguments
对象:箭头函数没有自己的arguments
对象,可以使用剩余参数(rest parameters)代替。
// 普通函数可以使用arguments对象
function regularFunction() {
console.log(arguments); // arguments对象
}
regularFunction(1, 2, 3);
// 箭头函数需要使用rest参数
const arrowFunction = (...args) => {
console.log(args); // rest参数的数组形式
};
arrowFunction(1, 2, 3);
- 没有原型链继承
this
:由于箭头函数没有自己的this
,所以它们不会像普通函数那样通过原型链继承this
。
// 定义一个对象
const obj = {
name: 'John',
sayHello: function () {
// 普通函数
setTimeout(function () {
console.log('Hello, ' + this.name); // 这里的 this 指向全局对象(或 undefined)
// Hello, undefined
}, 1000);
// 箭头函数
setTimeout(() => {
console.log('Hello, ' + this.name); // 这里的 this 指向 obj 对象
// Hello, John
}, 1000);
},
};
obj.sayHello();
- 函数体内的
this
绑定:在普通函数中,this
的值取决于函数被调用时的上下文,而在箭头函数中,this
的值取决于函数被定义时的上下文。
// 定义一个对象
const obj = {
name: 'John',
sayHello: function() {
console.log('Hello, ' + this.name);
}
};
// 定义一个全局函数
function globalFunction() {
console.log('Hello, ' + this.name);
}
// 在 obj 上调用 sayHello 方法
obj.sayHello(); // 输出:Hello, John
//当我们在 obj 上调用 sayHello 方法时,普通函数中的 this 指向了调用该方法的对象 obj,因此它可以正确地访问到 obj 的 name 属性。
// 将 obj.sayHello 赋值给一个变量
const helloFunc = obj.sayHello;
// 在全局作用域中调用 helloFunc
helloFunc(); // 输出:Hello, undefined
//当我们将 obj.sayHello 赋值给变量 helloFunc 并在全局作用域中调用它时,普通函数中的 this 指向了全局对象,因此无法访问到 obj 的 name 属性,输出结果是 undefined。
// 定义一个箭头函数
const arrowFunc = () => {
console.log('Hello, ' + this.name);
};
// 在 obj 上调用 arrowFunc
obj.sayHello = arrowFunc;
obj.sayHello(); // 输出:Hello, John
//箭头函数 arrowFunc 在定义时就捕获了外部作用域的 this 值,它指向了定义时的上下文。因此,无论是在 obj 上调用 arrowFunc 还是在全局作用域中调用它,箭头函数都能正确地访问到 obj 的 name 属性。
// 在全局作用域中调用 arrowFunc
arrowFunc(); // 输出:Hello, undefined
5.3使用方法和示例
1. 回调函数
[1, 2, 3].map((num) => num * 2); // [2, 4, 6]
2. 局部函数
function outer() {
let outerVar = 'I am outside';
function inner() {
let innerVar = 'I am inside';
return () => console.log(outerVar, innerVar);
}
return inner();
}
outer()(); // 输出: I am outside I am inside
3. 对象属性
const object = {
method: function() {
return () => console.log('Hello World!');
}
};
object.method()(); // 输出: Hello World!
4. 箭头函数与传统函数混用
function handleClick(event) {
// 传统函数
function logMessage() {
console.log('Clicked!');
}
// 箭头函数
[1, 2, 3].map((num) => {
console.log(num);
return num * 2;
});
logMessage();
}
// 调用函数
handleClick();
5.4实际应用场景
- 事件处理:在处理点击、键盘事件等DOM事件时,可以使用箭头函数提供简洁的语法,并且确保事件处理函数的
this
指向正确的元素。 - 数组处理函数:在处理数组时(如
map
,filter
,reduce
等),箭头函数可以提供一种非常简洁的方式来编写回调函数。 - 异步代码:箭头函数可以用于处理异步任务,如使用
Promise
或async/await
。 - 模块导出:在模块化编程中,箭头函数可以用来简化模块导出的语法。
箭头函数提供了一种更现代、更清晰的函数编写方式,使得代码更加简洁和易于理解,但在使用时也要注意其与传统函数的不同点,以免在特定情境下导致意外的行为。
6.对象字面量简写
对象字面量简写语法是一种方便的语法现象,可以在对象字面量中省略属性的值,让 JavaScript 自动根据属性名称查找变量或常量的值,并将其赋给属性。
使用对象字面量简写语法可以更简洁地定义对象,省略重复的代码,使代码更加简洁易读。
// 定义变量
const name = 'John';
const age = 30;
// 使用对象字面量简写语法定义对象
const person = {
name,
age,
//在对象的属性定义中,我们只写了属性的名称,而没有显式地指定属性的值。
//JavaScript会根据属性的名称从当前作用域中查找相同名称的变量或常量,并将其值作为属性的值。因此,person 对象中的 name 属性的值为 'John',age 属性的值为 30。
sayHello() {
console.log('Hello, ' + this.name);
}
};
console.log(person.name); // 输出:John
console.log(person.age); // 输出:30
person.sayHello(); // 输出:Hello, John
7.对象解构与...
在JavaScript(ES6)中,对象解构(Object Destructuring)是一种方便的语法,用于从对象中提取属性并将其赋值给变量。
7.1基本用法和原理
对象解构使用花括号 {}
来声明要提取的属性,并使用等号 =
将属性解构给对应的变量。解构过程是基于属性的键名来匹配和赋值的。
// 定义一个对象
const person = {
name: 'John',
age: 30,
address: {
city: 'New York',
country: 'USA'
}
};
// 对象解构
const { name, age } = person;
//通过对象解构,我们将 name 和 age 属性的值提取出来,并分别赋值给同名的变量。现在我们可以直接使用 name 和 age 这两个变量来访问对应的属性值。
console.log(name); // 输出:John
console.log(age); // 输出:30
7.2通过键名直接访问属性值:
在对象解构中,我们可以使用键名来直接访问属性值。
例如,如果我们想获取 person 对象中 address 属性的 city 值,可以通过嵌套的解构来实现:
const { address: { city } } = person;
//通过嵌套的对象解构,使用键名 address 来获取 person 对象中的 address 属性。然后,再使用键名 city 直接访问 address 属性中的 city 值。
console.log(city); // 输出:New York
7.3合并不同来源的对象属性
- 对象解构还可以用于合并不同来源的对象属性,将它们解构到一个新的对象中。这种方式可以用于合并默认选项和用户提供的选项,以及合并多个配置对象等。
- 对象解构和对象展开运算符
...
通常一起使用,可以轻松地提取对象的属性并将其赋值给变量,以及合并对象的属性到一个新的对象中。
对象展开运算符
...
用于将一个对象的属性展开到另一个对象中。它可以用于创建新对象,将已有对象的属性展开并复制到新对象中。对象展开运算符在对象字面量中使用,以
...
加上要展开的对象的变量名的方式表示。
const defaults = {
host: 'localhost',
port: 8080,
timeout: 5000
};
const userConfig = {
port: 3000,
timeout: 10000
};
// 合并默认选项和用户选项
//通过对象解构和对象展开运算符 `...`,我们将两个对象的属性解构到一个新的对象 config 中,从而合并了它们的属性
const config = { ...defaults, ...userConfig };
console.log(config);
// 输出:
// {
// host: 'localhost',
// port: 3000,
// timeout: 10000
// }
7.4应用场景和用途:
对象解构在许多方面都有应用场景和用途:
- 对象属性获取和赋值: 对象解构使得从对象中获取属性值更加简洁和直观。可以用于提取所需的属性值,并将它们赋值给对应的变量。
- 合并对象属性: 对象解构结合对象展开运算符可以用于合并多个对象的属性,方便地创建一个包含多个来源属性的新对象。
- 对象初始化和创建新对象: 对象解构可以用于快速初始化对象或创建新对象,将已有对象的属性解构给新对象。
- 继承和原型链: 对象解构可用于继承属性,从一个对象中继承属性并赋值给另一个对象,实现属性的继承。
- 事件处理: 在事件处理中,对象解构可以方便地提取事件对象中的属性,使代码更简洁易读。
// 模拟事件对象
const event = {
type: 'click',
target: 'button',
pageX: 100,
pageY: 200
};
// 对象解构提取属性
function handleClick({ type, pageX, pageY }) {
console.log(`Clicked at (${pageX}, ${pageY})`);
}
// 调用事件处理函数
handleClick(event);
// 输出:Clicked at (100, 200)
8.数组map
在JavaScript(ES6)中,map
和 reduce
是数组的两个常用方法,用于对数组进行转换和聚合操作。
数组map方法
map
方法用于创建一个新数组,该数组的元素是源数组经过指定函数的映射后的结果。它会遍历源数组的每个元素,对每个元素应用指定的函数,并将函数的返回值作为新数组的对应元素。
map
方法的基本语法如下:
array.map(callback(currentValue, index, array), thisArg)
array
:源数组。callback
:要对每个元素执行的回调函数,该函数接收三个参数:currentValue
:当前正在处理的元素。index
(可选):当前元素的索引。array
(可选):调用map
方法的源数组。
thisArg
(可选):执行回调函数时使用的this
值。
map
方法返回一个新数组,其中包含源数组经过回调函数映射后的结果。
下面是一个使用 map
方法的示例,将数组中的每个元素转换为它们的平方值:
const numbers = [1, 2, 3, 4, 5];
//使用 `map` 方法将数组 `numbers` 中的每个元素进行平方运算,并将结果存储在新数组 `squaredNumbers` 中。
const squaredNumbers = numbers.map((num) => num * num);
console.log(squaredNumbers); // 输出:[1, 4, 9, 16, 25]
数组reduce方法
reduce
方法用于从左到右对数组中的元素进行累积操作,最终得到一个值。它接收一个回调函数作为参数,该函数定义了累积逻辑和初始累积值。
reduce
方法的基本语法如下:
array.reduce(callback(accumulator, currentValue, index, array), initialValue)
array
:源数组。callback
:对每个元素执行的回调函数,该函数接收四个参数:accumulator
:累积值,初始值为initialValue
或数组的第一个元素(如果没有提供初始值)。currentValue
:当前正在处理的元素。index
(可选):当前元素的索引。array
(可选):调用reduce
方法的源数组。
initialValue
(可选):作为累积值的初始值。
reduce
方法返回最终的累积值。
下面是一个使用 reduce
方法的示例,计算数组中所有元素的总和:
const numbers = [1, 2, 3, 4, 5];
//使用 `reduce` 方法对数组 `numbers` 中的所有元素进行累加操作,并将结果存储在变量 `sum` 中。
const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
console.log(sum); // 输出:15
应用场景和示例代码
-
map
方法的应用场景:- 转换数组中的每个元素,生成一个新的数组。
- 根据数组中的每个元素创建一个新的对象。
- 格式化数组中的数据,生成特定格式的字符串。
示例代码:
// 将数组中的每个元素转换为字符串并添加前缀 const numbers = [1, 2, 3, 4, 5]; const prefixedNumbers = numbers.map((num) => `Number: ${num}`); console.log(prefixedNumbers); // 输出:['Number: 1', 'Number: 2', 'Number: 3', 'Number: 4', 'Number: 5'] ```
-
reduce
方法的应用场景:- 计算数组中的总和、平均值或其他聚合值。
- 对数组中的元素进行筛选、条件过滤。
- 将数组中的元素转换为其他形式的数据结构,如对象或 Map。
示例代码:
// 计算数组中的总和 const numbers = [1, 2, 3, 4, 5]; const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0); console.log(sum); // 输出:15 // 筛选出数组中大于 2 的元素 const numbers = [1, 2, 3, 4, 5]; const filteredNumbers = numbers.reduce((accumulator, currentValue) => { if (currentValue > 2) { accumulator.push(currentValue); } return accumulator; }, []); console.log(filteredNumbers); // 输出:[3, 4, 5] // 将数组中的元素转换为对象 const people = [ { name: 'Alice', age: 25 }, { name: 'Bob', age: 30 }, { name: 'Charlie', age: 35 } ]; const peopleMap = people.reduce((accumulator, person) => { accumulator[person.name] = person.age; return accumulator; }, {}); console.log(peopleMap); // 输出: // { // Alice: 25, // Bob: 30, // Charlie: 35 // } ```
通过 map
和 reduce
方法,我们可以对数组进行各种转换和聚合操作,从而简化代码并实现更高效的数据处理。它们是处理数组数据的常用工具,在实际编程中具有广泛的应用。
9.Symbol
介绍
JavaScript ES6 引入了一种新的原始数据类型 Symbol
,它代表独一无二的值。Symbol
是一种用于创建对象私有属性的标识符,它们在对象中作为属性名,确保了属性名的唯一性,避免了属性名冲突的问题。
细节
以下是 Symbol
的一些细节拓展:
- 创建 Symbol:
let sym1 = Symbol();
let sym2 = Symbol('description');
console.log(sym1); // Symbol()
console.log(sym2); // Symbol(description)
可以通过 Symbol
函数创建一个新的 Symbol
值。可选的字符串参数 description
用来描述这个 Symbol
,但它并不影响 Symbol
的值。
- 每个 Symbol 都是唯一的:
即使描述相同,两个Symbol
值也不相等。
console.log(Symbol('foo') !== Symbol('foo')); // true
- 作为对象属性的键:
Symbol
可以用作对象属性的键,这使得这些属性不会被常规的方法(如for...in
循环和Object.keys()
)枚举。
let obj = {};
obj[Symbol('foo')] = 'bar';
console.log(obj); // { [Symbol(foo)]: 'bar' }
- 全局 Symbol 注册表:
可以通过Symbol.for(key)
方法注册一个全局的Symbol
,它会在全局注册表中创建或检索一个以该键为名的Symbol
。
let globalSym = Symbol.for('foo'); // 如果 'foo' 不存在,则创建一个新的 Symbol
let otherGlobalSym = Symbol.for('foo'); // 如果 'foo' 已存在,则返回已存在的 Symbol
globalSym === otherGlobalSym; // true
- 内置 Symbol 值:
ES6 还定义了一些内置的Symbol
值,如Symbol.iterator
、Symbol.asyncIterator
、Symbol.toStringTag
等,它们用于表示对象的特殊行为。
class MyClass {
[Symbol.toStringTag] = 'MyClass';
}
console.log(Object.prototype.toString.call(new MyClass())); // '[object MyClass]'
- 类型转换:
Symbol
值不能与其他类型的值进行运算,也不能转换成数字,但可以转换成字符串和布尔值。
String(sym1); // 'Symbol(description)'
Boolean(sym1); // true
- 属性名的遍历:
Object.getOwnPropertySymbols()
方法可以获取一个对象所有用Symbol
做为键的属性名。
let obj = {};
let a = Symbol('a');
let b = Symbol('b');
obj[a] = 'foo';
obj[b] = 'bar';
let symbols = Object.getOwnPropertySymbols(obj);
console.log(symbols); // [Symbol(a), Symbol(b)]
console.log(obj[a]); // foo
console.log(obj[b]); // bar
示例
// 创建两个Symbol类型的变量,用作不同属性的私有标识
const id = Symbol('id');
const name = Symbol('name');
// 创建一个对象,使用Symbol作为属性名
const user = {
[id]: 123, // id属性
[name]: '张三' // name属性
};
// 尝试访问私有属性
console.log(user[id]); // 输出: 123
console.log(user[name]); // 输出: 张三
// 尝试修改私有属性
user[id] = 456;
console.log(user[id]); // 输出: 456
// 使用Symbol属性模拟私有方法
const privateMethod = Symbol('privateMethod');
user[privateMethod] = function() {
return `${this[name]}的ID是${this[id]}`;
};
// 调用私有方法
console.log(user[privateMethod]()); // 输出: 张三的ID是456
// 尝试枚举对象属性
for (let key in user) {
console.log(key); // 不会输出Symbol属性,因为它们不会出现在for...in循环中
}
// 使用Object.getOwnPropertySymbols获取所有的Symbol属性
let symbolKeys = Object.getOwnPropertySymbols(user);
console.log(symbolKeys); // 输出: [Symbol(id), Symbol(name), Symbol(privateMethod)]
// 遍历Symbol属性
symbolKeys.forEach(sym => {
console.log(user[sym]);
});
10.类
介绍
在JavaScript ES6中,class
关键字被引入,为JavaScript提供了一个类(class)的语法糖,使得JavaScript的面向对象编程变得更加直观和易于理解。尽管这并不是JavaScript引入类的真正语法,但它提供了一种声明类的简洁方式,同时保持了JavaScript基于原型的继承机制。
细节
- class关键字: 使用 class 关键字来声明一个类,后面跟着类的名称。
- 构造函数: 类的构造函数是使用
constructor
关键字定义的,它会在创建类的新实例时被调用。构造函数中的 this 关键字指向新创建的实例。 - 实例方法: 类的主体内部可以定义实例方法。这些方法会被添加到类的原型上,因此所有实例都能访问这些方法。
- 静态方法: 使用
static
关键字定义静态方法。静态方法属于类本身,而不是类的实例。调用静态方法时不需要类的实例,直接通过类来调用。 - 继承: 使用
extends
关键字来创建一个类,该类继承另一个类的属性和方法。子类的构造函数中必须调用 super(),它会执行父类的构造函数。 - 原型链: 类的继承仍然基于原型链。子类的原型是父类的一个实例,这意味着子类可以访问父类原型上的属性和方法。
- 方法遮蔽: 如果子类定义了一个与父类相同名称的方法,那么子类的方法会遮蔽(override)父类的方法。在子类的方法内部,可以使用 super 关键字来调用父类的同名方法。
- getter和setter: 类的主体内部可以定义getter和setter,用于控制对类属性的访问和修改。
// 定义一个基类
class Animal {
name;
sound;
// 构造函数
constructor(name, sound) {
this.name = name;
this.sound = sound;
}
// 实例方法
speak() {
return `${this.name} says${this.sound}`;
}
// 静态方法
static describe() {
return 'This is an animal.';
}
}
// 定义一个继承自Animal的子类
class Dog extends Animal {
// 构造函数
constructor(name, sound, breed) {
super(name, sound); // 调用父类的构造函数
this.breed = breed;
}
// 方法遮蔽
speak() {
return `${super.speak()}WOOF!`; // 调用父类的speak方法
}
// getter和setter
get sound() {
return this.sound;
}
set sound(newSound) {
this.sound = newSound;
}
}
// 创建类的实例
const dog = new Dog('Buddy', 'Bark', 'Golden Retriever');
console.log(dog.speak()); // 输出: Buddy says BarkWOOF!
console.log(Animal.describe()); // 输出: This is an animal.
// 使用getter和setter
console.log(dog.sound); // 输出: Bark
dog.sound = 'Yip';
console.log(dog.sound); // 输出: Yip
-
公有和私有字段:
-
- 共有字段:在ES2020之前,可以在构造函数中或者类的外部使用 this 来添加公有字段。从ES2020开始,可以直接在类主体内部声明公有字段,而不需要在构造函数中。
- 私有字段:私有字段是在ES2020中引入的,使用 # 前缀来声明。私有字段只能在类的内部访问,包括构造函数、实例方法和私有方法中。
// ES2020+ 公有字段语法
class Person {
name; // 公有字段
age; // 公有字段
constructor(name, age) {
this.name = name;
this.age = age;
}
}
const person = new Person('Alice', 30);
console.log(person.name); // 输出: Alice
console.log(person.age); // 输出: 30
// ES2020+ 私有字段语法
class Person {
#name; // 私有字段
#age; // 私有字段
constructor(name, age) {
this.#name = name;
this.#age = age;
}
greet() {
return `Hello, my name is ${this.#name} and I am${this.#age} years old.`;
}
getAge() {
return this.#age;
}
}
const person = new Person('Alice', 30);
console.log(person.greet()); // 输出: Hello, my name is Alice and I am 30 years old.
// console.log(person.#name); // 错误: SyntaxError: Private field '#name' must be declared in an enclosing class
console.log(person.getAge()); // 输出: 30
- 类表达式: 类也可以定义为类表达式,它可以是命名或匿名的,并且可以立即执行。
11.模块
介绍
静态结构:ES6模块支持静态导入和导出语句,这意味着模块的依赖关系在编译时就已经确定,而不是在运行时。
默认导出与命名导出:可以使用export default来导出默认成员,每个模块只能有一个默认导出。同时,还可以使用export来命名导出多个成员。
导入整个模块:可以使用import * as name from 'module’来导入整个模块。
模块提升:模块中的import和export语句会首先被解析,这意味着它们位于模块的顶级作用域,并且有提升的效果。
使用ES6模块
导出模块:
// 导出默认成员
export default function f1() {
console.log('This is f1');
}
// 命名导出
export const v = 'someValue';
export function f2() {
console.log('This is f2');
}
导入模块:
// 导入默认导出
import f1 from './myModule.js';
// 导入命名导出
import { v, f2 } from './myModule.js';
// 导入整个模块
//import * as myModule from './myModule.js';
//使用模块
// 使用默认导出
f1();
// 使用命名导出
console.log(v);
f2();
注意事项:
- 在浏览器中使用ES6模块时,需要服务器支持,或者文件使用.mjs扩展名。
- 在Node.js中,ES6模块需要.mjs扩展名或者在package.json中设置"type": “module”。
{
"name": "es6",
"version": "1.0.0",
"description": "",
"main": "main.js",
"type": "module",//主要是这句
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
//...
}
当你设置了"type": "module"后,你就可以在项目中使用ES6模块语法,例如使用import和export语句,而不需要将文件命名为.mjs。
- 导入和导出的语句必须位于模块的顶层作用域,不能嵌套在条件语句或其他语句中。
// 导入默认导出
let sum = 0;
for(let i = 0;i <10;i++){
import f1 from './myModule.js';//error
sum += i;
}
f1();
12.Promise
JavaScript ES6中的Promise
是一种用于处理异步操作的对象。它代表了一个尚未完成但预期将来会完成的操作的结果。Promise
是对未来某个时刻可能知道的结果的一个代理。
介绍
- 状态:
Promise
对象有三种状态:
-
pending
(进行中):初始状态,既不是成功,也不是失败状态。fulfilled
(已成功):意味着操作成功完成。rejected
(已失败):意味着操作失败。
- 状态转换:
pending
可以转换为fulfilled
或rejected
,一旦状态转换,就不会再变。 - 值:
Promise
的fulfilled
状态有一个不可变的值,rejected
状态有一个原因(错误信息)。 - 消费Promise:可以通过
.then()
和.catch()
方法来使用或处理Promise
的值或错误。 - 几个静态方法:
Promise.all(iterable)
: 等待所有Promise都解决后再解决。Promise.race(iterable)
: 等待第一个解决的Promise。Promise.resolve(value)
: 返回一个成功的Promise。Promise.reject(reason)
: 返回一个拒绝的Promise。
创建和使用
使用 new Promise()
创建Promise
// 创建一个返回 Promise 的函数,该函数在一段时间后解决或拒绝
function simulateAsyncOperation(success) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (success) {//根据success的值处理Promise
resolve('操作成功!'); // 成功解决 Promise
} else {
reject(new Error('操作失败!')); // 拒绝 Promise 并传递错误
}
}, 2000); // 模拟 2 秒的延迟
});
}
// 使用 Promise
var promise = simulateAsyncOperation(true);
console.log(promise);
Promise链和错误处理
Promise可能处于不同状态,我们可以使用 then
接收成功状态,使用 catch
接收失败状态
// 使用 Promise
simulateAsyncOperation(true)
.then((message) => {
console.log(message); // 如果操作成功,将打印出“操作成功!”
})
.catch((error) => {
console.error(error.message); // 如果操作失败,将打印出“操作失败!”
});
注意事项
Promise
一旦被解决或拒绝,它的状态就不再改变,这意味着多次调用resolve
或reject
只有第一次调用有效。Promise
中的错误应该总是通过reject
来处理,而不是使用throw
语句。Promise
链中返回的非Promise
值会自动包装成Promise
。
Promise
是处理异步JavaScript操作的一种强大方式,它提供了一种更加简洁和可读的代码结构,尤其是在处理多个异步操作时。
13.asysc函数
async
函数是一种特殊的函数,它允许使用async
关键字声明,使得函数返回一个Promise
。async
函数可以让异步代码的编写看起来更像是同步代码,使得代码更加清晰和易于理解。async
函数内部可以使用await
关键字,这使得异步操作看起来像是在同步顺序中执行。
async函数的特点
- 返回Promise:
async
函数始终返回一个Promise
。如果函数返回一个非Promise
的值,该值会自动包装成一个解决的Promise
。如果函数抛出异常,则返回一个被拒绝的Promise
。 - 使用await:在
async
函数内部,你可以使用await
关键字来暂停代码的执行,直到等待后面的Promise
解决。这允许你编写看起来像同步代码的异步逻辑。 - 错误处理:
async
函数可以通过try...catch
语句来捕获await
后面的Promise
拒绝时的错误。
如何定义和使用async函数
- 在函数前添加
async
定义async函数,之后可以像使用普通函数一样使用 - 在async函数内部,使用await关键字来等待一个Promise解决,允许编写看起来像同步代码的异步逻辑。
// 定义一个async函数
async function getUserInfo(userId) {
try {
// 使用await等待Promise解决
const response = await fetch(`https://api.example.com/users/${userId}`);//fetch()用于发起网络请求
if (!response.ok) {
throw new Error('Network response was not ok');
}
const userInfo = await response.json();
return userInfo;
} catch (error) {
// 处理错误
console.error('There was a problem with the fetch operation:', error);
throw error;
}
}
// 使用async函数
getUserInfo(123)
.then((userInfo) => {
console.log(userInfo);
})
.catch((error) => {
console.error('Failed to get user info:', error);
});
注意事项:
await
关键字只能在async
函数内部使用。async
函数可以包含多个await
表达式,每个表达式都会按顺序执行。async
函数可以与其他异步模式(如回调函数、事件监听器、Promise
链等)一起使用。
async
函数是ES6及以后版本JavaScript中处理异步编程的一种非常方便和强大的方式,它提供了一种更简洁、更易读的异步代码编写模式。
14.fetch()
fetch()
是一个全局的 JavaScript 函数,用于发起网络请求。它返回一个 Promise,这个 Promise 在请求完成时会解决为一个 Response 对象。fetch()
是 Web API 的一部分,旨在提供一种更简单、更现代的方式来处理网络请求。
JavaScript 实现了多种 Web API,这些 API 可以分为多个类别,如文档对象模型 (DOM) API、浏览器对象模型 (BOM) API、网络 API等,使得开发者能够构建丰富的Web应用程序。
基本用法
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Fetch error:', error));
传递选项
fetch()
函数可以接受第二个参数,一个配置对象,用于自定义请求。这个配置对象可以包含诸如方法、头部、主体内容等信息。
fetch('https://api.example.com/data', {
method: 'POST', // 指定请求方法
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer your-token'
},
body: JSON.stringify({ key: 'value' }) // 发送的数据
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Fetch error:', error));
处理响应状态
在处理 fetch()
返回的 Response 对象时,你可能需要检查响应的状态码来确定请求是否成功。
fetch('https://api.example.com/data')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error('Fetch error:', error));
三、NPM包管理器
1.介绍
官网:https://www.npmjs.com/
我们可以通过模块化的方式来封装自己的代码,或者封装一个工具,这个工具可以让同事通过导入的方式来使用,甚至可以分享给全世界的程序员来使用。
npm全程是Node Package Manager,也就是Node包管理器。但是它目前已经不仅仅是Node包管理器了,在前端项目中我们也使用它来管理依赖的包,包括:vue,vue-router,koa,axios,webpack等等。
2.下载与配置
本文不多阐述,可以参考别人的博客或自行搜索。
3.n工具和nvm工具
n工具
安装n
npm install -g n
n工具常用命令
arduino复制代码n <version> // 下载某一版本号node e.g:n 10.16.0
n latest // 安装最新版本
n stable // 安装最新稳定版
n lts //安装最新长期维护版(lts)
n rm <version> // 删除某个版本 e.g:n rm 10.16.0
n // 输入命令后直接使用上下箭头选择版本
nvm
nvm可以方便的在同一台设备上进行多个node版本之间切换。
- 1.卸载node.js(如果有),下载nvm安装包。安装包下载地址:https://github.com/coreybutler/nvm-windows/releases,windows系统下载nvm-setup.zip安装包
- 安装nvm
- 配置淘宝镜像。找到你下载的位置,然后打开settings.txt,那里边有两行代码,换行添加下边两行代码。
node_mirror: https://npm.taobao.org/mirrors/node/
npm_mirror: https://npm.taobao.org/mirrors/npm/
nvm工具常用命令
nvm off // 禁用node.js版本管理(不卸载任何东西)
nvm on // 启用node.js版本管理
nvm install <version> // 安装node.js的命名 version是版本号 例如:nvm install 8.12.0
nvm uninstall <version> // 卸载node.js是的命令,卸载指定版本的nodejs,当安装失败时卸载使用
nvm ls // 显示所有安装的node.js版本
nvm list available // 显示可以安装的所有node.js的版本
nvm use <version> // 切换到使用指定的nodejs版本
nvm v // 显示nvm版本
nvm install stable // 安装最新稳定版
- 下载指定版本,例如:
nvm install 20.11.0
- 使用指定版本,例如:
nvm use 20.11.0
- 检查node是否安装成功
node -v
npm -v
- 如果以上步骤出现错误,可能需要配置环境变量(一般会自动配置)
4.package.json
4.1介绍
package.json
会记录着你项目的名称、版本号、项目描述等,也会记录着你项目所依赖的其他库的信息和依赖库的版本号。
4.2获取
- 当我们从零开始创建项目的时候,我们需要:
npm init -y
生成的package.json如下(仅供参考):
{
"name": "es6",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "FL",
"license": "ISC"
}
- 如果是通过脚手架创建项目,脚手架会帮助我们生成package.json,并且配置有相关信息
4.3常见属性
name :必填,为项目的名称。
version: 必填,当前项目的版本号。
description:描述信息,很多时候是作为项目的基本描述
author:作者的相关信息(发布时会用到)
license:开源协议(发布的时候用到)
private属性:private属性记录当前项目是否私有。当值为true
时,npm无法发布,这是为了防止私有项目或者模块发布出去的方式。
module属性:main属性为文件的入口。这个非常重要,我们引入某个库require(‘xxxx’)就是自动从main找找对应的入口文件。
script属性:script属性支持用户配置一些自定义的脚本命令(定制化操作)。
type:设置为module,实现CommonJS规范。
4.4依赖的版本管理
在依赖的版本前面,会有~
和^
这连个符号,通常都是^
。这是因为npm的包通常都要遵从semever版本规范。
semver版本的规范是X.Y.Z
- X主版本号(major):当你做了不兼容的API修改(可能不兼容之前的版本)
- Y次版本号(minor):当你做了向下兼容的功能性新增(新功能增加,但是兼容之前的版本)
- Z修订号(patch):当你做了 向下兼容的问题修真(没有新功能,修复了之前版本的bug)
关于^和~ 连个符号
- ^x.y.z:表示x是保持不变的,y和z永远安装最新的版本。 这种我们一般称之为
大概的版本
- ~x.y.z:表示x和y不变,z永远安装最新的版本。
5.NPM常用指令
5.1 install
- 在使用
npm install
命令时,可以使用--save-dev
标志将依赖项添加到开发依赖项中,使用--save
标志将依赖项添加到生产依赖项中。 - 此外,还可以使用
--global/-g
标志进行全局安装,但这在大多数情况下不被推荐,可能导致版本冲突,或者在不同项目中使用不同版本的包时出现问题。因此,许多开发人员更倾向于使用本地安装,特别是当工具或库的版本需要根据项目不同而变化时。 - 在现代开发实践中,随着npx的引入,全局安装的必要性进一步减少了。npx可以在不全局安装包的情况下运行CLI工具,它会临时安装包到一个临时目录中,使用后即删除。这种方式可以确保你总是使用最新版本的包,并且避免了全局安装可能带来的问题。
# 全局安装
npm install package -g
# 项目(局部)安装
npm install package
示例:
# 全局安装
npm install webpack -g
# 项目(局部)安装
npm install webpack
仅以下情况推荐全局安装:
- 命令行工具。如:webpack、create-react-app、Vue CLI 等脚手架工具。
- 全局命令行工具集。如:像Gulp、Grunt这样的任务运行器,或者像Angular CLI这样的全局命令行工具集。
- 全局服务。如:数据库服务(如MongoDB、MySQL)或开发服务器(如live-server)。
5.2 uninstall
# 让 npm 根据 package.json 中的定义自动判断依赖类型
npm uninstall package
# 显式地说明你正在移除一个开发依赖
npm uninstall package -D
5.3 强制rebuild
npm rebuild
在以下情况下,你应该执行npm rebuild:
- 安装了新的npm模块或更新了现有的模块
- 更改了Node.js版本
- 升级了操作系统或更改了系统环境
- 发生了构建失败或模块损坏的情况
5.5 清除缓存
npm cache clean
在以下情况下,你应该执行npm cache clean
:
- 占用空间过多
- 安装了错误的依赖版本
- 更新了npm版本
- 修复依赖关系冲突
5.6 其他命令
6. cnpm工具
对于大多数人来说,并不希望将npm镜像去修改。
- 第一,我们不太愿意随意修改npm原本从官方下来包的渠道(其他渠道会有一些延迟)。
- 第二,担心某天比如淘宝的镜像不维护了,又要改来改去。
这个时候我们就可以使用cnpm工具了,并且将cnpm设置为淘宝的镜像
# 安装cnpm
npm install -g cnpm --registry=https://registry.npm.taobao.org
# 检查版本
cnpm -v
7.npx工具
作用:方便的使用局部的package工具
npx package --version
适用场景:
- 检查软件包的版本
- 确认软件包是否已安装
- 调试和故障排除
8.发布自己的包
- 注册
npm login
- 发布我们的包
npm publish
适用场景:
- 对某个软件包进行了重要的更新或修复,并且希望将这些更改发布到公共软件包注册表时
- 使用私有注册表或其他工具来发布软件包,而现在决定将其发布到公共注册表(如npm)中
- 需要更改软件包的发布配置时,例如更新发布的访问权限、标记稳定版本等
其他的命令:
# 删除发布的包
npm unpublish
# 让发布的包过期
npm deprecate
四、Babel转码器
1.介绍
Babel
是一个广泛使用的JavaScript转码器,用于将新版本的JavaScript代码转换为向后兼容的旧版本代码。
它实现了ECMAScript规范中的各种转换规则,例如箭头函数、解构赋值、模板字符串、类和模块等。
2.安装与使用
- 将Babel作为项目的开发依赖项进行本地安装比全局安装更可取。
- 安装Babel的核心模块和所需的转换插件
npm install --save-dev @babel/core @babel/cli @babel/preset-env
这将安装Babel的核心模块(`@babel/core`)以及命令行工具模块(`@babel/cli`)和用于转换ES6+语法的预设模块(`@babel/preset-env`)。这些模块将作为开发依赖项安装,并在`package.json`文件的`devDependencies`部分中列出。
- 创建一个名为
.babelrc
的文件,并在其中指定Babel的配置。
{
"presets": ["@babel/preset-env"]
}
这将告诉Babel使用`@babel/preset-env`预设来进行转换,该预设将根据目标环境自动确定需要的转换规则。
- 使用Babel来转换你的JavaScript代码
npx babel src --out-dir dist
这将使用Babel将`src`目录中的JavaScript代码转换并输出到`dist`目录中。你可以根据你的项目结构和需求进行相应的调整。
3.自定义脚本
详细的配置选项和用法可以在 Babel 官方文档 中找到
- 在
.babelrc
文件中的plugins
字段中添加插件名称或配置对象
{
"presets": ["@babel/preset-env"],
"plugins": ["log-function-calls"]
}
- 创建一个名为
log-function-calls.js
的文件,用于编写自定义 Babel 插件的逻辑
module.exports = function logFunctionCalls({ types }) {
return {
visitor: {
CallExpression(path) {
console.log('Function call:', path.node.callee.name);
path.traverse({
Identifier(path) {
console.log('Argument:', path.toString());
}
});
}
}
};
};
自定义插件 log-function-calls 会在每个函数调用前后打印日志。它通过 Babel 的访问器对象 path 来访问和修改 AST,使用 types 提供的方法来操作节点。
- 在命令行中运行 Babel CLI 来转换代码
npx babel src --out-dir dist
五、WebPack
1.介绍
- Webpack是一个现代JavaScript应用程序的静态模块打包器(module bundler)。当你在浏览器中加载一个应用程序时,你希望尽可能减少HTTP请求的数量,以提高加载速度和性能。
- Webpack的作用就是将你的应用程序中的所有模块(例如,你的JavaScript文件、CSS文件、图片、字体等)打包成一个或多个bundle。
本教程只记录常用功能,其他详情可见官网
2.安装与使用
- 全局安装WevPack(不推荐)
npm install --global webpack webpack-cli
- 初始化npm项目
npm init
- 本地安装Webpack(推荐)
npm install --save-dev webpack webpack-cli
- 执行Webpack打包
npm run build
3.创建Webpack配置文件
- 在项目根目录中创建一个名为webpack.config.js的文件。在这个文件中,你需要配置Webpack的入口点和输出文件。
- 注意,webpack.config.js必须在根目录下
例如,如果你有一个名为index.js的入口文件,你可以在webpack.config.js中这样配置:
// webpack.config.js
const path = require('path');
module.exports = {
entry: './index.js',
output: {
path: path.resolve(__dirname, 'dist'), // string
filename: 'main.js',
},
mode: 'development',
};
4.webpack合并JS
假设你的项目目录结构如下:
project/
├── dist/
├── src/
│ ├── index.js
│ └── test.js
├── package.json
└── webpack.config.js
- 创建工程,初始化
npm init
- 局部安装webpack:
npm i webpack webpack-cli -D
- 新建 webpack.config.js 配置文件(在根目录)
// webpack.config.js
const path = require('path');
module.exports = {
// 入口
entry: path.resolve(__dirname, 'src/index.js'),
// 出口
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: './'
}
}
- 定义模块:
在你的 JavaScript 文件中,你可以使用 import 语句来引入其他模块。Webpack 会解析这些导入语句,并将它们打包在一起。
例如,在index.js中:
//入口文件:src/index.js
const test = require('./test');
const a = 12
const b = 12
function add(x, y) {
return x + y
}
const c = add(a,b)
console.log(c)
test();
test.js中定义相应的功能或变量。
//非入口文件:src/test.js
function test(){
console.log(2);
}
module.exports = test;
- package.json 下的 scripts 中添加打包命令:
"build": "webpack --mode development"
- 这里说明一下,不加 –mode development,默认打包是生产环境,打包出来的代码会默认压缩
- 进行打包
npm run build
- 打包完成后,你会在配置的输出目录(在上面的例子中是dist目录)中找到一个或多个打包文件(bundle.js)。
5.处理CSS
- Webpack 本身只能处理 JavaScript 文件,但是通过
加载器
(loaders),Webpack 可以调用外部的工具来处理其他类型的文件,例如 CSS、图片等。 - 加载器是运行在 Node.js 中的函数,它接收资源文件作为参数,并返回转换后的结果。
假设你的项目目录结构如下:
demo
├─ src
│ ├─ css
│ │ └─ index.css
│ ├─ js
│ │ └─ idnex.js
│ │ └─ test.js
├─ webpack.config.js
└─ package.json
处理CSS步骤如下(示例):
- 创建工程,初始化
npm init
- 局部安装webpack:
npm i webpack webpack-cli -D
- 安装CSS加载器:Webpack 本身只能理解 JavaScript,所以需要安装 css-loader 和 style-loader 来处理 CSS 文件。
npm install css-loader style-loader -D
- 新建 webpack.config.js 配置文件(在根目录),在 webpack.config.js 文件中,添加一个模块(module)部分来配置加载器(loader)。
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');//方便清理 dist 目录中的旧文件
module.exports = {
// 入口
entry: path.resolve(__dirname, 'src/js/index.js'),
// 出口
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: './',
},
module: {
rules: [
{
test: /\.css$/, // 匹配CSS文件
use: ['style-loader', 'css-loader'], // 使用style-loader和css-loader
},
],
},
plugins: [
new CleanWebpackPlugin(), // 实例化插件
],
};
- publicPath:指定基础路径,开发环境一般是项目的根路径,上线之后一般是CDN的路径。
- __dirname:表示项目所在目录的根路径。
//入口文件:src/js/index.js
import './css/index.css'; // 在顶部导入CSS文件
const test = require('./test');
const a = 12;
const b = 12;
function add(x, y) {
return x + y;
}
const c = add(a, b);
console.log(c);
test();
//非入口文件:src/js/test.js
function test(){
console.log(2);
}
module.exports = test;
/* src/css/index.css */
body {
background-color: #f2f2f2;
}
- 在 package.json 中已经定义了 build 脚本:
"scripts": {
"build": "webpack --mode production"
}
- 进行打包
npm run build
6.处理图片
假设你的项目目录结构如下:
demo
├─ src
│ ├─ css
│ │ └─ index.css
│ ├─ js
│ │ └─ idnex.js
│ │ └─ test.js
│ └─ image
│ └─ image.jpeg
├─ index.html
├─ webpack.config.js
└─ package.json
- 安装加载器:
npm install file-loader url-loader -D
file-loader 用于处理图片文件,而 url-loader 可以将小图片转换为 base64 编码的字符串,从而减少 HTTP 请求。
- 配置 webpack.config.js
// webpack.config.js
const path = require('path');
module.exports = {
// ...其他配置
module: {
rules: [
// ...其他加载器配置
{
test: /\.(png|jpg|jpeg|gif)$/i, // 匹配图片文件
use: [
{
loader: 'url-loader', // 使用 url-loader
options: {
limit: 8192, // 图片大小小于8KB时转换为base64
name: 'images/[name].[hash:8].[ext]', // 设置输出图片的目录和名称
},
},
],
},
],
},
};
-
在 JavaScript 中导入图片:
在 src/js/index.js 中,你可以使用 import 语句来导入图片。
代码如下:
// src/js/index.js
import image from './image/image.jpeg';
// 使用图片,例如将其作为背景图
const imgElement = document.createElement('img');
imgElement.src = image;
document.body.appendChild(imgElement);
- 打包项目
npm run build
- 在 index.html 中引用打包后的资源:
由于图片文件是通过 url-loader 处理的,并且 file-loader 会将图片输出到 dist 目录中,你不需要在 index.html 中显式引用图片。Webpack 会处理图片路径,并在打包后的 JavaScript 或 CSS 文件中更新图片的引用路径。
7.clean-webpack-plugin
如果你之前已经运行过 npm run build,那么在重新构建之前,你可能想要清理 dist 目录中的旧文件。这可以通过使用一个插件如 clean-webpack-plugin 来自动完成。
- 安装插件
npm install clean-webpack-plugin -D
- 配置插件
在你的 webpack.config.js 文件中,首先需要引入 CleanWebpackPlugin,然后在插件(plugins)数组中实例化它。
// 引入插件
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
// ...其他配置
plugins: [
// ...其他插件
new CleanWebpackPlugin() // 实例化插件
]
};
-
lean-webpack-plugin 默认会删除 output.path 目录中的所有文件。如果你需要自定义要删除的文件或目录,可以在实例化插件时传递一个对象作为配置。
例如,如果你只想删除 dist 目录中的特定文件或目录,可以这样做:
new CleanWebpackPlugin({
cleanOnceBeforeBuildPatterns: ['**/*', '!static-files/**'] // 这里配置要保留的文件或目录
})
- 重新执行构建
配置好插件后,当你再次运行 npm run build 时,clean-webpack-plugin 将在构建开始之前自动清理指定的文件和目录。
npm run build
六、其他
前端开发与前端开发工具
模块化
1.介绍
JavaScript 模块化编程是一种将代码组织为可重用、可维护和可扩展模块的开发方法。
它通过将代码拆分为独立的模块来解决传统的脚本编程的一些问题,并提供了一种更好的方式来管理代码的结构和依赖关系。
在传统的脚本编程中,所有的代码都放在一个全局命名空间中,这导致了以下问题:
- 命名冲突
- 代码复用困难
- 可维护性差
- 协作困难
- 安全性低
通过使用 JavaScript 模块化编程,可以解决上述问题并带来以下优势:
- 封装和隔离:模块之间的接口通过明确定义的导出和导入来进行通信,减少了全局命名冲突的可能性。
- 代码复用:使得代码可以按照功能或特定的业务逻辑进行划分,并在需要的地方导入和使用。
- 维护性和可扩展性:每个模块都专注于特定的功能,可以方便地添加、删除或更新特定的功能模块
- 团队合作:有助于团队分工和协作,使得代码共享变得容易
- 系统安全性:有助于实施最小权限原则,即每个模块只拥有执行其任务所需的最少权限。确保应用程序使用的是安全的库和框架版本。
2.CommonJS规范
-
CommonJS是一种JavaScript模块规范,它允许开发者将每个文件作为一个模块来使用。在CommonJS中,每个模块都有自己的作用域,这意味着在一个文件中定义的变量、函数和类默认是私有的,不会污染全局作用域。
-
模块内部的属性和方法可以通过
exports
和require
进行导入导出函数、类、变量,私有的模块不能被外部访问。 -
CommonJS规范最初是为服务器端JavaScript设计的,特别是为了在Node.js中使用。但在浏览器端可能会导致性能问题,浏览器端通常使用异步加载的模块系统,如ES6模块(见3)。
示例:
myModule.js
// 定义一个简单的函数,这个函数是模块私有的
function privateFunction() {
console.log('这是一个私有函数');
}
// 定义一个变量,这个变量也是模块私有的
const privateVariable = '这是一个私有变量';
// 使用exports将一个函数暴露给外部
exports.publicFunction = function() {
console.log('这是一个公开的函数');
};
// 使用module.exports将一个对象暴露给外部
module.exports.someObject = {
key: '这是一个公开的属性',
publicMethod: function() {
console.log('这是一个公开的方法');
// 在模块内部调用私有函数
privateFunction();
}
};
// 可以直接替换整个module.exports对象
module.exports = {
anotherPublicFunction: function() {
console.log('这是另一个公开的函数');
}
// 注意:一旦直接替换了module.exports,之前的exports将不再有效
};
main.js
// 导入上面定义的模块
const myModule = require('./myModule.js');
// 调用模块暴露的公开函数
myModule.publicFunction(); // 输出:这是一个公开的函数
// 调用模块暴露的公开方法
myModule.someObject.publicMethod(); // 输出:这是一个公开的方法
// 注意:如果myModule.js中直接替换了module.exports,这里的调用方式可能会有所不同
- 如果提示
Warning: To load an ES module, set "type": "module" in the package.json
,就需要在package.json中添加下面语句
"type": "module"
- 如果提示
require is not defined in ES module scope, you can use import instead
,在你要require的代码前引入如下代码即可
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
3.ES6模块化规范
- ES6模块化规范(也称为ECMAScript 2015模块)是JavaScript官方的模块化系统。与CommonJS不同,ES6模块是静态的,意味着模块的依赖关系是在编译时确定的,而不是在运行时。ES6模块也支持异步加载和编译,这使得它们适合在浏览器中使用。
- 在ES6模块中,使用
export
关键字来导出函数、类、变量等,使用import
关键字来导入其他模块导出的成员。
// 导出变量
export const pi = 3.14159;
// 导出函数
export function sum(x, y) {
return x + y;
}
// 导出类
export class Circle {
constructor(radius) {
this.radius = radius;
}
area() {
return pi * this.radius * this.radius;
}
}
// 默认导出
export default function(x, y) {
return x - y;
}
// 在同一个模块中,也可以组合导出
export { pi, sum, Circle };
// 在另一个文件中,可以这样导入上面的模块
import { pi, sum, Circle } from './myModule.js';
import subtract from './myModule.js'; // 导入默认导出的函数
// 使用导入的模块成员
console.log(sum(pi, pi)); // 输出:6.28318
const circle = new Circle(10);
console.log(circle.area()); // 输出:314.159
// 使用默认导出的函数
console.log(subtract(10, pi)); // 输出:6.85841
- ES6模块在浏览器和现代JavaScript环境中得到了广泛支持,但为了兼容性,有时需要使用打包工具(如Webpack)或转换工具(如Babel)来处理模块。
案例
vue3-antd-admin
- vue3-antd-admin 基于vue-cli5.x/vite2.x + vue3.x + ant-design-vue3.x + typescript hooks 的基础后台管理系统模板 RBAC的权限系统, JSON Schema动态表单,动态表格,漂亮锁屏界面。
- 本案例只将Vue-element-admin部署到本地,体验前面的知识
npm config set registry https://registry.npm.taobao.org # 如果依赖下载慢就执行
npm install / npm install -f(强制安装)
- 下载完依赖后,使用下面命令运行
npm run dev
- 大家可以多在github或gitee找一些开源项目运行到本地,提前熟悉节约以后的开发时间
附录
参考文献: