feng xiaohan
Node(Express)连接数据库

MySQL

关系型数据库

使用 mysql 的 connection 连接数据库

准备工作跟下一章节相同。

我们可以使用它来进行查询,查询完成之后会关闭连接。

这种方法较为底层,且每次操作数据库都需要新建数据库连接,若数据库操作需求多,对服务器消耗较大,因此,可以使用第二种连接方式。

const mysql = require("mysql");
const connection = mysql.createConnection({
  host: "localhost",
  user: "me",
  password: "secret",
  database: "my_db",
});

connection.connect();

connection.query("SELECT 1 + 1 AS solution", function (err, rows, fields) {
  if (err) throw err;

  console.log("The solution is: ", rows[0].solution);
});

connection.end();

使用 mysql 建立数据库连接池连接数据库

准备工作

  • 需要下载 MySQL 数据库在电脑上;

  • 项目中下载 mysql2 来辅助连接 MySQL 数据库:

    npm install --save mysql2
    

    mysql2 就是 mysql 的升级版。

连接数据库

以连接池的方式连接数据库:

database.js

const mysql = require("mysql2"); // 导入mysql2模块

// 创建一个连接池 @1
const pool = mysql.createPool({
  host: "localhost", // 连接的服务器的IP地址
  user: "root", // 数据库用户
  database: "node-complete", // 连接的具体数据库
  password: "159638", // 安装时填写的密码
});

// 导出连接池
module.exports = pool.promise(); // @2

@1:连接池方式——每当我们运行一个查询语句时能随时访问它,然后从管理的多个连接池中获取一个新连接,这样就可以同时运行多个查询,因为每个查询都需要自己的连接,并且一次查询完成后连接会交还到连接池中用于新的查询,最后在应用程序关闭时完成连接池。

@2:以 Promise 的方式导出,我们在其他地方使用的时候可以处理异步任务。

获取数据

我们要获取数据库表中的数据,需要使用execute()结合一条数据库语句来实现对数据库中表的操作:

app.js

const db = require('./util/database');
...
// 查询数据库中的products表
db.execute('SELECT * FROM products')
    .then(([rows, fieldData]) => {
        console.log(rows); // 存储了数据库表中的数据
        console.log(fieldData); // 存储数据库表的一些元字段
    })
    .catch(err => console.log(err));

// 增加一条数据
db.execute(
    'INSERT INTO products (title, price, imageUrl, description) VALUES (?, ?, ?, ?)',
    [this.title, this.price, this.imageUrl, this.description] // 增加数据字段(与库中的表相对应)
).then(() => {}).catch(err => console.log(err));

使用 sequelize 连接数据库

详情见 sequelize 一章。

MongDB

非关系型数据库。MongoDB 是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似json的 bson格式,因此可以存储比较复杂的数据类型。

特点:

  • 能存储大量的数据;
  • 支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,查询速度块;
  • 而且还支持对数据建立索引;

使用 mongodb 连接数据库

准备工作

下载 mongodb:

npm instal --save mongodb

准备一个 MongoDB 数据库连接地址,我使用的是云数据库 MongoDB Atlas。

连接数据库

database.js

const mongodb = require("mongodb"); // 引入mongodb
const MongoClient = mongodb.MongoClient; // 新建一个MongoClient用于连接数据库

const mongoConnect = (callback) => {
  MongoClient.connect(
    // 连接数据库地址
    "mongodb+srv://<user>:<password>@cluster0.mnle1m2.mongodb.net/?retryWrites=true&w=majority"
  ) // @1
    .then((client) => {
      console.log("Connected!");
      callback(client);
    })
    .catch((err) => {
      console.log(err);
    });
};

module.exports = mongoConnect;

@1:该 url 是云数据库的地址,其中包含了云数据库存储的用户名和密码。

app.js

const mongoConnect = require('./util/database');
const app = express();
...
mongoConnect(client => {
  console.log(client);
  app.listen(3000);
});

更多详细信息见 MongoDB 一章。

使用 mongoose 连接数据库

准备工作

下载 mongoose:

npm install --save mongoose

连接数据库

app.js

