Js
Js 基础语法
01 变量与数据类型📦
这是编程的基石。在 JavaScript 中,我们主要用 let 和 const 来存储数据。
const: 用来声明一个常量。一旦声明,就不能再改变指向(推荐优先使用)。let: 用来声明一个变量。值可以被修改。
// --- 1. 变量声明 ---
// const: 声明后不可重新赋值
const username = "Alice";
// let: 声明后可以重新赋值
let age = 25;
age = 26; // 这是合法的
console.log(age); // 输出: 26
// --- 2. 基本数据类型 ---
// 字符串 (String)const message = "Hello World";
const greeting = `你好, ${username}`; // 模板字符串,可以嵌入变量
// 数字 (Number)// JS 中不区分整数和浮点数,统称 numberconst score = 100;
const price = 99.99;
// 布尔值 (Boolean)// 用来做判断,只有 true 或 falseconst isLogin = true;
const isAdult = score >= 18; // 结果是 true
// --- 3. typeof 操作符 ---// 用来检查变量的类型
console.log(typeof username); // 输出: "string"
console.log(typeof score); // 输出: "number"
console.log(typeof isLogin); // 输出: "boolean"
// --- 4. 特殊值 ---
// null: 表示“空值”或“无”,需要手动赋值
let car = null;
console.log(car); // 输出: null
// undefined: 表示“未定义”,变量声明了但没赋值
let house;
console.log(house); // 输出: undefined
02 模板字符串
模板字符串用反引号 `` 包裹,并用 $ {} 插入变量或表达式。
const user = {
name: "Alice",
age: 25,
hobbies: ["读书", "游泳"]
};
// --- 1. 基本插值 ---// 用 ${} 包裹变量或表达式
const greeting = `你好,我是 ${user.name},明年我就 ${user.age + 1} 岁了!`;
console.log(greeting);
// 输出: 你好,我是 Alice,明年我就 26 岁了!
// --- 2. 多行文本 (保留换行) ---
// 普通引号里换行会报错,或者必须用 \n// 模板字符串直接回车即可,格式完全保留
const letter = `
亲爱的 ${user.name}:
欢迎你加入我们的俱乐部。
你的兴趣爱好是: ${user.hobbies.join("、")}。
祝好!
`;
console.log(letter);
// 输出会是格式整齐的信件,保留了缩进和换行
// --- 3. 逻辑运算 (三元表达式) ---
// 可以在 ${} 里写简单的逻辑
const status = `用户状态: ${user.age >= 18 ? "成年人" : "未成年人"}`;
console.log(status); // 输出: 用户状态: 成年人
03 逻辑控制与循环
几乎和 java 一样啊,太爽了,我真让 python 调坏了,真服了,每次写完 python 再写其他的,我左右脑都快打起来了....
条件分支
// if else if else 结构
if (age > 10){
console.log("大于10")
}else if(age > 15){
console.log("大于15")
}else{
console.log("小于10")
}
// 三元运算符
str = a > 10 ? "aaa" : "bbb"
逻辑运算符
||(或): 找真值。常用于设置默认值。&&(且): 找假值。常用于条件执行
循环 for, while, for...of
// for
for (let i = 10; i < 20; i ++){
console.log(i)
}
// while
let i = 0
while (i < 10){
i += 1
console.log(i)
}
// for of java 的增强for for(var c : list) python 本身就是这样子 for c in list
const arr = [1,2,3,4,5]
for(let i of arr){
console.log(i)
}
- break :立刻跳出整个循环
- continue:跳过本次循环,进入下一次
04 数组
和 python 一样,数组是 [] ,这里就不得不提我 java 的 {} 了,真无敌
初始化数组
添加,删除元素
arr.push() // python arr.append java(list,非静态数组) arr.add
arr.pop() # 删除并返回最后一个元素 // py arr.pop java list.removelast | first
// 这个很难记,参数: (起始位置, 删除几个, [插入的元素...])
arr.splice()
arr.splice(1, 0, 9); // 在索引1处,删除0个,插入9 -> [1, 9, 2, 3]
arr.splice(1, 1); // 在索引1处,删除1个 -> [1, 2, 3]
查找元素
遍历与转换 (核心灵魂)
const numbers = [1, 2, 3, 4, 5];
const users = [
{ name: "Alice", age: 25, active: true },
{ name: "Bob", age: 30, active: false },
{ name: "Charlie", age: 35, active: true }
];
// --- 1. map: 一一对应 (转换) ---
// 场景: 把数字数组变成平方数组,或者把用户数组变成 JSX 列表
const squares = numbers.map(n => n * n)
// 结果: [2, 4, 6, 8, 10]
console.log( squares)
const names = users.map(user => user.name);
// 结果: ["Alice", "Bob", "Charlie"]
// --- 2. filter: 筛选 (过滤) ---
// 场景: 找出所有成年人,或者所有激活状态的用户
const evenNums = numbers.filter(n => n % 2 === 0);
// 结果: [2, 4]
const activeUsers = users.filter(user => user.active);
// 结果: [{Alice}, {Charlie}]
1
// --- 3. find: 找一个 (立刻停止) ---
// 场景: 登录验证,找特定 ID 的用户
const bob = users.find(user => user.name === "Bob");
// 结果: { name: "Bob", age: 30, active: false }
// 注意: 找不到返回 undefined
// --- 4. reduce: 归并 (汇总) ---
// 场景: 求和,或者把数组转成对象
const sum = numbers.reduce((accumulator, current) => {
return accumulator + current;
}, 0); // 0 是初始值
// 结果: 15
其他实用 API
// --- forEach: 只是为了执行动作,没有返回值 ---
// 一般用来打印日志,或者修改 DOM
users.forEach(user => console.log(user.name));
// --- some / every: 判断 ---
users.some(user => user.age > 30); // 有人大于30吗? true
users.every(user => user.age > 18); // 所有人都大于18吗? true
// --- sort: 排序 (注意: 会改变原数组!) ---
// 字符串排序
["c", "a", "b"].sort(); // ["a", "b", "c"]
// 数字排序 (需要传比较函数)
[3, 1, 10].sort((a, b) => a - b); // [1, 3, 10]
// --- slice: 截取 (不改变原数组) ---
// 参数: (开始, 结束) 结束不包含
const copy = numbers.slice(); // 全拷贝 (常用)
const part = numbers.slice(1, 3); // [2, 3]
05 对象与解构赋值
对象由“键(key)”和“值(value)”组成。在 js 中,这对象怎么又是对象,又是字典的
1、对象的创建和读取
// --- 1. 创建与读取 ---
const user = {
name: "Alice",
age: 25,
// 嵌套对象
address: {
city: "Beijing",
zip: "100000"
}
};
// 点符号 (常用)
console.log(user.name); // "Alice"
console.log(user.address.city); // "Beijing"
// 括号符号 (动态读取)
const key = "age";
console.log(user[key]); // 25 (相当于 user["age"])
// --- 2. 修改与添加 ---
user.age = 26; // 修改已有
user.job = "Engineer"; // 添加新属性
// --- 3. 删除属性 ---
delete user.job; // 删除 job 属性
2、对象的遍历
for in 专门用来遍历现象,注意和 for of 区别开来
for (let key in stu){
console.log(key, stu[key]);
}
// --- 获取所有键/值 ---
console.log(Object.keys(stu));
console.log(Object.values(stu));
console.log(Object.entries(stu));
3、Es6 解构赋值
这是现代 JS 代码里出现频率极高的语法,用来“从对象或数组中提取数据”。
const product = {
title: "iPhone",
price: 999,
stock: 50,
// 嵌套
seller: {
name: "Apple Store",
level: 5
}
};
// --- 传统的取值方式 (太啰嗦) ---
// const title = product.title;
// const price = product.price;
// --- 解构赋值 (优雅) ---
// 声明变量名必须和属性名一致
const { title, price } = product;
console.log(title); // "iPhone"
// --- 重命名变量 ---// 如果你想把 price 存到变量 p 中
const { price: p } = product;
console.log(p); // 999
// --- 默认值 ---// 如果对象里没有 discount 属性,默认设为 0
const { discount = 0 } = product;
console.log(discount); // 0
// --- 嵌套解构 ---// 直接从 seller 对象里拿出
levelconst { seller: { level } } = product;
console.log(level); // 5
06 函数
函数声明与表达式
// --- 函数声明 (Function Declaration) ---
// 这种写法会被“提升”(Hoisting),可以在声明前调用
function add(a, b) {
return a + b;
}
console.log(add(2, 3)); // 5
// --- 函数表达式 (Function Expression) ---
// 将函数赋值给一个变量
// 这种写法不会被提升,必须在赋值之后调用
const subtract = function(a, b) {
return a - b;
};
console.log(subtract(5, 2)); // 3
// --- 立即执行函数 (IIFE) - 了解即可 ---
// 声明的同时立刻执行
(function() {
console.log("我立刻就执行了!");
})();
// es 箭头函数
const add = (a, b) => a + b; // 单行,{} 和 return 可以省略
const f1 = (a, b, c) => {
a += 10
b += 10
c += 10
return a + b + c
}
this 关键字
-
普通函数:
this指向调用者(谁调用它,this就是谁)。这有时会导致在回调函数中this指向错误。 -
箭头函数:没有自己的
this。它的this继承自外层作用域(定义时所在的上下文),这被称为“词法绑定”。
| 类型 | 语法 | 适用场景 | 注意事项 |
|---|---|---|---|
| 函数声明 | function fn(){} |
普通工具函数,需要变量提升时 | 尽量用 const 替代 |
| 函数表达式 | const fn = function(){} |
回调、IIFE | 不能在赋值前调用 |
| 箭头函数 | const fn = () => {} |
回调函数、对象方法 (需小心 this)、返回对象 |
没有 this、arguments,不能用作构造函数 (new) |
| 默认参数 | (a=10) => {} |
参数有默认值时 | 简洁清晰 |
-
优先使用箭头函数:特别是在
map,filter,setTimeout,addEventListener这些回调场景中,箭头函数能帮你省去var self = this的麻烦。 -
对象方法慎用箭头函数:如果你需要在方法里用
this指向对象本身,不要用箭头函数,用传统的method() {}写法。 -
默认参数很好用:写组件或工具函数时,给可选参数设置默认值,能大大增加代码的健壮性。
07 异步编程
首先要明白:JavaScript 是单线程的。这意味着它同一时间只能做一件事。如果前面有个任务卡住了(比如读取大文件或网络请求),后面的任务就得一直干等着,页面就会“假死”。
异步就是为了解决这个问题而生的——它允许我们先去处理别的事情,等耗时操作完成了再回头处理结果。
回调函数
这是最早期的解决方案。简单说就是“你先干别的,干完了回来调用我”。
虽然简单,且好理解,但是回调函数会造成回调地狱
Promise
ES6 引入了 Promise,这是异步编程的一大步。它把回调函数的写法变成了“链式调用”,解决了回调地狱的问题。
Promise 有三种状态:pending(进行中)、fulfilled(成功)、rejected(失败)。
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
const success = false
if (success){
resolve('成功')
}else{
reject('失败')
}
}, 1000)
})
p1
.then(resp =>{
console.log(resp)
return '成功' // 这里可以返回一个新的promise对象
})
.then(resp =>{
console.log(resp)
}
)
.catch(error => {
console.log(error)
})
.finally(() =>{
// 无论成功还是失败,都会执行
console.log('finally')
})
async/await
这是目前最主流、最推荐的写法。async/await 其实就是 Promise 的语法糖,它让我们可以用写同步代码的风格来写异步代码。
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
const success = true
if (success){
resolve('成功')
}else{
reject('失败')
}
}, 1000)
})
async function f1(){
try{
console.log("开始请求....")
// await 会暂停函数的执行,等待 Promise 完成
// 但不会阻塞整个线程,浏览器可以去干别的事
const result = await p1
console.log(result)
console.log("请求结束")
}catch (error){
console.log(error)
}
}
f1()
Js 高级
01 axios
简介
Axios 是一个基于 Promise 的 HTTP 客户端,用于浏览器和 Node.js 环境。
安装
基本用法
get 请求
// 基本 GET 请求
axios.get('/user?id=123')
.then(response => console.log(response.data))
.catch(error => console.error(error));
// 带参数的 GET 请求
axios.get('/user', {
params: {
id: 123,
name: 'John'
}
})
.then(response => console.log(response.data));
// async/await 方式
async function getUser() {
try {
const response = await axios.get('/user', { params: { id: 123 } });
console.log(response.data);
} catch (error) {
console.error(error);
}
}
post请求
// 基本 POST 请求
axios.post('/user', {
firstName: 'John',
lastName: 'Doe'
})
.then(response => console.log(response.data));
// 带配置对象的 POST
axios.post('/user', {
firstName: 'John',
lastName: 'Doe'
}, {
headers: {
'Content-Type': 'application/json'
}
});
其他请求
axios.put('/user', { id: 1, name: 'New Name' });
axios.delete('/user', { params: { id: 123 } });
axios.patch('/user', { name: 'Updated' });
创建 axios 示例
const instance = axios.create({
baseURL: 'https://api.example.com',
timeout: 5000,
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token'
}
});
// 使用实例
instance.get('/user');
拦截器
// 添加请求拦截器
axios.interceptors.request.use(
config => {
// 在发送请求之前做些什么
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
error => {
// 请求错误时做些什么
return Promise.reject(error);
}
);
// 添加响应拦截器
axios.interceptors.response.use(
response => {
// 对响应数据做点什么
return response.data;
},
error => {
// 对响应错误做点什么
if (error.response.status === 401) {
// 未授权,跳转登录
}
return Promise.reject(error);
}
);
// 移除拦截器
const myInterceptor = axios.interceptors.request.use(/* ... */);
axios.interceptors.request.eject(myInterceptor);
解决跨域
配置代理:方案一
vue-cli 构建的项目:
// vue.config.js
module.exports = {
devServer: {
proxy: {
// 凡是 /api 开头的请求,都会被代理
'/api': {
target: 'http://api.target.com', // 目标服务器地址
changeOrigin: true, // 【核心】:是否改变请求来源,设为 true 才能绕过跨域
pathRewrite: {
'^/api': '' // 把路径中的 /api 去掉,比如 /api/user 变成 /user
}
}
}
}
}
Vite 构建的项目
// vite.config.js
export default {
server: {
proxy: {
'/api': {
target: 'http://api.target.com', // 目标地址
changeOrigin: true, // 允许跨域
rewrite: (path) => path.replace(/^\/api/, '') // 路径重写
}
}
}
}
最佳实践
// src/utils/request.js
import axios from 'axios';
const request = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
timeout: 5000
});
// 请求拦截器
request.interceptors.request.use(config => {
// 添加 token
return config;
}, error => {
return Promise.reject(error);
});
// 响应拦截器
request.interceptors.response.use(response => {
return response.data;
}, error => {
return Promise.reject(error);
});
export default request;
02 JS 模块化
为什么需要模块化?
随着项目变大,所有代码写在一个文件里会很难维护。模块化就是把代码拆分成多个小文件(模块),每个文件负责一个功能,最后再组合起来。
核心好处:
-
命名空间隔离:不同模块里的变量不会冲突
-
代码复用:写好的模块可以在多处使用
-
可维护性:修改一个模块不影响其他模块
CommonJS (Node.js 模块规范)
Node.js 采用 CommonJS 规范,使用 require 和 module.exports。
// --- 导出模块 (module.js) ---
const name = 'Alice';
const age = 25;
// 方式 1:导出单个值
module.exports = name;
// 方式 2:导出多个值(对象形式)
module.exports = {
name,
age
};
// 方式 3:等价写法
exports.name = name;
exports.age = age;
// --- 导入模块 (main.js) ---
// 导入整个模块
const mod = require('./module.js');
console.log(mod.name); // Alice
// 解构导入
const { name, age } = require('./module.js');
console.log(name); // Alice
ES Module (ES6 模块规范)
浏览器和现代前端项目采用 ES6 模块规范,使用 import 和 export。
// --- 导出模块 (module.js) ---
// 方式 1:导出变量/函数
export const name = 'Alice';
export const age = 25;
export function sayHi() {
console.log('Hi');
}
// 方式 2:默认导出(一个模块只能有一个 default)
export default function() {
console.log('我是默认导出');
}
// 方式 3:统一导出
const name = 'Alice';
const age = 25;
export { name, age };
// --- 导入模块 (main.js) ---
// 对应方式 1:按需导入
import { name, sayHi } from './module.js';
// 对应方式 2:导入默认导出(名字随便起)
import anyName from './module.js';
anyName(); // 我是默认导出
// 导入整个模块
import * as mod from './module.js';
console.log(mod.name); // Alice
// 只执行模块,不导入任何东西
import './module.js';
CommonJS vs ES Module
| 特性 | CommonJS | ES Module |
|---|---|---|
| 语法 | require / module.exports |
import / export |
| 运行环境 | Node.js | 浏览器 / 现代前端 |
| 执行时机 | 运行时加载 | 编译时静态分析 |
| 导出方式 | 只能导出对象 | 可导出多个命名 export + 一个 default |
| 循环依赖 | 支持(但可能拿到不完整值) | 不支持(会报错) |
03 JS 原型链
构造函数与 new
在 ES6 class 出现之前,JS 使用构造函数来创建对象。
// 构造函数(首字母大写是约定)
function Person(name, age) {
this.name = name;
this.age = age;
}
// 使用 new 关键字创建实例
const p1 = new Person('Alice', 25);
const p2 = new Person('Bob', 30);
console.log(p1.name); // Alice
console.log(p2.name); // Bob
new 做了什么:
-
创建一个空对象
-
将构造函数的
this指向这个空对象 -
执行构造函数
-
返回这个对象
prototype 和 __proto__
function Person(name) {
this.name = name;
}
// prototype:构造函数的属性,是一个对象
Person.prototype.sayHi = function() {
console.log('Hi, I am ' + this.name);
};
const p1 = new Person('Alice');
// __proto__:实例对象的内部属性,指向构造函数的 prototype
console.log(p1.__proto__ === Person.prototype); // true
// 调用 sayHi 时,p1 自己没有这个方法,就会沿着 __proto__ 去找
p1.sayHi(); // Hi, I am Alice
核心关系图:
原型链继承
JS 的对象查找机制:先在自身找,找不到就沿着 __proto__ 往上找,这就是原型链。
// 爷爷
function Grand() {
this.lastName = '张';
}
Grand.prototype.grandMethod = function() {
console.log('爷爷的方法');
};
// 爸爸
function Father() {
this.money = 100;
}
Father.prototype = new Grand(); // 爸爸的 prototype 指向爷爷的实例
Father.prototype.fatherMethod = function() {
console.log('爸爸的方法');
};
// 儿子
function Son() {
this.age = 10;
}
Son.prototype = new Father(); // 儿子的 prototype 指向爸爸的实例
Son.prototype.sonMethod = function() {
console.log('儿子的方法');
};
const son = new Son();
// 原型链查找
son.sonMethod(); // 自己的方法
son.fatherMethod(); // 爸爸的方法
son.grandMethod(); // 爷爷的方法
son.lastName; // 张(从原型链继承的属性)
ES6 class 语法糖
ES6 的 class 只是让写法更简洁,底层还是基于原型链。
// --- 基本用法 ---
class Person {
// 构造函数
constructor(name, age) {
this.name = name;
this.age = age;
}
// 实例方法(自动添加到 prototype)
sayHi() {
console.log('Hi, I am ' + this.name);
}
// 静态方法(只能用类名调用)
static create(name) {
return new Person(name, 0);
}
}
const p = new Person('Alice', 25);
p.sayHi(); // Hi, I am Alice
Person.create('Bob'); // 创建新实例
// --- 继承 (extends) ---
class Student extends Person {
constructor(name, age, grade) {
super(name, age); // 调用父类构造函数
this.grade = grade;
}
// 重写父类方法
sayHi() {
super.sayHi(); // 调用父类方法
console.log('I am in grade ' + this.grade);
}
}
const s = new Student('Charlie', 15, 9);
s.sayHi();
// 输出:
// Hi, I am Charlie
// I am in grade 9
总结对比
| 概念 | 说明 |
|---|---|
prototype |
构造函数的属性,用来存放共享方法 |
__proto__ |
实例对象的内部链接,指向构造函数的 prototype |
| 原型链 | 对象通过 __proto__ 层层向上查找的链条 |
class |
ES6 语法糖,让原型继承写法更清晰 |
一句话理解:原型链就是"找不到就问爸爸,爸爸找不到就问爷爷"的继承机制。
Ts 基础语法
在 Vue 3 中,TS 主要用于约束 props、ref、reactive 以及函数的参数,确保代码在编写阶段就能发现错误。
为了导入不报错,做以下配置:(tsconfig.json and tsconfig.app.json)
01 接口
接口用于定义对象的结构(外形)。在 Vue 中常用于定义用户信息、列表数据等模型。
export interface PersonInterface {
id: string;
name: string;
age: number;
gender?: string; // ? 表示可选属性
}
层级嵌套:
export interface GroupInterface {
groupName: string;
members: PersonInterface[]; // 数组中每一项都必须符合 PersonInterface
}
02 自定义类型
type 与 interface 相似,但它更灵活,可以定义联合类型或原始类型的别名。
type Status = 'loading' | 'success' | 'error';
let currentStatus: Status = 'loading'; // 只能从这三个字符串中选
# 自定义类型
type Persons = Array<xxx>
03 泛型
泛型是指在定义函数、接口或类时,不预先指定具体类型,而在使用时再指定类型。它像是一个“类型的占位符”。