const mongoose = require('mongoose'); // 引入mongoose
...
mongoose
  .connect(
    'mongodb+srv://<user>:<password>@cluster0.mnle1m2.mongodb.net/shop?retryWrites=true&w=majority'
  ) // 连接MongoDB数据库
  .then(result => {
    app.listen(3000);
  })
  .catch(err => {
    console.log(err);
  });
Express

安装

npm install --save express

原理

Express.js 所有都是关于中间件的。中间件意味着传入的请求会通过一堆由 Express.js 汇集(封装)的函数。(实际上挂载并拥有多个请求处理程序,而不是一个请求处理程序。请求将一步一步通过这个功能,直到我们发出响应)

image-20230206142003029

中间件允许我们将代码拆分为多个块或片段,而不是一个包含许多功能的巨大函数。这就是 Express.js 的可插拔性(pluggable),我们可以在其中轻松添加其他第三方包(添加中间件)

使用

下载完后引入 Express.js 的模块,并创建一个由 Express.js 管理的对象:

const express = require("express");

const app = express();

在我们把它传递给创建的服务器之前,我们可以通过 Express.js 使用 app 并调用定义的方法——use

添加中间件

use()允许我们添加一个新的中间件,它接收一个必填的函数,所有传入的请求都会执行它;这个函数接收三个参数:

  • request:请求对象

  • response:响应对象

  • next:Express.js 提供的内部函数,前往下一个中间件

    如果不调用next,就不会前往下一个中间件。

app.use((req, res, next) => {
  console.log("我是中间件");
  next(); // 前往下一个中间件
});

注意:中间件是根据代码的先后顺序,从上到下执行的。

不同路由

use()的第一个参数是可选的路径字符串,通过它来区分不同的路由:

app.use("/add-product", (req, res, next) => {
  console.log("我是中间件");
  res.send('<h1>The "Add Product" Page</h1>');
});

app.use("/", (req, res, next) => {
  console.log("我是另一个中间件");
  res.send("<h1>Hello from Express!</h1>");
});

可以通过调整中间件的位置,控制路由先后或是否执行:

app.use('/', (req, res, next) => {
    console.log('总是要通过我才能进入下一个');
    next();
})
...

设置响应标头

默认响应标头是text/html

发送响应

使用res.send()发送响应:

app.use((req, res, next) => {
  res.send("<h1>Hello from Express!</h1>");
});

使用res.sendFile()发送响应 HTML 文件(路径需path模块):

app.use((req, res, next) => {
  res.sendFile(path.join("../", "views", "shop.html"));
});

监听端口

app.listen(3000);

Express.js 会帮我们完成(背后原理):

const http = require("http");
const server = http.createServer(app);
server.listen(3000);

解析请求体

通过第三方包中间件(body-parser)来解析,比使用 Node.js 本身的 Buffer 的存储机制来解析要简单得多:

const bodyParser = require('body-parser');
...
app.use(bodyParser.urlencoded({extended: false}));

启动静态文件(CSS/JS/…)

静态文件的启动需要借助express.static()来访问,例如静态文件存存储在根目录下public/css/main.css中:

app.use(express.static(path.join(__dirname, "public")));

express.static 会寻找在...(根目录)/public下的所有地址文件。

在需要使用的 HTML 中:

<link rel="stylesheet" href="/css/main.css" />

例子

const express = require("express");
const bodyParser = require("body-parser");

const app = express();

app.use(bodyParser.urlencoded({ extended: false }));

// app.use('/', (req, res, next) => {
//     console.log('总是要通过我');
//     next();
// })

app.use("/add-product", (req, res, next) => {
  res.send(
    '<form action="/product" method="POST"><input type="text" name="title"><button type="submit">Add Product</button></form>'
  );
});

app.post("/product", (req, res, next) => {
  console.log(req.body);
  res.redirect("/");
});

app.use("/", (req, res, next) => {
  res.send("<h1>Hello from Express!</h1>");
});

app.listen(3000);

附录

app

use()

注册一个中间件。总是会按顺序执行的中间件,所有传入的请求都会执行它(无论请求是 GET 还是 POST)。

用途:

  • 添加中间件

  • 不同路由

  • 添加多个回调函数(中间件)

    在注册中间件时可以添加多个回调函数,不同回调函数之间用逗号隔开:

    app.use("/add-product", callback1, callback2);
    

get()/post()…

只会在传入 GET/POST/…请求(request)时触发

listen()

监听端口。

set()

允许我们设置任何全局变量的值。

const app = express();

app.set("view engine", "pug"); // 设置模板引擎

注意:因为 Pug 下载后是内置的,所以可以使用 set 直接引用。

engine()

注册一个非内置的模板引擎。

app.engine("handlebars", expressHba());

res

响应相关信息。

send()

发送响应。默认响应标头是text/html

res.send("<h1>Hello from Express!</h1>");

sendFile()

传入一个需响应的文件地址,发送响应文件。需借助 path(Node.js)模块。

res.sendFile(path.join("../", "views", "shop.html"));

redirect()

路由重定向。

res.redirect("/");

render()

发送默认模板引擎渲染的文件。

res.render("shop"); // 发送渲染shop.pug文件

它可以使用第二个参数,向模板文件中传递参数:

const products = "apple";
res.render("shop", { prods: products, docTitle: "Shop" }); // 发送到shop.pug的参数

locals

设置局部变量传递给视图。

res.locals.xxx =

例子:

app.use((res, req, next) => {
  res.locals.isAuthenticated = req.session.isLoggedIn;
  res.locals.csrfToken = req.csrfToken();
  next();
});
Express.js模块化路由(Router)

基本使用

使用 express.Router 类来创建可安装的模块化路由处理程序。Router 实例是完整的中间件和路由系统;因此,常常将其称为“微型应用程序”(像是在服务器内部嵌套一个小型的服务器)。

router 的用法和const app = express()创建的 app 是一样的。

routes/oneRoute.js:

const express = require("express");

const router = express.Router(); // 创建Router实例

router.use("/add-product", (req, res, next) => {
  res.send(
    '<form action="/product" method="POST"><input type="text" name="title"><button type="submit">Add Product</button></form>'
  );
});

router.post("/product", (req, res, next) => {
  console.log(req.body);
  res.redirect("/");
});

module.exports = router; // 导出router

在某个文件中导入:

const oneRoute = require("./routes/oneRoute");

app.use(oneRoute);

添加公共路径路由(过滤路径)

有时候我们的模块化路由里中间件的路由地址有一段是相同,可以在注册这个路由模块时通过use来定义:

...
app.use('/admin', adminRoutes); // 公共路径
...

routes/oneRoute.js:

// /admin/add-product => GET
router.get("/add-product", (req, res, next) => {
  res.send(
    '<form action="/admin/product" method="POST"><input type="text" name="title"><button type="submit">Add Product</button></form>'
  );
});

// /admin/product => POST
router.post("/product", (req, res, next) => {
  console.log(req.body);
  res.redirect("/");
});
Express.js中间件的包
  • body-parser:解析传入的请求体(request body)

    npm install --save body-parser
    

    注意:body-parser 实际上是默认包含在 Express.js 中的,但是 Express.js 很可能在以后又删除,所以我们可以使用第三方包来确定这个功能的稳定性。

    const bodyParser = require('body-parser');
    ...
    app.use(bodyParser.urlencoded({extended: false})); // 使用urlencoded解析器解析传入请求体,要求传入的数据为文本数据
    
  • csurf:允许我们生成 CSRF Token

    此包依赖 express 和 express-session。

    npm install --save csurf
    
  • express-validator:Express 验证器

    npm install --save express-validator
    
uni-app 和 Vue

迁移对比

uni-app 与 Vue 相对比。

js

  • 一些在 js 中的事件直接作为标签的属性来设置:
    • 按下样式
    • 事件冒泡
    • 手指按住和松开状态时间

html

uni-app 与 html 的标签对应。

  • view:div
  • text:span
  • ul/li:view v-for
  • img:image
  • a:navigator
  • iframe:web-view
  • select:picker
  • table:uni-table

其他没提到的大致相同。

css

  • 一些在 css 需要设置定义的属性可直接通过对应容器和标签属性来开启:scroll-view

组件

视图容器

  • view:div
  • scroll-view:区域滚动。
  • swiper:滑块区域。类似实现轮播图。

表单组件

  • button:原生组件就跟用了组件库的效果差不多,还可以设置大小,样式。
  • input:输入框,同 html 差不多,但是还可以指定设备弹出键盘。
  • switch:开关。

路由(页面跳转)

  • navigator:可设置是否可以返回上一个页面(open-type)。

媒体组件

  • image:通过 mode 属性来设置图像相对容器的适应(缩放、裁剪等)。

生命周期

页面级别

  • onLoad:页面进入后执行(只执行一次)。

    默认参数接收 url 参数,代替 vue-router。

  • onShow(keep-alive):进入页面时触发,离开页面再回来也能触发。

    广告植入(讨厌)。

  • onReady(onMounted)(只执行一次)。

  • onHide:离开页面时触发。

    清除生命周期钩子等。

  • onUnload:需要配置 open-type=reLaunch

    卸载页面是进行数据缓存的清理。

  • onPageScroll:监听页面滚动。

  • onPullDownRefresh:监听用户下拉动作。

    下拉刷新。

  • onReachBottom:页面滚动到底部的事件(不是 scroll-view 滚到底)。

    用于下拉到下一页数据。

应用级别

  • onLaunch:第一次进入应用执行。

    验证本地缓存是否有登录信息。

  • onShow:应用级别。(全局)

  • onHide:应用级别。(全局)

生命周期顺序

应用 -> 页面

不包含组件

onLoad -> onShow ->onReady

包含组件

onLoad -> onShow -> setup -> beforeMount -> onReady -> mounted

onReady 和 mounted 文档是 onReady 在后面,但实际测试确实在前面执行?

uni-app 的生命周期虽然支持与 Vue 混用,但是最好还是只用一个。

尺寸单位

rpx

微信小程序提出的一种用于响应式布局的单位,可以根据屏幕自适应的动态单位。(以 750 像素为基准)

一般来说 UI 稿子等比缩放宽以 750 为基准。

pages.json

该文件可以:

  • 定义路由。对于每个路由页面也可也设置单独的窗口页面表现。pages
  • 设置默认窗口页面表现(导航栏等)。globalStyle
  • 设置自动引入组件规则(默认开启)。easycom
  • 设置页面底部切换按钮栏。tabBar
  • 模拟直达页面场景(仅限开发,用于测试)。condition

manifest.json

进行一些应用的配置。

  • 如果想上传应用,或者进行真机调试,需要定义好 AppId。

    这个 AppId 需要进行小程序注册才可以获得

小结

注意:针对不同的平台可能某些 API 或者事件等不可使用,具体需要查阅官方标准。

  • 有些标签的名字有变化:div(view)、span(text)

  • 一些在 js 中的事件直接作为标签的属性来设置;

  • 一些之前需要引用组件库或者手写的样式,直接被容器集成了;并且可以在容器上直接设置属性来设置无需使用 js

  • 使用路由跳转时可设置 open-type 模式来控制跳转后上一个页面的行为(是否可以回跳);

  • Vue 的一些语法和事件可以参照官方对照表将其一一对应起来;

  • uni-app 有对应的 js/vue 事件,也有自己原生的事件

  • uni-app 中的组件不需要手动导入,只需要放在 components 文件夹下即可;

  • uni-app 中的生命周期除了继承 Vue 的组件生命周期,还有应用级别和页面级别的生命周期;

  • 可以在 uni.scss 中定义一些全局的 css 变量;也可以指定小程序特有的环境变量(例如 env(safe-area-inset-bottom))来设置其独有的样式属性

    如果想定义局部的变量可以在 common 文件夹中定义好后,在 uni.scss 中使用@import导入,重启生效。

  • 内置有 API 可以完成交互反馈(uni.showToast 等),类似于全局封装好的组件;

  • 发送请求也可以使用 uni.request 完成,自身支持 Promise,不需要额外使用 axios 等网络请求库

  • 可直接配置跨域;

    微信小程序:在本地设置配置不检验 https,上线时在小程序的后台里配置服务器域名即可。

  • 官方有扩展组件可直接安装使用(官方组件库);

    安装好之后会出现在 uni_modules 底下。

  • 对于不同端的适配,可以使用#ifdef#ifndef#endif来根据设备控制不同的 dom 元素渲染

    例如对于小程序来说可以配置联系客服,就可以直接到达客服的微信;而对于 pc 端,可以展示电话号码。

  • 改变小程序内置图标样式也需添加一个类名,再深度选择即可

  • 针对不同的设备,uni-app 会提供 api 来获取当前设备的信息,我们可以根据里面的设备型号做处理;

    uni.getSystemInfoSync、uni.getSystemInfo。

  • 一些样式需要和胶囊按钮对齐,可以用 api 获取胶囊按钮的位置信息,做动态的自适应

    uni.getMenuButtonBoundingClientRect。一般将其抽离出来在 utils 下的 system.ts 中。

  • 路由传参直接拼在 url 上,再在组件内的生命周期的默认参数中获取

    navigator;load。

  • uni-app 中同样也可以将数据放入缓存中;

    uni.setStorage。

  • 调用小程序 api 分享时,可以通过 url 传参到达指定的分享页面

  • 对于富文本的渲染uni-app 有专门的插件,还可以渲染 dom 元素

Hello World

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

$ hexo new "My New Post"

More info: Writing

Run server

$ hexo server

More info: Server

Generate static files

$ hexo generate

More info: Generating

Deploy to remote sites

$ hexo deploy

More info: Deployment

Web Worker

浏览器为 JS 提供的多线程技术,允许在后台运行脚本,避免阻塞主线程(UI 线程),从而提升复杂任务的性能和用户体验。

特点

  • 独立线程:运行在浏览器后台线程,与主线程隔离。两者之间无法直接操作。
  • 通信:主线程和独立线程都可通过 postMessageonmessage 与主线程交换数据。

    注意:数据传递时是被复制的,而不是共享。

  • 兼容性:现代浏览器普遍支持(IE10+)。
  • 错误处理:通过 worker.onerror 捕获 Worker 错误。
  • 资源释放:用 worker.terminate() 手动关闭 Worker。

限制

  • 不能访问 domwindow 对象

    Worker 有自己的线程对象 DedicatedWorkerGlobalScope

  • 同源限制:Worker 脚本需与主线程同源。
  • 不能使用 alert()confirm() 方法

    但是可以使用 AJAX 。

  • 无法读取本地文件:不能直接打开 file:// 协议的资源。

核心原理

浏览器环境本身是多线程的(如网络请求、定时器、渲染引擎等由浏览器管理),当我们创建一个 worker 时,浏览器就会为其分配一个独立的线程与主线程并行执行。该线程由浏览器底层管理,操作系统可能进一步分配 CPU 核心资源。

浏览器的多线程能力基于操作系统(如 Windows 的线程 API、Linux 的 pthread)或引擎(如 V8 的 Isolate 机制)实现。

执行顺序

主线程和 Worker 线程各自运行在独立的线程中,二者的代码执行顺序取决于浏览器的线程调度,我们无法直接控制。通过 postMessage 发送的消息会进入接收线程的消息队列,按事件循环机制处理。

小结

经测试,Web Worker 自身拥有自己的事件循环,主线程和 Worker 中的通信都是在对应触发 postMessage 才会运行接收的,相当于挂载监听器事件;而且两者的微任务和宏任务都是谁先执行完毕谁输出,但是主线程的微任务总是先于 worker 中的微任务,主线程中的宏任务和 worker 的微任务、宏任务执行的顺序就要看浏览器调度了

基本使用

创建

cosnt myWorker = new Worker(jsUrl, options);
  • jsUrl:必须是 js 脚本,并且地址遵守同源策略。
  • options:配置对象。可以用于指定 Worker 的名称。

通信

onmessagepostMessage

加载脚本

importScripts。可动态加载多个脚本。

importScripts("script1.js", "script2.js");

错误处理

onerror

关闭 Worker

  • 主线程关闭:worker.terminate();
  • Worker 内部关闭:self.close();

SharedArrayBuffer

Web Worker 在进行消息传递的时候数据会被复制,而不是共享,这对于大数据量或频繁通信的情况来说可能效率不高。

SharedArrayBuffer 是 JavaScript 中的一个对象,用于在多个线程(如主线程和 Web Workers)之间共享同一块内存区域(固定长度的原始二进制数据缓冲区)。它允许不同线程直接读写同一块内存,从而实现高效的数据共享,避免了通过 postMessage 复制数据的性能开销。

特性

  • 共享内存
  • 固定长度:创建时需指定缓冲区大小,不可动态调整。
  • 类型化视图:通过 TypedArray(如 Int32Array)或 DataView 访问和操作内存。
  • 线程安全依赖:需配合 Atomics 方法实现原子操作,避免竞争条件。
CDN

content delivery network。内容分发网络。
用于优化网络资源请求的时间。

原理

核心原理就是将用户所需要的内容缓存在就近的服务器中。通过中心平台的调度、负载均衡、内容分发等功能模块,使用户就近获取所需资源。
我们所请求的资源最终都是存放在一个服务器里,这个服务器的存放的地址一般是固定的,如果我们在很远的地方访问这个服务器资源,其访问速度就可能变得很慢,而 cdn 就是在各个地区放了服务器,当你访问资源时会查找离你最近的 cdn 专用服务器,然后将资源返回。如果没有找到则一层一层网上找,直到找到源服务器,将源服务器中所需要的资源再一层一层缓存下来,最后最近的服务器将资源缓存下来之后返回给你。

这个过程是再配置 dns 解析的时候,会将 cdn 专用服务器的地址配置按远近配置进去。

使用

基于 CDN 就近原则,可将所有静态资源全部部署到 CDN 服务器里。可降低网络拥塞,提高用户访问响应速度和命中率。

好处

  • 可以做异地容灾;
  • 负载均衡;在 CDN 中,负载均衡又分为服务器负载均衡和服务器整体负载均衡。如果一个地区的服务器流量访问较高,他会向旁边就近的一些服务器分一些流量。
grammar

基本语法

Walrus 操作符(海象操作符)

:=

赋值表达式。在 Python 3.8 中引入的新特性。这个操作符允许你在表达式中进行赋值,而不仅仅是语句中。

if (t := sum ^ value) in cnt:
    ans += cnt[t] * key - total[t]

切片操作

用于截取子字符串,子列表也可可以,只要数据类型为 list

test = 'hello world

# 截取前 7 个字符:hello w
substring = test[:7]
substring = test[0:7]

# 截取第 1 个到第 5 个字符(不包含):ello
substring = test[1:5]

# 截取后 3 个字符:rld
substring = test[-3:]

# 从第 1 个到第 6 个字符,步长为3(每隔 2 个取一个):eo
substring = test[1:6:3]
substring = test[::3] # 对整个字符串每隔步长为 3 进行取
substring = test[::-1] # 反转字符串

# 截取到第 1 个空格:hello
substring = test[:test.find(' ')]

nonlocal

用于在嵌套函数中声明一个变量不是局部变量。如果不申明的话,就会将同名变量当作嵌套作用域内部自己的局部变量。

def wrappedFunc() -> None:
    ans = 0
    def insideFunc() -> None:
        nonlocal ans # 声明为非局部变量
        ans += 1

内置方法

join()

用于将一个字符串列表连接成一个单一的字符串。

separator.join(iterable)

count()

获取一个字符串中某个字符的数量。

print('1jjjjj0'.count('j')) # 5

sort()

原地排序。默认从小到大。

arr = [1,4,6,2,4,9]
people = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]

# 根据第一项从大到小排序后根据第二项从小到大
people.sort(key = lambda x : (-x[0], x[1])) # [[7, 0], [7, 1], [6, 1], [5, 0], [5, 2], [4, 4]]

split

拆分字符串。

text = "hello world"
words = text.split() # 默认以空格为分隔符 ['hello', 'world']

items()

用于返回字典(dict、Counter)的键值对列表。

Counter 继承于字典。

arr = [1,1,1,2,2,3]
cnt = Counter(arr)
for c in cnt.items():
  print(c)

二进制操作

bit()

返回一个数字的二进制。

但是在 python 中该二进制的数字是以 0b 开头,所以需要进行拆分。

bit(3)[2:] # 11

bit_count()

返回一个数字的二进制形式的 1 的数量。

库函数

Counter()

计数器。专门用于计数元素的频率,默认值是一个整形(0),是一个特殊的字典类。

可以实现类似 js map 的数据结构。

defaultdict(list)

创建一个字典类,当你访问一个不存在的键时,它会自动返回一个默认值(这个是空列表)。

可以实现类似 js map 的数据结构。

idx = {} # {} 被认为是一个空的字典

enumerate()

迭代函数。

对于遍历的数组来说,可以遍历获取它的索引和值。第二个参数可以控制遍历的开始的索引值。

for i, h in enumerate(hours, 1):
  print(i) # 1...

accumulate()

累加函数。

可遍历累加后的值。

sum()

累加函数。

累加遍历的值。

arr = [1, 2, 3, 4, 5]
total1 = sum(arr)
total2 = sum(-(-x // 2) for x in arr)

遍历累加符合的值(找到符合的值将其映射为 1)

max_count = sum(1 for v in cnt.values() if v == max_exec)

max()

遍历元素获取最大值。

  • 获取二维数组中的最大值:
# intervals = [[1, 8], [2, 3], [3, 4]]
len = max(max(x) for x in intervals)

list()

动态数组类型,它可以存储任何类型的元素,并且支持动态扩展。

range(start, end, step)

创建一个整数序列,从 start 到 end,步长为 step。

ord()

获取一个字符对应的 Unicode 编码。

all()

用于检测一个可迭代对象(如列表、元组、集合等)中的所有元素是否都为 True 或者是否都满足某个条件。

# all(iterable)
if all(i > 0 for i in [1, 2, 3]):

zip()

将多个可迭代对象(如列表、元组等)中的元素按位置配对,返回一个迭代器。如果可迭代对象的长度不一致,则返回的迭代器长度与最短的可迭代对象相同。

# 将两个列表的元素配对
list1 = [1, 2, 3]
list2 = ['a', 'b', 'c']
zipped = zip(list1, list2)

# 将zip对象转换为列表
zipped_list = list(zipped)
print(zipped_list)  # [(1, 'a'), (2, 'b'), (3, 'c')]

# 直接遍历 zip 对象
for x, y in zipped:
  print(x, y)

# 解码 zip (in place)
a, b = zip(*zipped)
print(a, b) # (1, 2, 3) ('a', 'b', 'c')
print(list(zipped)) # []

set()

将可迭代对象转换为集合。

s = set([2, 3, 3]) # {2, 3}

s1 = s - {2} # {3}

print(s) # {2, 3}

注意:python 可以使用-{}直接减去 set 中的某个值,并且会生成一个新的 set,而不会修改原 set!!!

哇哦!真是方便呢(js 没有,叽叽叽)

permutations

对迭代器进行排列组合。

nums = [1, 2, 3]
for p in permutations(nums):
  print(p)

heapq

使用 heapq 库创建一个小根堆,如果想用大根堆可以将里面的数值变为负数然后再处理。

heapify

初始化堆。

heap = [4,5,7,2,3,9]
heapq.heapify(heap)

heappop

弹出堆顶。

heappush

入堆顶。

heapreplace

弹出堆的最小元素,并将给定的新元素插入到堆中。这个操作是一个原子操作,即在单次操作中完成弹出和插入的过程。

新元素也会根据按照堆进行排序。

itertools

为高效循环创建迭代器的函数。

chain.from_iterable

可用于扁平化数组。

函数定义

函数定义的语法:

使用def来定义一个函数。

  • self 是类方法中的第一个参数,它代表类的实例本身。具体来说,在类的方法定义中,self 参数是用来访问类的属性和其他方法的。
class Solution:
    def backspaceCompare(self, s: str, t: str) -> bool:
        self.getStr('ert')
    def getStr(self, s: str):
        print(self)

比较

=

python 中的数组其实是一个 list(列表),它是可以直接进行比较的。== 操作符可以用来比较两个列表是否相等,它会逐元素地比较两个列表的内容和顺序是否完全相同。如果两个列表的内容和顺序都相同,则返回 True;否则返回 False。

js 中是不能直接进行数组比较的,数组是引用数据类型,比较的永远是内存地址,永远是 false。

time

该模块提供了各种与时间相关的函数。

此模块中定义的大多数函数的实现都是调用其所在平台的 C 语言库的同名函数。因为这些函数的语义可能因平台而异,所以使用时最好查阅对应平台的相关文档。

下面是一些术语和惯例的解释.

time()

返回以浮点数表示的从 epoch 开始的秒数形式的时间。

epoch 是起始的时间点,即 time.gmtime(0) 的返回值。 这在所有平台上都是 1970-01-01, 00:00:00 (UTC)。

匿名函数(lambda)

lambda arguments: expression
# 定义一个加法的 lambda 函数
add = lambda x, y: x + y
print(add(2, 3))  # 输出: 5

points = [(2, 3), (1, 2), (4, 1)]
# 根据 y 坐标排序
sorted_points = sorted(points, key=lambda p: p[1])
print(sorted_points)

bisect

bisect 是 Python 内置的二分查找模块,用于在已排序的列表中查找元素。

bisect_left

查找元素在列表中的插入位置,如果元素在列表中,则返回该元素左边插入的索引。

arr = [1, 2, 5, 9, 12]
print(bisect.bisect_left(arr, 3)) # 2
print(bisect.bisect_left(arr, 2)) # 1

可以相当于找的是大于等于 target 的第一个元素的索引值。

bisect_right

查找元素在列表中的插入位置,如果元素在列表中,则返回该元素右边插入的索引。

arr = [1, 2, 5, 9, 12]
print(bisect.bisect_right(arr, 3)) # 2
print(bisect.bisect_right(arr, 2)) # 2

可以相当于找的是大于 target 的第一个元素的索引值。

自定义函数(key)

使用二分时可以通过自定义目标值(目标值可以是任何类型)和函数来控制二分的结果和方向。

def check(size: int) -> bool:
    if size < 11:
        return True
    return False
ans = bisect_left(range(20), True, key = check) # 找到 key=True 的值后往左二分,如果找不到则一直往右二分
ans2 = bisect_right(range(20), True, key = check) # 找到 key=True 的值后往右二分,如果找不到则一直往左二分

操作符技巧

//

做除法,向下取整。

# 向下取整
print(5 // 2) # 2

# 向上取整
print(-(-5 // 2)) # 3

math

lcm

找到两个数的最小公倍数。

num = math.lcm(2, 3) # 6

gcd

找到两个数的最大公约数。

num = math.gcd(2, 3) # 1

isqrt

返回一个整数,它是给定整数的平方根。如果给定整数不是平方数,则返回一个小于给定整数的平方数的整数。

num = math.isqrt(9) # 3
num2 = math.isqrt(8) # 2
Promise

注意不是实例上的方法。

  • any():传入一个 promise 实例 数组,返回第一个 fulfiiled promise 的值,如果都 rejected,则会在 catch 中捕获到 All promises were rejected,并且包含一个 reject 函数传递值得数组。
  • all():传入一个 promise 实例 数组,返回一个 fulfiiled promise 的值的数组,如果有一个 rejected,则会在 catch 中捕获第一个 reject 函数传递值得数组。

    webpack 的动态组件引入是借助的Promise.all()

  • allSettled():传入一个 promise 实例 数组,返回所有 promise 的值的数组,无论是 fulfilled 还是 rejected
  • race():传入一个 promise 实例 数组,返回第一个被处理的 promise 的值,无论 fulfilled 还是 rejected

2024 新特性。

  • withResolvers():解构 promise,可以让我们在外面使用 resolve、reject 函数。
({ promise, resolve, reject } = Promise.withResolvers());

火狐暂不支持。

  • try():可以简化在 promise 中 resolve 一个函数的写法,该函数也需要返回值、抛出异常或者返回一个 promise。
function test() {
  return 1;
} // 可异步

Promise.try(test).then((res) => {
  console.log(res);
});