Fork me on GitHub

程序员必备开发工具

程序员必备开发工具

程序员在日常开发工作中,经常需要使用到各种集成开发环境(IDE)和其他辅助工具。以下是一些广泛使用的IDE和开发工具:

集成开发环境(IDE)

  1. IntelliJ IDEA
    • 由JetBrains开发,支持Java、Kotlin、Scala等多种语言,提供智能代码辅助、强大的重构工具和丰富的插件生态系统。
  2. Eclipse
    • 开源IDE,支持Java、C/C++、PHP等多种语言,具有强大的插件系统,可以根据需要进行定制。
  3. Visual Studio
    • 微软开发,支持C#、Visual Basic、JavaScript等语言,提供集成的数据库工具、Web开发工具和强大的调试功能。
  4. PyCharm
    • 同样由JetBrains开发,专门为Python设计,提供代码自动完成、项目管理、版本控制和科学工具等功能。
  5. Visual Studio Code(VS Code):
    • 微软开发,免费、开源,支持多种语言,通过扩展市场提供丰富的插件,轻量级但功能强大。
  6. Sublime Text
    • 轻量级的文本编辑器,支持多种编程语言,通过插件系统可以扩展功能,适合快速编写代码和文本。
  7. NetBeans
    • 支持Java、PHP、C/C++等语言,提供集成的版本控制和数据库工具,界面友好,易于上手。
  8. RStudio
    • 专为R语言设计的IDE,提供代码编辑、调试、可视化和项目管理等功能。
  9. HBuilderX
    • 由 DCloud 公司开发,是一款轻量级的前端开发工具,特别适合前端开发者和小程序开发者。
  10. **Embarcadero Dev-C++**:
  • 一款 C/C++ 集成开发环境,提供代码编辑、调试和项目管理功能。
  1. GoLand
    • 由 JetBrains 开发,专为 Go 语言设计的跨平台 IDE。内置支持 Go 模块管理和丰富的插件生态系统,如数据库工具、Web 开发和版本控制。

版本控制系统

  1. Git

    • 分布式版本控制系统,广泛用于代码的版本管理和团队协作。
  2. Subversion(SVN):

    • 集中式版本控制系统,适用于需要集中管理代码的场景。

数据库管理工具

  1. phpMyAdmin

    • 基于Web的MySQL数据库管理工具,提供用户友好的界面来管理数据库。
  2. Navicat

    • 一款强大的数据库开发工具,支持多种数据库,如 MySQL、PostgreSQL、SQLite、Oracle 等。
  3. DataGrip

    • 由 JetBrains 开发的数据库管理工具,支持多种数据库系统,提供智能查询编辑器和数据库版本控制。

其他工具

  1. Docker
    • 应用容器引擎,用于打包应用及其依赖到一个可移植的容器中。

接口测试化工具

  1. Apifox
  • 一款 API 设计、开发、测试、文档和模拟的全能工具,支持接口自动化测试和数据 Mock。
  1. Postman
  • API开发的辅助工具,用于测试和文档化RESTful API。

虚拟机工具

  1. Oracle VM VirtualBox

    • 由 Oracle 提供的开源虚拟化软件,可以在一台机器上创建和管理多个虚拟机。
  2. VMware

    • 提供虚拟化解决方案,包括桌面虚拟化、服务器虚拟化和云计算管理。

现有工具补充

  1. Android Studio

    • 官方的 Android 开发 IDE,基于 IntelliJ IDEA,提供丰富的 Android 应用开发工具和插件。
  2. CLion

    • 由 JetBrains 开发的 C 和 C++ 跨平台 IDE,提供智能代码分析、重构和调试功能。

选择合适的IDE和工具可以显著提高开发效率和代码质量。不同的项目和开发需求可能需要不同的工具组合,因此程序员通常会根据具体的工作环境和个人偏好来选择最适合自己的工具。

Spring Boot+Vue前后端分离项目如何部署到服务器

Spring Boot+Vue前后端分离项目如何部署到服务器

部署Spring Boot+Vue前后端分离项目到服务器涉及以下步骤:首先,将Spring Boot后端应用打包成jar或war文件并上传至服务器,通过运行java -jar命令启动后端服务。接着,构建Vue前端项目生成静态文件,同样上传至服务器的Web服务器目录,如Nginx的html目录。最后,配置Nginx作为反向代理,将前端请求代理至Vue应用目录,同时确保后端API的路径正确映射。这样,前后端分离的项目就成功部署在服务器上。
部署Spring Boot和Vue项目到服务器之前,需要确保服务器具备以下环境和工具:

环境准备

通用准备:

  1. 服务器

    • 一台具有稳定网络连接的服务器,可以是云服务器或本地服务器。
    • 服务器操作系统,常见的有Linux发行版(如Ubuntu、CentOS)或Windows Server。
  2. SSH访问(对于Linux服务器):

    • SSH客户端,如xshell。
      在这里插入图片描述
  3. 文件传输工具

    • FTP客户端,如xftp。
    • 或者SCP工具,如WinSCP(Windows)或终端(macOS、Linux)。
      在这里插入图片描述

数据库准备

  1. MySQL环境
    • 安装MySQL5.7MySQL8.0
    • 可以通过运行sudo systemctl status mysqld来检查MySQL是否正在运行(MySQL需要配置远程访问)。
  1. CentOS上安装MySQL 5.7和MySQL 8.0教程
  • 837阅读 · 21点赞 · 13收藏

在这里插入图片描述

  1. MySQL远程访问工具
    • 用于构建远程访问MySQL工具,如Navicat
      在这里插入图片描述

Spring Boot部署环境:

  1. Java环境

    • 安装Java Development Kit (JDK),至少需要1.8版本。
    • 可以通过运行java -versionjavac -version来检查Java是否已安装。
    1. CentOS上安装JDK的详细教程
    • 739阅读 · 12点赞 · 13收藏

在这里插入图片描述

  1. Maven或Gradle(如果需要本地构建):
    • 用于构建Spring Boot项目的构建工具。
    • 可以通过运行mvn -versiongradle -v来检查是否已安装。

Vue.js部署环境:

  1. Node.js和npm

    • 安装Node.js,它附带npm包管理器。
    • 可以通过运行node -vnpm -v来检查是否已安装。
  2. Web服务器

    • Nginx或Apache用于提供静态文件服务。
    • 对于Nginx,可以通过运行nginx -v来检查是否已安装。
    • 对于Apache,可以通过运行apache2 -vhttpd -v来检查是否已安装。
  1. CentOS上安装与配置Nginx
  • 683阅读 · 9点赞 · 6收藏
  1. 手把手教你CentOS下载Nginx配置使用
  • 464阅读 · 2点赞 · 3收藏

在这里插入图片描述

可选环境:

  1. 数据库

    • 如果Spring Boot应用需要数据库,需要安装相应的数据库服务器,如MySQL、PostgreSQL等。
    • 确保数据库服务已启动,并且应用有正确的数据库连接配置。
  2. 反向代理/负载均衡

    • 如果有多个应用实例或需要SSL终端,可能需要配置Nginx或HAProxy作为反向代理或负载均衡器。
  3. 容器化工具(如Docker):

    • 如果使用Docker,需要在服务器上安装Docker和Docker Compose。
  4. 自动化部署工具(如Jenkins、GitLab CI/CD):

    • 如果需要自动化部署流程,可能需要安装和配置CI/CD工具。

确保在部署之前,服务器上的环境和工具都已经正确安装和配置,这样可以避免在部署过程中遇到不必要的问题。

运行sql文件创建数据库

  1. 远程连接服务器上的数据库:使用Navicat连接数据库。

  2. 运行项目相关的SQL文件:运行SQL

Spring Boot项目部署到服务器

  1. 打包应用
    使用Maven或Gradle将Spring Boot项目打包成可执行的jar或war文件。如果使用Maven,可以在pom.xml中添加spring-boot-maven-plugin插件,并运行mvn clean package命令来打包。
    在这里插入图片描述

  2. 上传到服务器
    使用FTP、SCP或其他文件传输方法将打包好的jar或war文件上传到服务器的适当目录中。

  3. 运行应用(如果是jar包):
    在服务器上,使用以下命令在后台运行jar文件:

    1
    nohup java -jar yourapp.jar > output.log 2>&1 &

    这将把应用的输出重定向到output.log文件中,并在后台运行。

  4. 配置服务(可选):
    为了确保应用可以在系统启动时自动运行,并能在出现问题时自动重启,可以创建一个Systemd服务文件。

  5. 开放端口
    确保服务器的防火墙设置允许外部访问Spring Boot应用的端口。

Vue项目部署到服务器

  1. 构建项目
    在本地环境中运行npm run build命令,这将会在Vue项目的dist/目录下生成构建后的文件。
    在这里插入图片描述

  2. 上传文件
    dist/目录下的所有文件上传到服务器的Web服务器目录中,例如Nginx或Apache的htmlpublic目录。
    在这里插入图片描述

  3. 配置Web服务器
    配置Web服务器以提供静态文件服务。如果是Nginx,你需要编辑nginx.conf或相应的配置文件,设置正确的根目录,并可能需要配置SSL(如果你使用的是HTTPS)。

  4. 重启Web服务器
    为了使配置生效,重启Web服务器。
    先查询nginx服务运行的进程:ps -ef|grep nginx
    杀死所查询到的nginx服务:kill -9 [ 查到的进程]
    重启服务:cd /nginx安装的sbin目录
    ./nginx

  5. 访问应用
    在Web浏览器中输入服务器的IP地址或域名,你应该能够看到你的Vue应用。

综合步骤

对于同时包含前端Vue项目和后端Spring Boot项目的全栈应用,通常的做法是:

  • 将Spring Boot应用打包并部署到服务器,作为后端API服务。
  • 将Vue应用构建并上传到同一服务器的Web服务器目录中,或者配置反向代理(如Nginx)将前端请求代理到Vue应用的服务器。

确保在部署过程中,Vue应用的publicPathbase配置正确指向后端API的URL,以确保前后端能够正确地通信。

以上步骤是部署Spring Boot和Vue项目到服务器的基本流程。具体的命令和配置可能会根据你的项目和服务器环境有所不同。

如果对你有帮助,点赞、收藏、关注是我更新的动力!👋🌟🚀

Spring Cloud原理详解

Spring Cloud原理详解

Spring Cloud是什么?

Spring Cloud 是一个基于Spring Boot实现的云应用开发工具,它为基于JVM的云应用开发中涉及的配置管理、服务发现、断路器、智能路由、微代理、事件总线、全局锁、决策竞选、分布式会话等操作提供了一种简单的开发方式。

Spring Cloud官网
在这里插入图片描述

Spring Boot是什么

Spring Boot是一个开源的Java基础框架,旨在简化Spring应用的创建和开发过程。它是基于Spring框架的一个模块,提供了一种快速、便捷的方式来开发基于Spring的应用程序。Spring Boot的主要目标是减少配置和部署的复杂性,使得开发者可以轻松地创建独立的、生产级别的基于Spring的应用程序。

核心特性

  1. 自动配置:Spring Boot通过自动配置机制减少了显式配置的需求。它能够根据项目中的jar依赖自动配置Spring和第三方库。

  2. 独立运行:Spring Boot应用程序可以打包成一个独立的JAR文件,这个JAR文件包含了所有必要的依赖项,可以直接运行,无需部署到外部的应用服务器。

  3. 内嵌服务器:Spring Boot内嵌了Tomcat、Jetty或Undertow等Servlet容器,无需额外安装Web服务器即可运行Web应用。

  4. 生产就绪:Spring Boot提供了各种生产级别的特性,如健康检查、度量信息收集、外部化配置等,以便于监控和管理应用程序。

  5. 无代码生成和XML配置:Spring Boot不需要生成大量的样板代码,也不需要复杂的XML配置文件,大多数情况下使用注解和一些属性文件即可完成配置。

  6. 微服务支持:Spring Boot与Spring Cloud紧密集成,支持构建微服务架构的应用,提供了服务发现、配置管理、负载均衡等微服务相关的功能。

优势

  • 快速开发:Spring Boot的约定优于配置的原则使得开发者可以快速搭建和运行Spring应用程序。
  • 简化部署:独立的JAR文件使得部署变得简单,可以直接通过Java命令运行,也可以打包为Docker容器。
  • 社区支持:Spring Boot由一个活跃的社区支持,有大量的文档、教程和第三方库可供选择。
  • 灵活性:虽然Spring Boot提供了默认配置,但它同样支持高度定制化,满足复杂应用的需求。

使用场景

Spring Boot适用于各种类型的Java应用程序,包括Web应用、RESTful API、微服务、数据访问应用等。它特别适合于快速原型开发和中小型项目,但也可以用于大型企业级应用的开发。

Spring Cloud发展历程

Spring Cloud的发展史是与Spring框架的演进紧密相连的。

2003年 - Spring框架的诞生

Spring框架最初由Rod Johnson创建,并在2003年发布。它旨在简化Java EE开发,通过提供全面的编程和配置模型来解决企业级应用开发的复杂性。

2013年 - Spring Boot的发布

随着时间的推移,Spring生态系统变得越来越庞大,配置也变得越来越复杂。为了简化Spring应用的创建和部署,Pivotal团队在2013年推出了Spring Boot,它通过提供默认配置来简化Spring应用的开发。

2014年 - Spring Cloud的初步形成

随着微服务架构的流行,开发者需要一套工具来帮助他们在云环境中构建和管理分布式系统。Spring Cloud在2014年左右开始形成,它提供了一系列框架来支持微服务架构模式。

2015年 - Spring Cloud的稳定发展

Spring Cloud发布了多个子项目,如Spring Cloud Config、Spring Cloud Netflix Eureka、Spring Cloud Hystrix等,这些项目帮助开发者解决了配置管理、服务发现、断路器等问题。

2016年 - Spring Cloud的成熟

Spring Cloud继续扩展,引入了更多的项目,如Spring Cloud Stream、Spring Cloud Function等,同时,Spring Cloud Netflix项目也得到了进一步的发展和完善。

2017年 - 服务网格的探索

随着服务网格架构的兴起,Spring Cloud开始探索如何将服务网格技术与Spring Cloud集成,以便更好地支持微服务的监控、安全和流量控制。

2018年 - Spring Cloud的整合与演进

Spring Cloud发布了Spring Cloud Data Flow,用于简化数据驱动的微服务应用的开发。同时,Spring Cloud也整合了Spring Security,提供了更全面的安全解决方案。

2019年 - 向Serverless的转变

随着云原生技术的发展,Spring Cloud开始支持Serverless架构,发布了Spring Cloud Function for Kubernetes,使得开发者可以在Kubernetes上以无服务器(Serverless)的方式运行Spring Boot函数。

2020年至今 - 持续创新与适应

Spring Cloud持续创新,适应新的技术和趋势,如增强对Spring Native的支持,使得Spring应用可以编译为本地二进制文件,提高启动速度和运行效率。同时,Spring Cloud也继续扩展其对云原生和微服务的支持。

Spring Cloud的发展史是不断适应新的技术挑战和市场需求的历史。随着云计算和微服务架构的不断演进,Spring Cloud也在不断地更新和扩展其项目,以帮助开发者构建更加灵活、可扩展和可维护的云原生应用。

Spring Cloud核心组件

  1. Spring Cloud Config

    • 用于配置管理,支持集中化外部配置。可以存储配置信息并在运行时动态更新,支持不同环境(开发、测试、生产)的配置信息。
  2. Spring Cloud Netflix Eureka

    • 服务注册与发现机制,提供服务注册中心,允许微服务应用进行注册和发现。Eureka Client用于服务的注册与注销,Eureka Server用于服务的注册信息管理和服务发现。
  3. Spring Cloud Netflix Ribbon

    • 客户端负载均衡器,提供基于HTTP和TCP的客户端负载均衡算法。Ribbon与Eureka结合使用,可以实现服务调用时的负载均衡。
  4. Spring Cloud Netflix Hystrix

    • 断路器模式的实现,防止服务雪崩效应。当某个服务出现故障时,Hystrix可以快速失败或提供备选响应,而不是长时间的等待或重试。
  5. Spring Cloud Zuul

    • 动态路由,用于API网关服务,提供请求路由、过滤和安全控制。Zuul可以与Ribbon和Eureka结合,实现请求的负载均衡和服务发现。
  6. Spring Cloud Bus

    • 事件、消息总线,用于传播服务间的事件和状态变化。结合Spring Cloud Config可以实现配置的动态刷新。
  7. Spring Cloud Security

    • 安全管理,提供认证和授权的支持。可以集成OAuth2、LDAP等安全机制,保护微服务的安全性。
  8. Spring Cloud Stream

    • 消息驱动的微服务框架,用于构建基于消息的异步系统。支持与外部消息中间件(如RabbitMQ、Kafka)的集成。
  9. Spring Cloud Sleuth

    • 分布式跟踪,支持跨服务的请求链路追踪。通常与Zipkin或ELK Stack结合使用,以监控和分析微服务的调用链。

Spring Cloud工作原理

  1. 服务注册与发现

    • 微服务启动时,向Eureka Server注册自己的信息。
    • 服务之间通过Eureka Server查询其他服务的实例列表和地址。
  2. 客户端负载均衡

    • Ribbon客户端通过Eureka获取服务实例列表,并根据负载均衡策略选择一个实例进行调用。
  3. 断路器

    • Hystrix监控服务调用的成功率和响应时间,当失败率过高或响应时间过长时,会自动切换到断路状态,避免进一步的调用。
  4. API网关

    • Zuul作为API网关,统一处理所有服务的请求,提供路由、过滤和安全控制。
  5. 配置管理

    • 应用启动时从Config Server获取配置信息。
    • 当配置信息发生变化时,通过Bus广播给所有订阅的服务,实现配置的动态更新。
  6. 消息驱动

    • 应用通过Stream框架发布和消费消息,实现服务间的异步通信。
  7. 分布式跟踪

    • Sleuth生成和传播跟踪ID,通过Zipkin实现跨服务调用的链路追踪。

Spring Cloud通过这些组件和机制,简化了微服务架构下的应用开发、部署和运维。开发者可以利用Spring Cloud快速构建出弹性、可扩展和可维护的云原生应用。

如果对你有帮助,点赞、收藏、关注是我更新的动力!👋🌟🚀

爬虫基本原理介绍、实现及问题解决、爬虫实战、爬取经典moba游戏英雄列表

爬虫基本原理介绍、实现及问题解决、爬虫实战、爬取经典moba游戏英雄列表

爬虫(Web Crawler)是一种自动化的网络机器人,其基本功能是从互联网上抓取信息。爬虫的工作原理、实现方法以及可能遇到的问题及其解决方案如下:

基本原理

  1. 请求(Request):爬虫向目标网站发送HTTP请求,获取网页内容。
  2. 响应(Response):服务器处理请求后,返回网页的HTML、JSON或其他格式的响应数据。
  3. 解析(Parsing):爬虫解析响应内容,提取有用信息或链接。
  4. 存储(Storage):将提取的数据存储到数据库或文件中。
  5. 遍历(Traversal):根据解析出的链接,递归或迭代地发起新的请求,继续抓取其他网页。

实现方法

  1. 选择合适的工具:Python是实现爬虫的流行语言,常用的库有Requests、BeautifulSoup、lxml和Scrapy。
  2. 编写爬虫代码
    • 使用requests库发起请求获取网页内容。
    • 使用BeautifulSouplxml解析HTML,提取数据。
    • 使用正则表达式或XPath选择器定位所需信息。
    • 遍历页面中的链接,构建待抓取的URL队列。
  3. 遵守robots.txt协议:robots.txt文件定义了搜索引擎爬虫可以抓取的网站范围,合法的爬虫应遵守该协议。
  4. 设置User-Agent:模拟浏览器的User-Agent,避免被识别为爬虫而被封禁。
  5. 异常处理:编写代码处理可能出现的异常,如网络请求失败、解析错误等。

问题解决

  1. 反爬虫机制
    • IP封禁:使用代理服务器池,轮换IP地址。
    • 请求频率限制:设置合理的请求间隔,避免过快请求。
    • 验证码:使用OCR技术或第三方服务识别验证码。
    • 动态内容:分析Ajax请求,模拟浏览器行为或使用Selenium获取动态加载的数据。
  2. 数据清洗:对抓取的数据进行清洗和格式化,以便后续处理和分析。
  3. 性能优化
    • 使用多线程或异步IO提高爬取效率。
    • 合理设计数据存储结构,提高读写效率。
  4. 法律风险:尊重版权和隐私,遵守相关法律法规,避免侵权行为。

爬虫的开发和使用需要遵循法律法规和道德标准,不得侵犯他人权益。在实际应用中,应尽量减少对目标网站的影响,合理合法地使用爬虫技术。

Python爬虫简单实现

以下是一个简单的Python爬虫脚本,它使用requests库从百度网站获取HTML内容,并将其保存到本地文件中。我已经为代码添加了注释,以便更好地理解每一步的过程。

1
2
3
4
5
6
7
8
9
import requests

response = requests.get('https://www.baidu.com')
html = response.content.decode()
print(html)

with open('data/json/baidu.html', 'w') as fp:
fp.write(html)
print('保存完毕!')

在运行上述脚本时,需要注意以下几点:

  1. 确保requests库已经安装在你的Python环境中。如果没有安装,可以通过运行pip install requests来安装。
  2. 脚本中的data/json/baidu.html是保存文件的路径和文件名,你需要根据你的文件系统结构来修改这个路径,确保程序有权限写入到该路径。
  3. 如果你运行脚本时遇到网络问题,如无法访问百度网站,可能需要检查你的网络连接,或者检查是否有代理、VPN等网络设置影响了请求。
  4. 由于网络请求可能受到目标网站的反爬虫策略影响,建议在实际使用中添加适当的请求头、User-Agent等信息,并遵守目标网站的robots.txt文件规定。

数据清洗

BeautifulSoup 是一个用于解析HTML和XML文档的Python库,它创建了一个解析树,使得我们可以方便地提取和操作页面数据。在数据清洗过程中,BeautifulSoup 可以帮助我们从网页中提取有用的信息,并将其转换为统一的格式。以下是使用 BeautifulSoup 进行数据清洗的基本步骤:

安装BeautifulSoup

如果你还没有安装 BeautifulSoup,可以通过以下命令进行安装:

1
pip install beautifulsoup4

解析文档

首先,你需要一个HTML或XML文档作为输入。这可以是通过HTTP请求获取的网页内容,也可以是本地文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from bs4 import BeautifulSoup

# 假设我们已经有了HTML内容,可以直接解析
html_content = """
<html>
<head><title>页面标题</title></head>
<body>
<div class="content">
<p class="date">2023-03-17</p>
<ul id="list">
<li class="item">第一项</li>
<li class="item">第二项</li>
</ul>
</div>
</body>
</html>
"""

# 使用BeautifulSoup解析HTML
soup = BeautifulSoup(html_content, 'html.parser') # 'html.parser'是解析器,也可以使用'lxml'等

提取数据

使用 BeautifulSoup 提供的方法,如 find(), find_all(), select() 等,来定位和提取所需的元素。

1
2
3
4
5
6
7
# 提取单个元素
date_tag = soup.find('p', class_='date')
date = date_tag.text.strip() # 去除空白字符

# 提取多个元素
items = soup.find_all('li', class_='item')
item_texts = [item.text.strip() for item in items] # 列表推导式提取所有文本内容

数据清洗

对提取出的数据进行清洗,包括去除空白字符、标准化格式、转换数据类型等。

1
2
3
4
5
6
7
8
9
10
# 去除空白字符
cleaned_date = date.replace('\n', '').replace(' ', '')

# 标准化格式,例如日期格式
standard_date = cleaned_date.replace('-', '')

# 转换数据类型,例如将字符串转换为整数或浮点数
# 假设我们有一个价格字符串
price_str = '¥29.99'
price = float(price_str.replace('¥', '')) # 去除货币符号并转换为浮点数

存储数据

将清洗后的数据存储到适当的数据结构中,如列表、字典、数据帧等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 将清洗后的数据存储到字典中
data = {
'date': standard_date,
'items': item_texts
}

# 或者存储到列表中
data_list = [
{'date': standard_date, 'items': item_texts}
]

# 如果需要,可以将数据保存到文件或数据库中
import json
with open('data.json', 'w', encoding='utf-8') as f:
json.dump(data_list, f, ensure_ascii=False, indent=4)

以上步骤展示了如何使用 BeautifulSoup 进行基本的数据清洗。在实际应用中,你可能需要根据具体的网页结构和数据需求,编写更复杂的解析和清洗逻辑。此外,BeautifulSoup 也提供了许多其他功能,如处理编码问题、解析嵌套标签等,可以根据需要进行学习和使用。

爬取经典moba游戏英雄列表

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import requests
from pyquery import PyQuery

url = 'https://pvp.qq.com/web201605/herolist.shtml'
html = requests.get(url).content
# print(html)

doc = PyQuery(html)

items = doc('.herolist > li').items()
# print(items)
# 生成迭代对象

# 循环遍历
for item in items:
# print(item)
url = item.find('img').attr('src')
# print(url)
urls = 'http:' + url
name = item.find('a').text()
# print(name)
url_content = requests.get(urls).content
# 下载 w write b bytes 二进制写入
with open('data/herolist/' + name + '.jpg', 'wb') as file:
# 保存
file.write(url_content)
print('正在下载:%s---------%s' % (name, urls))

print('下载完毕')

Ajax技术包含Fetch和Axios

Ajax技术

一. 初识前后端交互

传统网站的问题:

  • 为了获取数据,需要重新加载,浪费资源,增加等待时间,性能不好
  • 验证表单过程中,一项内容不合格,页面需要重新加载,体验不好

解决问题:

  • ajax 全名 async javascript and XML
  • 是前后台交互的能力
  • 也就是我们客户端给服务端发送消息的工具,以及接受响应的工具
  • 是一个 默认异步 执行机制的功能

AJAX 的优势

  1. 不需要插件的支持,原生 js 就可以使用
  2. 用户体验好(不需要刷新页面就可以更新数据)
  3. 减轻服务端和带宽的负担
  4. 缺点: 搜索引擎的支持度不够,因为数据都不在页面上,搜索引擎搜索不到

二. 原生Ajax

1. AJAX 基础

  • 在 js 中有内置的构造函数来创建 ajax 对象
  • 创建 ajax 对象以后,我们就使用 ajax 对象的方法去发送请求和接受响应

创建一个 ajax 对象

1
2
3
4
5
// IE9及以上
const xhr = new XMLHttpRequest()

// IE9以下
const xhr = new ActiveXObject('Mricosoft.XMLHTTP')
  • 上面就是有了一个 ajax 对象
  • 我们就可以使用这个 xhr 对象来发送 ajax 请求了

配置链接信息

1
2
3
4
5
6
7
8
const xhr = new XMLHttpRequest()

// xhr 对象中的 open 方法是来配置请求信息的
// 第一个参数是本次请求的请求方式 get / post / put / ...
// 第二个参数是本次请求的 url
// 第三个参数是本次请求是否异步,默认 true 表示异步,false 表示同步
// xhr.open('请求方式', '请求地址', 是否异步)
xhr.open('get', './data.php')
  • 上面的代码执行完毕以后,本次请求的基本配置信息就写完了

发送请求

1
2
3
4
5
const xhr = new XMLHttpRequest()
xhr.open('get', './data.php')

// 使用 xhr 对象中的 send 方法来发送请求
xhr.send()
  • 上面代码是把配置好信息的 ajax 对象发送到服务端

一个基本的 ajax 请求

  • 一个最基本的 ajax 请求就是上面三步
  • 但是光有上面的三个步骤,我们确实能把请求发送的到服务端
  • 如果服务端正常的话,响应也能回到客户端
  • 但是我们拿不到响应
  • 如果想拿到响应,我们有两个前提条件
    1. 本次 HTTP 请求是成功的,也就是我们之前说的 http 状态码为 200 ~ 299
    2. ajax 对象也有自己的状态码,用来表示本次 ajax 请求中各个阶段

ajax 状态码

  • ajax 状态码 - xhr.readyState
  • 是用来表示一个 ajax 请求的全部过程中的某一个状态
    • readyState === 0: 表示未初始化完成,也就是 open 方法还没有执行
    • readyState === 1: 表示配置信息已经完成,也就是执行完 open 之后
    • readyState === 2: 表示 send 方法已经执行完成
    • readyState === 3: 表示正在解析响应内容
    • readyState === 4: 表示响应内容已经解析完毕,可以在客户端使用了
  • 这个时候我们就会发现,当一个 ajax 请求的全部过程中,只有当 readyState === 4 的时候,我们才可以正常使用服务端给我们的数据
  • 所以,配合 http 状态码为 200 ~ 299
    • 一个 ajax 对象中有一个成员叫做 xhr.status
    • 这个成员就是记录本次请求的 http 状态码的
  • 两个条件都满足的时候,才是本次请求正常完成

readyStateChange

  • 在 ajax 对象中有一个事件,叫做 readyStateChange 事件

  • 这个事件是专门用来监听 ajax 对象的 readyState 值改变的的行为

  • 也就是说只要 readyState 的值发生变化了,那么就会触发该事件

  • 所以我们就在这个事件中来监听 ajax 的 readyState 是不是到 4 了

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    const xhr = new XMLHttpRequest()
    xhr.open('get', './data.php')

    xhr.send()

    xhr.onreadyStateChange = function () {
    // 每次 readyState 改变的时候都会触发该事件
    // 我们就在这里判断 readyState 的值是不是到 4
    // 并且 http 的状态码是不是 200 ~ 299
    if (xhr.readyState === 4 && /^2\d{2}$/.test(xhr.status)) {
    // 这里表示验证通过
    // 我们就可以获取服务端给我们响应的内容了
    }
    }

responseText

  • ajax 对象中的 responseText 成员

  • 就是用来记录服务端给我们的响应体内容的

  • 所以我们就用这个成员来获取响应体内容就可以

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    const xhr = new XMLHttpRequest()
    xhr.open('get', './data.php')

    xhr.send()

    xhr.onreadyStateChange = function () {
    if (xhr.readyState === 4 && /^2\d{2}$/.test(xhr.status)) {
    // 我们在这里直接打印 xhr.responseText 来查看服务端给我们返回的内容
    console.log(xhr.responseText)
    }
    }

2. 使用 ajax 发送请求时携带参数

  • 我们使用 ajax 发送请求也是可以携带参数的
  • 参数就是和后台交互的时候给他的一些信息
  • 但是携带参数 get 和 post 两个方式还是有区别的

发送一个带有参数的 get 请求

  • get 请求的参数就直接在 url 后面进行拼接就可以

    1
    2
    3
    4
    5
    6
    const xhr = new XMLHttpRequest()
    // 直接在地址后面加一个 ?,然后以 key=value 的形式传递
    // 两个数据之间以 & 分割
    xhr.open('get', './data.php?a=100&b=200')

    xhr.send()
    • 这样服务端就能接受到两个参数
    • 一个是 a,值是 100
    • 一个是 b,值是 200

发送一个带有参数的 post 请求

  • post 请求的参数是携带在请求体中的,所以不需要再 url 后面拼接

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const xhr = new XMLHttpRequest()
    xhr.open('get', './data.php')

    // 如果是用 ajax 对象发送 post 请求,必须要先设置一下请求头中的 content-type
    // 告诉一下服务端我给你的是一个什么样子的数据格式
    xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')

    // 请求体直接再 send 的时候写在 () 里面就行
    // 不需要问号,直接就是 'key=value&key=value' 的形式
    xhr.send('a=100&b=200')
    • application/x-www-form-urlencoded 表示的数据格式就是 key=value&key=value

不同的请求方式

  • get 偏向获取

  • post 偏向提交

  • put 偏向更新

  • patch 偏向修改部分

  • delete 偏向删除信息

  • head 偏向获取服务器头的信息

  • option 偏向获取服务器设备信息

  • connnect 保留请求方式

三. Fetch

*XMLHttpRequest 是一个设计粗糙的 API,配置和调用方式非常混乱, 而且基于事件的异步模型写起来不友好。* 

**兼容性不好 polyfill: https://github.com/camsong/fetch-ie8**

1. 用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
fetch("http://localhost:3000/users")
.then(res=>res.json())
.then(res=>{
console.log(res)
})


fetch("http://localhost:3000/users",{
method:"POST",
headers:{
"content-type":"application/json"
},
body:JSON.stringify({
username:"kerwin",
password:"123"
})
})
.then(res=>res.json())
.then(res=>{
console.log(res)
})

fetch("http://localhost:3000/users/5",{
method:"PUT",
headers:{
"content-type":"application/json"
},
body:JSON.stringify({
username:"kerwin",
password:"456"
})
})
.then(res=>res.json())
.then(res=>{
console.log(res)
})

fetch("http://localhost:3000/users/5",{
method:"DELETE"
})
.then(res=>res.json())
.then(res=>{
console.log(res)
})

2. 错误处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//
fetch("http://localhost:3000/users1")
.then(res=>{
if(res.ok){
return res.json()
}else{
return Promise.reject({
status:res.status,
statusText:res.statusText
})
}
})
.then(res=>{
console.log(res)
})
.catch(err=>{
console.log(err)
})

四. axios

Axios是一个基于promise 的 HTTP 库,可以用在浏览器和 node.js中。

https://www.npmjs.com/package/axios

1
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>

1. get请求

1
2
3
4
5
6
7
axios.get("http://localhost:3000/users",{
params:{
name:"kerwin"
}
}).then(res=>{
console.log(res.data)
})

2.post请求

1
2
3
4
5
6
axios.post("http://localhost:3000/users",{
name:"kerwin",
age:100
}).then(res=>{
console.log(res.data)
})

3. put请求

1
2
3
4
5
6
axios.put("http://localhost:3000/users/12",{
name:"kerwin111",
age:200
}).then(res=>{
console.log(res.data)
})

4. delete请求

1
2
3
axios.delete("http://localhost:3000/users/11").then(res=>{
console.log(res.data)
})

5. axios(config)配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14

axios({
method: 'post',
url: 'http://localhost:3000/users',
data: {
name: 'kerwin',
age: 100
}
})
.then(res => {
console.log(res.data)
}).catch(err=>{
console.log(err)
})

6. axios拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
axios.interceptors.request.use(function (config) {
// Do something before request is sent
console.log("loading-开始")
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
});

// Add a response interceptor
axios.interceptors.response.use(function (response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data

console.log("loading-结束")
return response;
}, function (error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
console.log("loading---结束")
return Promise.reject(error);
});

7. axios 中断器

1
2
3
4
5
6
7
8
9
10
const controller = new AbortController();

axios.get('/foo/bar', {
signal: controller.signal
}).then(function(response) {
//...
});
// cancel the request
controller.abort()

五. 同源策略(Same-origin policy)

一个 URL 有三部分组成:协议、域名(指向主机)、端口,只有这三个完全相同的 URL 才能称之为同源。如下,能和 http://www.example.com/dir1/index.html 同源的是?

URL 结果 原因
http://www.example.com/dir2/api 同源 只有路径不同
https://www.example.com/api 不同源 协议不同
http://www.example.com:81/dir1/etc.html 不同源 端口不同 ( http:// 默认端口是80)
http://www.kerwin.com/dir1/other.html 不同源 域名不同

(1) 无法读取非同源网页的 Cookie、LocalStorage 。
(2) 无法接触非同源网页的 DOM。
(3) 无法向非同源地址发送 AJAX 请求(可以发送,但浏览器会拒绝接受响应)。

注意:

同源策略是浏览器的行为,是为了保护本地数据不被JavaScript代码获取回来的数据污染,因此拦截的是客户端发出的请求回来的数据接收,即请求发送了,服务器响应了,但是无法被浏览器接收。

六. jsonp

Jsonp(JSON with Padding) 是 json 的一种”使用模式”,可以让网页从别的域名(网站)那获取资料,即跨域读取数据。

为什么我们从不同的域(网站)访问数据需要一个特殊的技术( JSONP )呢?这是因为同源策略。

1
2
3
const script = document.createElement('script')
script.src = './kerwin.txt'
document.body.appendChild(script)

实战

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
mysearch.oninput = function(evt){
console.log(evt.target.value)
if(!evt.target.value){
list.innerHTML = ""
return
}

var oscript = document.createElement("script")
oscript.src = `https://www.baidu.com/sugrec?pre=1&p=3&ie=utf-8&json=1&prod=pc&from=pc_web&sugsid=36542,36464,36673,36454,31660,36692,36166,36695,36697,36570,36074,36655,36345,26350,36469,36314&wd=${evt.target.value}&req=2&csor=1&cb=test&_=1656294200527`
document.body.appendChild(oscript)

oscript.onload = function(){
oscript.remove()
}
}

function test(obj){
console.log(obj.g)

list.innerHTML = obj.g.map(item=>
`<li>${item.q}</li>`
).join("")
}

ES6教程

ES6教程

一.ES6基础

1.初识ES6

ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。

  • 1997年:ECMAScript 1.0

  • 1998年:ECMAScript 2.0

  • 1999年:ECMAScript 3.0

  • 2006年:ECMAScript 4.0 未通过

  • 2009年:ECMAScript 5.0

  • 2015年:ECMAScript 6.0

  • 至今,版本号改用年号的形式。

2.let声明变量与const声明常量
  1. let 不允许重复声明变量

    1
    2
    3
    // 使用 var 的时候重复声明变量是没问题的,只不过就是后面会把前面覆盖掉
    var num = 100
    var num = 200
    1
    2
    3
    // 使用 let 重复声明变量的时候就会报错了
    let num = 100
    let num = 200 // 这里就会报错了
    1
    2
    3
    // 使用 const 重复声明变量的时候就会报错
    const num = 100
    const num = 200 // 这里就会报错了
  2. letconst 声明的变量不会在预解析的时候解析(也就是没有变量提升)

    1
    2
    3
    // 因为预解析(变量提升)的原因,在前面是有这个变量的,只不过没有赋值
    console.log(num) // undefined
    var num = 100
    1
    2
    3
    // 因为 let 不会进行预解析(变量提升),所以直接报错了
    console.log(num)
    let num = 100
    1
    2
    3
    // 因为 const 不会进行预解析(变量提升),所以直接报错了
    console.log(num)
    const num = 100
  3. letconst 声明的变量会被所有代码块限制作用范围

    1
    2
    3
    4
    5
    // var 声明的变量只有函数能限制其作用域,其他的不能限制
    if (true) {
    var num = 100
    }
    console.log(num) // 100
    1
    2
    3
    4
    5
    6
    // let 声明的变量,除了函数可以限制,所有的代码块都可以限制其作用域(if/while/for/...)
    if (true) {
    let num = 100
    console.log(num) // 100
    }
    console.log(num) // 报错
    1
    2
    3
    4
    5
    6
    // const 声明的变量,除了函数可以限制,所有的代码块都可以限制其作用域(if/while/for/...)
    if (true) {
    const num = 100
    console.log(num) // 100
    }
    console.log(num) // 报错
  • letconst 的区别

    1. let 声明的变量的值可以改变,const 声明的变量的值不可以改变

      1
      2
      3
      let num = 100
      num = 200
      console.log(num) // 200
      1
      2
      const num = 100
      num = 200 // 这里就会报错了,因为 const 声明的变量值不可以改变(我们也叫做常量)
    2. let 声明的时候可以不赋值,const 声明的时候必须赋值

      1
      2
      3
      let num
      num = 100
      console.log(num) // 100
      1
      const num // 这里就会报错了,因为 const 声明的时候必须赋值
3.解构赋值
  • 解构赋值,就是快速的从对象或者数组中取出成员的一个语法方式
3-1 解构对象
  • 快速的从对象中获取成员

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // ES5 的方法向得到对象中的成员
    const obj = {
    name: 'kerwin',
    age: 100,
    gender: '男'
    }

    let name = obj.name
    let age = obj.age
    let gender = obj.gender
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 解构赋值的方式从对象中获取成员
    const obj = {
    name: 'kerwin',
    age: 100,
    gender: '男'
    }

    // 前面的 {} 表示我要从 obj 这个对象中获取成员了
    // name age gender 都得是 obj 中有的成员
    // obj 必须是一个对象
    let { name, age, gender } = obj
3-2 解构数组
  • 快速的从数组中获取成员

    1
    2
    3
    4
    5
    // ES5 的方式从数组中获取成员
    const arr = ['kerwin', 'tiechui', 'gangdan']
    let a = arr[0]
    let b = arr[1]
    let c = arr[2]
    1
    2
    3
    4
    5
    6
    7
    // 使用解构赋值的方式从数组中获取成员
    const arr = ['kerwin', 'tiechui', 'gangdan']

    // 前面的 [] 表示要从 arr 这个数组中获取成员了
    // a b c 分别对应这数组中的索引 0 1 2
    // arr 必须是一个数组
    let [a, b, c] = arr
4. 模版字符串
  • ES5 中我们表示字符串的时候使用 '' 或者 ""

  • 在 ES6 中,我们还有一个东西可以表示字符串,就是 **``**(反引号)

    1
    2
    let str = `hello world`
    console.log(typeof str) // string
  • 和单引号好友双引号的区别

    1. 反引号可以换行书写

      1
      2
      3
      4
      5
      6
      // 这个单引号或者双引号不能换行,换行就会报错了
      let str = 'hello world'

      // 下面这个就报错了
      let str2 = 'hello
      world'
      1
      2
      3
      4
      5
      6
      let str = `
      hello
      world
      `

      console.log(str) // 是可以使用的
    2. 反引号可以直接在字符串里面拼接变量

      1
      2
      3
      4
      5
      6
      7
      8
      // ES5 需要字符串拼接变量的时候
      let num = 100
      let str = 'hello' + num + 'world' + num
      console.log(str) // hello100world100

      // 直接写在字符串里面不好使
      let str2 = 'hellonumworldnum'
      console.log(str2) // hellonumworldnum
      1
      2
      3
      4
      // 模版字符串拼接变量
      let num = 100
      let str = `hello${num}world${num}`
      console.log(str) // hello100world100
      • `` 里面的 ${} 就是用来书写变量的位置
5.字符串扩展
5-1 includes函数

判断字符串中是否存在指定字符

1
2
3
4
5
let myname = "kerwin"

console.log(myname.includes("e")) //true
console.log(myname.startsWith("k")) //true
console.log(myname.endsWith("n")) //true
5-2 repeat函数

repeat()方法返回一个新字符串,表示将原字符串重复n次。

1
2
3
4
5
6
7
8
let myname = "kerwin"

console.log(myname.repeat(3)) //kerwinkerwinkerwin

console.log(myname.repeat(0)) //""
console.log(myname.repeat(3.5)) //kerwinkerwinkerwin

console.log(myname.repeat("3"))//kerwinkerwinkerwin
6.数值扩展
6-1 二进制和八进制表示法
1
2
3
4
let count1 = 100
let count2 = 0x100
let count3 = 0o100
let count4 = 0b100
6-2 isFinite与isNaN方法

减少全局性方法,使得语言逐步模块化

1
2
3
4
let num1 = Number.isFinite(100) //true
let num2 = Number.isFinite(100/0) //false
let num3 = Number.isFinite(Infinity) // false
let num4 = Number.isFinite("100") //false
1
2
3
4
let num1 = Number.isNaN(100) // false
let num2 = Number.isNaN(NaN) //true
let num3 = Number.isNaN("kerwin") //false
let num4 = Number.isNaN("100") // false

它们与传统的全局方法isFinite()和isNaN()的区别在于,传统方法先调用Number()将非数值的值转为数值,再进行判断,而这两个新方法只对数值有效,Number.isFinite()对于非数值一律返回false, Number.isNaN()只有对于NaN才返回true,非NaN一律返回false。

6-3 isInteger方法

用来判断一个数值是否为整数。

1
2
3
4
let num1 = Number.isInteger(100) // true
let num2 = Number.isInteger(100.0) //true
let num3 = Number.isInteger("kerwin") //false
let num4 = Number.isInteger("100") // false
6-4 极小常量Number.EPSILON

它表示 1 与大于 1 的最小浮点数之间的差。2.220446049250313e-16

1
2
3
4
5
6
function isEqual(a,b){
return Math.abs(a-b)<Number.EPSILON
}

console.log(isEqual(0.1+0.2,0.3)) //true
console.log(0.1+0.2===0.3) //false
6-5 Math.trunc

将小数部分抹掉,返回一个整数。

1
2
3
4
console.log(Math.trunc(1.2)) //1
console.log(Math.trunc(1.8))// 1
console.log(Math.trunc(-1.8)) //-1
console.log(Math.trunc(-1.2))//-1
6-6 Math.sign

Math.sign方法用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值。

1
2
3
4
5
Math.sign(-100) // -1
Math.sign(100) // +1
Math.sign(0) // +0
Math.sign(-0) // -0
Math.sign("kerwin") // NaN
7.数组扩展
7-1 扩展运算符
1
2
3
4
let arr1 = [1,2,3]
let arr2 = [4,5,6]

console.log([...arr1,...arr2])
7-2 Array.from

将类数组对象转换为真正数组

1
2
3
4
5
6
7
8
function test(){
console.log(Array.from(arguments))
}

test(1,2,3)

let oli = document.querySelectorAll("li")
console.log(Array.from(oli))
7-3 Array.of

将一组值转化为数组,即新建数组

1
2
3
4
5
let arr1 = Array(3)
console.log(arr1)// [,,]

let arr2 = Array.of(3)
console.log(arr2)// [3]
7-4. find方法

1)该方法主要应用于查找第一个符合条件的数组元素

2)它的参数是一个回调函数。在回调函数中可以写你要查找元素的条件,当条件成立为true时,返回该元素。如果没有符合条件的元素,返回值为undefined

1
2
3
4
5
6
7
8
9
let arr = [11,12,13,14,15]
let res1 = arr.find(function(item){
return item>13
})
let res2 = arr.findIndex(function(item){
return item>13
})
console.log(res1) //14
console.log(res2) //3
7-5. fill方法

使用自己想要的参数替换原数组内容,但是会改变原来的数组

1
2
3
4
let arr1 = new Array(3).fill("kerwin")
let arr2 = ['a', 'b', 'c'].fill("kerwin", 1, 2)
console.log(arr1)//['kerwin', 'kerwin', 'kerwin']
console.log(arr2)// ['a', 'kerwin', 'c']
7-6 flat与flatMap方法

按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回

1
2
3
4
5
6
7
8
9
10
var obj = [{
name: "A",
list: ["鞍山", "安庆", "安阳"]
},
{
name: "B",
list: ["北京", "保定", "包头"]
}
]
console.log(obj.flatMap(item => item.list))
8.对象扩展
8-1 对象简写
1
2
3
4
5
6
7
8
9
10
let name ="moduleA"
let obj = {
name,
test1(){

},
test2(){

}
}
8-2 属性名表达式
1
2
3
4
5
6
7
8
9
10
let name ="moduleA"
let obj = {
name,
[name+"test1"](){

},
[name+"test2"](){

}
}
8-3 Object.assign

Object.assign(target, object1,object2)的第一个参数是目标对象,后面可以跟一个或多个源对象作为参数。

target:参数合并后存放的对象

object1:参数1

object2:参数2

1
2
3
4
5
6
7
8
9
10
11
12
13
const obj1 = {
name: "kerwin"
};

const obj2 = {
name:"tiechui"
};
const obj3 = {
age:100
};

Object.assign(obj1, obj2, obj3);
//obj1 {name: 'tiechui', age: 100}
8-4 Object.is

方法判断两个值是否是相同的值

1
2
3
4
5
console.log(NaN===NaN) //false
console.log(+0===-0) //true

console.log(Object.is(NaN,NaN)) //true
console.log(Object.is(+0,-0)) //false
9.函数扩展
9-1 箭头函数
  • 箭头函数是 ES6 里面一个简写函数的语法方式

  • 重点: 箭头函数只能简写函数表达式,不能简写声明式函数

    1
    2
    3
    4
    5
    function fn() {} // 不能简写
    const fun = function () {} // 可以简写
    const obj = {
    fn: function () {} // 可以简写
    }
  • 语法: (函数的行参) => { 函数体内要执行的代码 }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    const fn = function (a, b) {
    console.log(a)
    console.log(b)
    }
    // 可以使用箭头函数写成
    const fun = (a, b) => {
    console.log(a)
    console.log(b)
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    const obj = {
    fn: function (a, b) {
    console.log(a)
    console.log(b)
    }
    }
    // 可以使用箭头函数写成
    const obj2 = {
    fn: (a, b) => {
    console.log(a)
    console.log(b)
    }
    }
9-2 箭头函数的特殊性
  • 箭头函数内部没有 this,箭头函数的 this 是上下文的 this

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // 在箭头函数定义的位置往上数,这一行是可以打印出 this 的
    // 因为这里的 this 是 window
    // 所以箭头函数内部的 this 就是 window
    const obj = {
    fn: function () {
    console.log(this)
    },
    // 这个位置是箭头函数的上一行,但是不能打印出 this
    fun: () => {
    // 箭头函数内部的 this 是书写箭头函数的上一行一个可以打印出 this 的位置
    console.log(this)
    }
    }

    obj.fn()
    obj.fun()
    • 按照我们之前的 this 指向来判断,两个都应该指向 obj
    • 但是 fun 因为是箭头函数,所以 this 不指向 obj,而是指向 fun 的外层,就是 window
  • 箭头函数内部没有 arguments 这个参数集合

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const obj = {
    fn: function () {
    console.log(arguments)
    },
    fun: () => {
    console.log(arguments)
    }
    }
    obj.fn(1, 2, 3) // 会打印一个伪数组 [1, 2, 3]
    obj.fun(1, 2, 3) // 会直接报错
  • 函数的行参只有一个的时候可以不写 () 其余情况必须写

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    const obj = {
    fn: () => {
    console.log('没有参数,必须写小括号')
    },
    fn2: a => {
    console.log('一个行参,可以不写小括号')
    },
    fn3: (a, b) => {
    console.log('两个或两个以上参数,必须写小括号')
    }
    }
  • 函数体只有一行代码的时候,可以不写 {} ,并且会自动 return

    1
    2
    3
    4
    5
    6
    7
    8
    9
    const obj = {
    fn: a => {
    return a + 10
    },
    fun: a => a + 10
    }

    console.log(fn(10)) // 20
    console.log(fun(10)) // 20
9-3 函数传递参数的时候的默认值
  • 我们在定义函数的时候,有的时候需要一个默认值出现

  • 就是当我不传递参数的时候,使用默认值,传递参数了就使用传递的参数

    1
    2
    3
    4
    5
    6
    function fn(a) {
    a = a || 10
    console.log(a)
    }
    fn() // 不传递参数的时候,函数内部的 a 就是 10
    fn(20) // 传递了参数 20 的时候,函数内部的 a 就是 20
    • 在 ES6 中我们可以直接把默认值写在函数的行参位置
    1
    2
    3
    4
    5
    function fn(a = 10) {
    console.log(a)
    }
    fn() // 不传递参数的时候,函数内部的 a 就是 10
    fn(20) // 传递了参数 20 的时候,函数内部的 a 就是 20
    • 这个默认值的方式箭头函数也可以使用
    1
    2
    3
    4
    5
    const fn = (a = 10) => {
    console.log(a)
    }
    fn() // 不传递参数的时候,函数内部的 a 就是 10
    fn(20) // 传递了参数 20 的时候,函数内部的 a 就是 20
    • 注意: 箭头函数如果你需要使用默认值的话,那么一个参数的时候也需要写 ()
10.Symbol

ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它属于 JavaScript 语言的原生数据类型之一,其他数据类型是:undefinednull、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。

  1. 使用Symbol作为对象属性名
1
2
3
4
5
6
let name = Symbol()
let age = Symbol()
var obj ={
[name]:"kerwin",
[age]:100
}
  1. Symbol()函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述。这主要是为了在控制台显示,比较容易区分。
1
2
3
4
5
6
7
let name = Symbol("name")
let age = Symbol("age")
var obj ={
[name]:"kerwin",
[age]:100
}
console.log(obj)
  1. 遍历问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let keys = {
name:Symbol("name"),
age:Symbol("age")
}
var obj ={
[keys.name]:"kerwin",
[keys.age]:100,
a:1,
b:2,
c:3
}

Reflect.ownKeys(obj).forEach(item=>{
console.log(item,obj[item])
})
  1. Symbol.for()可以重新使用同一个 Symbol 值
1
2
3
4
5
6
var obj  ={
[Symbol.for("name")]:"kerwin",
[Symbol.for("age")]:100
}

console.log(obj[Symbol.for("name")])
11.Iterator迭代器

Iterator 的作用有三个:

一是为各种数据结构,提供一个统一的、简便的访问接口;

二是使得数据结构的成员能够按某种次序排列;

三是 ES6 创造了一种新的遍历命令for…of循环,Iterator 接口主要供for…of循环

1
2
3
4
5
let arr = ["kerwin", "tiechui", "gangdaner"]

for(let i of arr){
console.log(i)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Iterator 的遍历过程是这样的。

1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。

2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。

3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。

4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。

let i = arr[Symbol.iterator]()
console.log(i.next())
console.log(i.next())
console.log(i.next())
console.log(i.next())

ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)。Symbol.iterator属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。

原生默认具备 Iterator 接口的数据结构如下。

  • Array
  • Set
  • Map
  • String
  • arguments 对象
  • NodeList 对象

如何对于对象进行for fo遍历?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
let obj = {
0: "kerwin",
1: "tiechui",
2: "gangdaner",
length: 3,
[Symbol.iterator]: Array.prototype[Symbol.iterator]
}

for (let i of obj) {
console.log(i)
}


let obj2 = {
data: ['kerwin', 'tiechui', "gangdaner"],
[Symbol.iterator]() {
// let _this = this
let index = 0;
return {
next: () => {
if (index < this.data.length) {

return {
value: this.data[index++],
done: false
}
} else {
return {
value: undefined,
done: true
}
}
}
}
}
};

for (let i of obj2) {
console.log(i)
}
12.Set结构

它类似于数组,但成员的值都是唯一的,没有重复的值。

12-1 初识Set
1
2
3
4
5
6
7
8
let s1 = new Set([1, 2, 3, 2, 3])
console.log(s1)

let s2 = new Set()
s2.add(1)
s2.add(2)
s2.add(3)
console.log(s2)
12-2 实例的属性和方法
  • size:返回Set实例的成员总数。

  • Set.prototype.add(value):添加某个value。

  • Set.prototype.delete(value):删除某个value,返回一个布尔值,表示删除是否成功。

  • Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。

  • Set.prototype.clear():清除所有成员,没有返回值。

12-3 遍历
  • Set.prototype.keys():返回键名的遍历器
  • Set.prototype.values():返回键值的遍历器
  • Set.prototype.entries():返回键值对的遍历器
  • Set.prototype.forEach():遍历每个成员
12-4 复杂数据结构去重
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function uni(arr) {
let res = new Set()
return arr.filter(item => {
let id = JSON.stringify(item)
if (res.has(id)) {
return false
} else {
res.add(id)
return true
}
})
}

var arr = [1, 2, 3, "data", {
name: "kerwin"
}, {
name: "kerwin"
},
[1, 2],
[3, 4],
[3, 4]
]
console.log(uni(arr))
13.Map结构

类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。

13-1 初识Map
1
2
3
4
5
6
7
8
9
10
11
let m1 = new Map()
m1.set("name","kerwin")
m1.set({a:1},"大连")

console.log(m1)

let m2= new Map([
["name","kerwin"],
[{a:1},"大连"]
])
console.log(m2)
13-2 实例的属性和方法
  • size:返回 Map 结构的成员总数。

  • Map.prototype.set(key,value):添加key对应得value,返回 Map 结构本身。

  • Map.prototype.get(key):获取key对应的value

  • Map.prototype.delete(key):删除某个键(键名+键值)

  • Map.prototype.has(key):某个键是否在当前 Map 对象之中。

  • Map.prototype.clear():清除所有成员,没有返回值。

13-3 遍历
  • Map.prototype.keys():返回键名的遍历器。
  • Map.prototype.values():返回键值的遍历器。
  • Map.prototype.entries():返回所有成员的遍历器。
  • Map.prototype.forEach():遍历 Map 的所有成员。
14.Proxy代理

Proxy如其名, 它的作用是在对象和和对象的属性值之间设置一个代理,获取该对象的值或者设置该对象的值, 以及实例化等等多种操作, 都会被拦截住, 经过这一层我们可以统一处理,我们可以认为它就是“代理器”

14-1.get方法
1
2
3
4
5
6
let target = {}
let proxy = new Proxy(target,{
get(target,prop){
return target[prop]
}
})
14-2.set方法
1
2
3
4
5
6
7
8
9
10
11
12
let target = {}
let proxy = new Proxy(target,{
get(target,prop){
return target[prop]
},
set(target,prop,value){
if(prop==="data"){
box.innerHTML = value
}
target[prop] = value;
}
})
14-3.has方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let target = {
_prop: "内部数据"
}
let proxy = new Proxy(target, {
get(target, prop) {
return target[prop]
},
set(target, prop, value) {
if (prop === "data") {
box.innerHTML = value
}
target[prop] = value;
},
has(target, key) {
if (key[0] === '_') {
return false;
}
return key in target;
}
})
14-4.this问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
let target = new Set()
const proxy = new Proxy(target, {
get(target, key) {
const value = target[key]
// 遇到 Function 都手动绑定一下 this
if (value instanceof Function) {
console.log(`访问${value}方法了`)
return value.bind(target)
//不能 是 call apply
}
return value
}
})
proxy.add(1)

Proxy本质上属于元编程非破坏性数据劫持,在原对象的基础上进行了功能的衍生而又不影响原对象,符合松耦合高内聚的设计理念。

15.Reflect对象

Reflect 可以用于获取目标对象的行为,它与 Object 类似,但是更易读,为操作对象提供了一种更优雅的方式。它的方法与 Proxy 是对应的。

15-1 代替Object的某些方法
1
2
3
4
5
6
7
8
9
const obj = {
};
Reflect.defineProperty(obj, 'name', {
value: 'kerwin',
writable: false,
configurable:false
});


15-2 修改某些Object方法返回结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 老写法
try {
Object.defineProperty(target, property, attributes);
// success
} catch (e) {
// fail
}

// 新写法
if (Reflect.defineProperty(target, property, attributes)) {
// success
} else {
// fail
}

15-3 命令式变为函数行为
1
2
3
4
5
6
7
8
9
10
11
12
const obj = {
name:"kerwin"
};
//老写法
console.log("name" in obj) //true
//新写法
console.log(Reflect.has(obj, 'name')) //true

//老写法
delete obj.name
//新写法
Reflect.deleteProperty(obj, "name")
15-4 配合Proxy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let target = new Set()
const proxy = new Proxy(target, {
get(target, key) {
const value = Reflect.get(target,key)
// 遇到 Function 都手动绑定一下 this
if (value instanceof Function) {
console.log(`访问${value}方法了`)
return value.bind(target)
//不能 是 call apply
}
return value
},
set() {
return Reflect.set(...arguments)
}
})
proxy.add(1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let arr = [1, 2, 3]
let proxy = new Proxy(arr, {
get(target, key) {
console.log('get', key)
return Reflect.get(...arguments)
},
set(target, key, value) {
console.log('set', key, value)
return Reflect.set(...arguments)
}
})
proxy.push(4)
// 能够打印出很多内容
// get push (寻找 proxy.push 方法)
// get length (获取当前的 length)
// set 3 4 (设置 proxy[3] = 4)
// set length 4 (设置 proxy.length = 4)
16.Promise

Promise 是异步编程的一种解决方案,比传统的解决方案回调函数, 更合理和更强大。ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象 。

  • 指定回调函数方式更灵活易懂。

  • 解决异步 回调地狱 的问题。

16-1 回调地狱
  • 当一个回调函数嵌套一个回调函数的时候

  • 就会出现一个嵌套结构

  • 当嵌套的多了就会出现回调地狱的情况

  • 比如我们发送三个 ajax 请求

    • 第一个正常发送
    • 第二个请求需要第一个请求的结果中的某一个值作为参数
    • 第三个请求需要第二个请求的结果中的某一个值作为参数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    ajax({
    url: '我是第一个请求',
    success (res) {
    // 现在发送第二个请求
    ajax({
    url: '我是第二个请求'
    data: { a: res.a, b: res.b },
    success (res2) {
    // 进行第三个请求
    ajax({
    url: '我是第三个请求',
    data: { a: res2.a, b: res2.b },
    success (res3) {
    console.log(res3)
    }
    })
    }
    })
    }
    })
  • 回调地狱,其实就是回调函数嵌套过多导致的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u6H56r6e-1681921901149)(%E7%AC%94%E8%AE%B0.assets/%E5%9B%9E%E8%B0%83%E5%9C%B0%E7%8B%B1.jpeg)]

  • 当代码成为这个结构以后,已经没有维护的可能了
16-2 Promise使用
  • 语法:

    1
    2
    3
    4
    5
    6
    7
    8
    new Promise(function (resolve, reject) {
    // resolve 表示成功的回调
    // reject 表示失败的回调
    }).then(function (res) {
    // 成功的函数
    }).catch(function (err) {
    // 失败的函数
    })
16-3 Promise 对象的状态

Promise 对象通过自身的状态,来控制异步操作。Promise 实例具有三种状态。

1
2
3
异步操作未完成(pending)
异步操作成功(fulfilled)
异步操作失败(rejected)

这三种的状态的变化途径只有两种。

1
2
从“未完成”到“成功”
从“未完成”到“失败”

一旦状态发生变化,就凝固了,不会再有新的状态变化。这也是 Promise 这个名字的由来,它的英语意思是“承诺”,一旦承诺成效,就不得再改变了。这也意味着,Promise 实例的状态变化只可能发生一次。

因此,Promise 的最终结果只有两种。

1
2
异步操作成功,Promise 实例传回一个值(value),状态变为fulfilled。
异步操作失败,Promise 实例抛出一个错误(error),状态变为rejected。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X5z03Bpt-1681921901150)(%E7%AC%94%E8%AE%B0.assets/image-20220902141409899.png)]

16-4 Promise.all

Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

1
const p = Promise.all([p1, p2, p3]);

p的状态由p1,p2,p3 决定,分成两种情况。

(1)只有p1p2p3的状态都变成fulfilledp的状态才会变成fulfilled,此时p1p2p3的返回值组成一个数组,传递给p的回调函数。

(2)只要p1p2p3之中有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

16-5 Promise.race

Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

1
const p = Promise.race([p1, p2, p3]);

上面代码中,只要p1p2p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

17.Generator 函数

Generator 函数是 ES6 提供的一种异步编程解决方案

Generator 函数是一个状态机,封装了多个内部状态。

执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。

17-1 基本语法
1
2
3
4
5
6
7
8
9
10
11
12
function *gen(){
console.log(1)
yield;
console.log(2)
yield;
console.log(3)
}

let g = gen()
g.next()
g.next()
g.next()
image-20220917070351858

yield(产出)表达式是暂停执行的标记,而next方法可以恢复执行。

1
2
3
4
5
6
7
8
9
10
11
12
function *gen(){
yield 1;
yield 2;
}

let g = gen()
let res1 = g.next()
console.log(res1)
let res2 = g.next()
console.log(res2)
let res3 = g.next()
console.log(res3)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UmesXKSH-1681921901151)(%E7%AC%94%E8%AE%B0.assets/image-20220917070836171.png)]

1
2
3
4
5
6
7
8
9
10
11
function *gen(){
let res1 = yield;
console.log(res1)
let res2 = yield;
console.log(res2)
}

let g = gen()
g.next("data-1")
g.next("data-2")
g.next("data-3")

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tOdc6zF1-1681921901151)(%E7%AC%94%E8%AE%B0.assets/image-20220917071219520.png)]

17-2 异步流程

手动版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function *gen(){
let res1 = yield ajax("1.json")
console.log(res1)
let res2 = yield ajax("2.json")
console.log(res2)
}

let g = gen()

g.next().value.then(data=>{
g.next(data).value.then(data=>{
g.next(data)
})
})

自动版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function* gen() {
let res1 = yield ajax("1.json")
console.log(res1)
let res2 = yield ajax("2.json")
console.log(res2)
}


function AutoRun(gen) {
let g = gen();

function next(data) {
let res = g.next(data);
if (res.done) return
res.value.then(function (data) {
next(data);
});
}

next();
}

AutoRun(gen);
18. Class语法
18-1 类的写法
1
2
3
4
5
6
7
8
9
10
11
class Person {
constructor(name,age){
this.name = name;
this.age = age;
}
say(){
console.log(this.name,this.age)
}
}
let obj = new Person("kerwin",100)
console.log(obj)
18-2 getter与setter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class List{
constructor(ele){
this.element = ele
}

get html(){
return this.element.innerHTML
}
set html(arr){
this.element.innerHTML = arr.map(item=>`<li>${item}</li>`).join("")
}
}
let obj = new List(document.querySelector("#list"))

obj.html = ["aaa","bbb","cccc"]
18-3 静态属性和静态方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Person {
static name = "Person这个类"
constructor(name,age){
this.name = name;
this.age = age;
}
say(){
console.log(this.name,this.age)
}

static eat(){
console.log("eat")
}
}
let obj = new Person("kerwin",100)

console.log(Person.name)
Person.eat()
18-4 继承

ES6 规定,子类必须在constructor()方法中调用super(),否则就会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,添加子类自己的实例属性和方法。如果不调用super()方法,子类就得不到自己的this对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Person {
static name = "Person这个类"
constructor(name,age){
this.name = name;
this.age = age;
}
say(){
console.log(this.name,this.age)
}

static eat(){
console.log("eat")
}
}
class Student extends Person{
constructor(name,age,score){
super(name,age)
this.score = score
}
say(){
super.say()
console.log(this.score)
}

static eat(){
super.eat();
console.log("student eat")
}
}
let obj = new Student("kerwin",100,200)
console.log(obj)
obj.say()
Student.eat()
19.模块化

JavaScript 现在有两种模块。一种是 ES6 模块,简称 ESM;另一种是 CommonJS 模块,简称 CJS。

CommonJS 模块是 Node.js 专用的,与 ES6 模块不兼容。语法上面,两者最明显的差异是,CommonJS 模块使用require()module.exports,ES6 模块使用importexport

ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。

写法1:

1
2
3
4
export default A1

import a1 from "./1.js"

写法2:

1
2
3
4
5
6
7
export {A1,A2}

import {A1,A2} from "./1.js"

import {A1 as a1,A2 as a2} from "./1.js"

import * as obj from "./1.js"
1
2
3
4
5
6
7
8
9
10
11
12
13
export function A1(){
console.log("A1")
}
export function A2(){
console.log("A2")
}


import {A1,A2} from "./1.js"

import {A1 as a1,A2 as a2} from "./1.js"

import * as obj from "./1.js"

混合写法:

1
2
3
4
export {A1}
export default A2

import A2,{A1} from "./1.js"

二. ES7新特性

1. 求幂运算符
1
Math.pow(3, 2) === 3 ** 2    // 9
2.数组的includes方法
1
2
3
[1, 2, NaN].includes(NaN)     // true
[1, 2, NaN].indexOf(NaN) // -1

如果仅仅查找数据是否在数组中,建议使用includes,如果是查找数据的索引位置,建议使用indexOf更好一些

三. ES8新特性

1. async和await
1-1.Async

async 函数,使得异步操作变得更加方便。

  • 更好的语义。
  • 返回值是 Promise。
1
2
3
4
async function test(){

}
test()
1-2.Await

await命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。

1
2
3
4
5
6
7
8
9
10
11
async function test(){
var res1 = await ajax("http://localhost:3000/news1")
var res2 = await ajax("http://localhost:3000/news2")
return res2
}

test().then(res=>{
console.log("返回结果",res)
}).catch(err=>{
console.log("err",err)
})
1-3.错误处理
1
2
3
4
5
6
try{
var res1 = await ajax("http://localhost:3000/news1")
var res2 = await ajax("http://localhost:3000/news2")
}catch(err){
console.log("err",err)
}
2.对象方法扩展
1
2
3
4
5
let obj = {
name:"kerwin",
age:100
}
console.log(Object.values(obj))
image-20220920154527417
1
2
3
4
5
let obj = {
name:"kerwin",
age:100
}
console.log(Object.entries(obj))
image-20220920154622530
1
2
3
4
5
let obj = {
name:"kerwin",
age:100
}
console.log(Object.getOwnPropertyDescriptors(obj))
image-20220920155143538

克隆对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
let obj1 = {
name:"Kerwin",
age:100,
location:{
provice:"辽宁",
city:"大连"
},
//只设置city,防止破坏province
get city(){
return this.location.city
},
set city(value){
this.location.city = value
},
set nameset(value){
this.name = value.substring(0,1).toUpperCase()+value.substring(1)
},
get nameset(){
return this.name
}
}
console.log(Object.getOwnPropertyDescriptors(obj1))
var obj2= {}

//Object.assign(obj2,obj1)//无法克隆 get set方法
Object.defineProperties(obj2,Object.getOwnPropertyDescriptors(obj1))

3. 字符串填充

padStart()、padEnd()方法可以使得字符串达到固定长度,有两个参数,字符串目标长度和填充内容。

1
2
3
4
5
6
let str= "kerwin"

console.log(str.padStart(10,"x"));//xxxxkerwin
console.log(str.padEnd(10,"x"));//kerwinxxxx
console.log(str.padStart(5,"x"))//kerwin
console.log(str.padEnd(5,"x"))//kerwin
4. 函数参数的末尾加逗号
1
2
3
4
5
6
7
8
9
10
11
12
function test(
a,
b,
c,
){
console.log(a,b)
}
test(
1,
2,
3,
)

『末尾逗号』在添加新的参数、属性、元素时是有用的,你可以直接新加一行而不必给上一行再补充一个逗号,这样使版本控制工具的修改记录也更加整洁

四. ES9新特性

1. 对象的剩余参数与扩展运算符
1-1 对象的剩余参数
1
2
3
4
5
6
7
8
9
let obj = {
name:"kerwin",
age:100,
location:"dalian"
}

let {name,...other} = obj
console.log(name) //kerwin
console.log(other) //{age: 100, location: 'dalian'}
1-2 对象的扩展运算符
1
2
3
4
5
6
7
8
9
let obj1 = {
name:"kerwin"
}

let obj2 = {
age:100
}

console.log({...obj1,...obj2})
2.正则表达式命名捕获组

JS正则表达式可以返回一个匹配的对象, 一个包含匹配字符串的类数组, 比如: 以 YYYY-MM-DD的格式解析日期,

这样的代码可读性很差, 并且在改变正则表达式的结构的时候很有可能就会改变匹配对象的索引

ES9允许使用命名捕获 ? , 在打开捕获括号后立即命名

1
2
3
4
5
let str = "今天是2022-10-10"
let reg = /([0-9]{4})-([0-9]{2})-([0-9]{2})/g

let res1 = reg.exec(str)
console.log(res1)
image-20220921110518183
1
2
3
4
5
let str = "今天是2022-10-10"
let reg = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/g

let res1 = reg.exec(str)
console.log(res1)
image-20220921110644896
3. Promise.finally()

无论是成功还是失败, 都运行同样的代码, 比如隐藏对话框, 关闭数据连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function ajax(){
return new Promise((resolve,reject)=>{
reject(1111)
})
}
//showloading
ajax().then(res=>{

}).catch(err=>{

}).finally(()=>{
//hideloading
console.log("finally")
})
4. 异步遍历器
4-1 同步遍历器的问题
1
2
3
4
5
6
7
8
9
function* fn() {
yield 1111
yield 2222

}
const syncI = fn();
console.log(syncI.next())
console.log(syncI.next())
console.log(syncI.next())
image-20220921132030973
1
2
3
4
5
6
7
8
function* fn() {
yield new Promise(resolve=>resolve("1111"))
yield new Promise(resolve=>resolve("2222"))

}
const syncI = fn();
syncI.next().value.then(res=>{console.log(res)})
syncI.next().value.then(res=>{console.log(res)})
image-20220921132403676

value属性的返回值是一个 Promise 对象,用来放置异步操作。但是这样写很麻烦,不太符合直觉,语义也比较绕。

4-2 异步遍历器生成函数

Generator 函数返回一个同步遍历器,异步 Generator 函数的作用,是返回一个异步遍历器对象。在语法上,异步 Generator 函数就是async函数与 Generator 函数的结合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
async function* fn() {
yield new Promise(resolve=>resolve("1111"))
yield new Promise(resolve=>resolve("2222"))

}
const asyncI = fn();

asyncI.next().then(res=>{
console.log(res)
return asyncI.next()
}).then(res=>{
console.log(res)
return asyncI.next()
})
.then(res=>{
console.log(res)
})
image-20220921132528997
4-3 for await of

for...of循环用于遍历同步的 Iterator 接口。新引入的for await...of循环,则是用于遍历异步的 Iterator 接口。

1
2
3
4
5
6
async function test() {
for await (let i of asyncI) {
console.log(i)
}
}
test()
4-4 案例改造
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function timer(t) {
return new Promise(resolve => {
setTimeout(() => {
resolve(t)
}, t)
})
}


async function* fn() {
yield timer(1000)//任务1
yield timer(2000)//任务2
yield timer(3000)//任务3
}

// 使用一下 for await ...of
async function fn1() {
for await(const val of fn()) {
console.log("start",Date.now())
console.log(val);
console.log("end",Date.now())
}
}
fn1();
4-5 nodejs用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 传统写法
function main(inputFilePath) {
const readStream = fs.createReadStream(
inputFilePath,
{ encoding: 'utf8', highWaterMark: 1024 }
);
readStream.on('data', (chunk) => {
console.log('>>> '+chunk);
});
readStream.on('end', () => {
console.log('### DONE ###');
});
}

// 异步遍历器写法
async function main(inputFilePath) {
const readStream = fs.createReadStream(
inputFilePath,
{ encoding: 'utf8', highWaterMark: 1024 }
);

for await (const chunk of readStream) {
console.log('>>> '+chunk);
}
console.log('### DONE ###');
}

五.ES10新特性

1. Object.fromEntries

Object.fromEntries()方法允许你轻松地将键值对列表转换为对象

1
2
3
4
5
6
7
const arr = [["name", "kerwin"], ["age", 100]];
console.log(Object.fromEntries(arr))//{name: 'kerwin', age: 100}

const m = new Map()
m.set("name","tiechui")
m.set("age",18)
console.log(Object.fromEntries(m))

用处

1
2
3
4
let str ="name=kerwin&age=100"

let searchParams = new URLSearchParams(str)
console.log(Object.fromEntries(searchParams))//{name: 'kerwin', age: '100'}
2. trimStart() and trimEnd()

trimStart()和trimEnd()方法在实现与trimLeft()和trimRight()相同。

1
2
3
4
5
let str = "   kerwin    "
console.log("|"+str.trimStart(str)+"|")
console.log("|"+str.trimEnd(str)+"|")
console.log("|"+str.trimLeft(str)+"|")
console.log("|"+str.trimRight(str)+"|")
3. Symbol 对象的 description 属性

为Symbol对象添加了只读属性 description ,该对象返回包含Symbol描述的字符串。

1
2
let s = Symbol("kerwin")
console.log(s.description) //kerwin
4. 可选的 catch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let pro1 = new Promise(function (resolve, reject) {
//执行器函数
setTimeout(() => {
resolve("成功的结果")
}, 30000)
})
let pro2 = new Promise(function (resolve, reject) {
//执行器函数
setTimeout(() => {
reject()
}, 2000)
})
async function test() {
try {
await Promise.race([pro1, pro2])
} catch {
console.log("不关心错误结果,网络超时")
}
}
test()

六. ES11新特性

1. Promise.allSettled

Promise.allSettled() 方法返回一个在所有给定的 promise 都已经 fulfilled 或 rejected 后的 promise ,并带有一个对象数组,每个对象表示对应的 promise 结果。

1
2
3
4
5
6
7
8
9
const promises = [ ajax('/200接口'), ajax('/401接口') ];

Promise.allSettled(promises).then(results=>{
// 过滤出成功的请求
results.filter(item =>item.status === 'fulfilled');
过滤出失败的请求
results.filter(item=> item.status === 'rejected');
})

2.module新增
2-1 动态导入 import()

标准用法的 import 导入的模块是静态的,会使所有被导入的模块,在加载时就被编译(无法做到按需编译,降低首页加载速度)。有些场景中,你可能希望根据条件导入模块或者按需导入模块,这时你可以使用动态导入代替静态导入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<body>
<button>login</button>
<script type="module">
let role1 = "管理员"
let role2 = "普通用户"

function login(){
return "普通用户"
}

async function render(role){
if(role===role1){
let res1 = await import("./1.js")
console.log(res1.default)
}else{
let res2 = await import("./2.js")
console.log(res2.default)
}
}

let obtn = document.querySelector("button")
obtn.onclick = function(){
let role = login()
render(role)
}
</script>
</body>

2-2 import.meta

import.meta 会返回一个对象,有一个 url 属性,返回当前模块的url路径,只能在模块内部使用。

1
2
3
4
5
6
7
8
9
10
11
<script type="module">
import obj from './1.js'
</script>


//1.js

console.log(import.meta)
export default {

}
2-3 export * as obj from ‘module’
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//1.js
export default {
name:'111111'
}

export function test1(){

}
//2.js
export default {
name:"22222"
}
export function test2(){

}
export * as obj1 from './1.js'
//html
<script type="module">
import * as obj from './2.js'
console.log(obj)
</script>
image-20220922111416681
3.字符串的matchAll方法

matchAll() 方法返回一个包含所有匹配正则表达式的结果的迭代器。可以使用 for…of 遍历,或者使用 展开运算符(…) 或者 Array.from 转换为数组.

1
2
3
4
5
6
7
8
9
10
11
12
let str = `
<ul>
<li>1111</li>
<li>2222</li>
<li>3333</li>
<li>4444</li>
</ul>
`
let reg = /<li>(.*)<\/li>/g

console.log(str.match(reg))
//'<li>1111</li>', '<li>2222</li>', '<li>3333</li>', '<li>4444</li>'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
let str = `
<ul>
<li>1111</li>
<li>2222</li>
<li>3333</li>
<li>4444</li>
</ul>
`
let reg = /<li>(.*)<\/li>/g
let match = null;
while(match = reg.exec(str)){
console.log(match[0])
console.log(match[1])
}
1
2
3
4
5
6
7
8
9
10
11
12
13
let str = `
<ul>
<li>1111</li>
<li>2222</li>
<li>3333</li>
<li>4444</li>
</ul>
`
let reg = /<li>(.*)<\/li>/g

for(let i of str.matchAll(reg)){
console.log(i)
}
4. BigInt

JavaScript 能够准确表示的整数范围在-2^53到2^53之间(不含两个端点),超过这个范围,无法精确表示这个值,这使得 JavaScript 不适合进行科学和金融方面的精确计算。

1
2
3
4
9007199254740992 //9007199254740992
9007199254740993 //9007199254740992

Math.pow(2,53) === Math.pow(2,53)+1

为了与 Number 类型区别,BigInt 类型的数据必须添加后缀n

1
2
3
4
5
6
1234 // 普通整数
1234n // BigInt

// BigInt 的运算
1n + 2n // 3n

5. globalThis

globalThis 提供了一个标准的方式来获取不同环境下的全局 this 对象(也就是全局对象自身)。不像 window 或者 self这些属性,它确保可以在有无窗口的各种环境下正常工作。所以,你可以安心的使用 globalThis,不必担心它的运行环境。为便于记忆,你只需要记住,全局作用域中的 this 就是 globalThis。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//es6-shim

var getGlobal = function () {

// the only reliable means to get the global object is

// Function('return this')()

// However, this causes CSP violations in Chrome apps.

if (typeof self !== 'undefined') { return self; }

if (typeof window !== 'undefined') { return window; }

if (typeof global !== 'undefined') { return global; }

throw new Error('unable to locate global object');

};

var globals = getGlobal();

if (!globals.Reflect) {

defineProperty(globals, ‘Reflect’, {}, true);

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//以前
var getGlobal = function () {
if (typeof self !== 'undefined') { return self; }

if (typeof window !== 'undefined') { return window; }

if (typeof global !== 'undefined') { return global; }

throw new Error('unable to locate global object');
};

let globals = getGlobal()

if (globals.document) {
console.log("进行dom操作相关")
} else {
console.log("不能进行dom操作")
}

//现在
if (globalThis.document) {
console.log("进行dom操作相关")
} else {
console.log("不能进行dom操作")
}
6.空值合并运算符

空值合并运算符(*??*)是一个逻辑运算符。当左侧操作数为 null 或 undefined 时,其返回右侧的操作数。否则返回左侧的操作数。

1
2
3
4
5
6
7
let obj = {
name:"kerwin",
introduction:0
}

console.log(obj.introduction || "这个人很懒")
console.log(obj.introduction ?? "这个人很懒")

??和 || 的区别是什么呢?

他们两个最大的区别就是 ’ ‘和 0,??的左侧 为 ’ ‘或者为 0 的时候,依然会返回左侧的值;

|| 会对左侧的数据进行boolean类型转换,所以’ ‘和 0 会被转换成false,返回右侧的值

7.可选链操作符

可选链前面的值如果是null或undefined,则不再执行后面的,之前返回可选链前面的值

1
2
3
4
5
6
7
8
9
10
let obj = {
name:"kerwin",
introduction:0,
// location:{
// city:"dalian"
// }
}

console.log(obj && obj.location && obj.location.city)
console.log(obj?.location?.city)

七. ES12新特性

1. 逻辑赋值操作符

逻辑赋值操作符 ??=、&&=、 ||=

1
2
3
4
5
6
7
8
9
10
11
12
13
let a = true
let b = false
//a &&= b //false
a ||= b ; //true
console.log(a)


let obj = {
name:"kerwin",
}

obj.introduction = obj.introduction??"很懒"
obj.introduction??="很懒"
2.数字分隔符

这个新特性是为了方便程序员看代码而出现的,如果数字比较大,那么看起来就不是那么一目了然

1
2
const num= 123456789

分隔符不仅可以分割十进制,也可以分割二净值或者十六净值的数据,非常好用。

1
2
3
const number = 1_000_000_000_000;
const binary = 0b1010_0101_1111_1101;
const hex = 0xA1_B2_C3;
3. replaceAll

所有匹配都会被替代项替换。模式可以是字符串或正则表达式,而替换项可以是字符串或针对每次匹配执行的函数。并返回一个全新的字符串

1
2
3
4
const str =
"I wish to wish the wish you wish to wish, but if you wish the wish the witch wishes, I won't wish the wish you wish to wish. ";
const newStr = str.replaceAll("wish", "kerwin");
console.log(newStr);
4.Promise.any

只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。

Promise.any()Promise.race()方法很像,只有一点不同,就是Promise.any()不会因为某个 Promise 变成rejected状态而结束,必须等到所有参数 Promise 变成rejected状态才会结束。

5. WeakRef

在一般情况下,对象的引用是强引用的,这意味着只要持有对象的引用,它就不会被垃圾回收。只有当该对象没有任何的强引用时,垃圾回收才会销毁该对象并且回收该对象所占的内存空间。

WeakRef 允许您保留对另一个对象的弱引用,而不会阻止被弱引用对象被垃圾回收。

1
2
3
let target = {};
let wr = new WeakRef(target);

WeakRef 实例对象有一个deref()方法,如果原始对象存在,该方法返回原始对象;如果原始对象已经被垃圾回收机制清除,该方法返回undefined

1
2
3
4
5
6
7
let target = {};
let wr = new WeakRef(target);

let obj = wr.deref();
if (obj) { // target 未被垃圾回收机制清除
// ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
let like = new WeakRef(document.getElementById("like"))
let mymap = new WeakMap()
mymap.set(like.deref(), {
click: 0
})
like.deref().onclick = function () {
let times = mymap.get(like.deref())
times.click++
}

setTimeout(() => {
document.body.removeChild(like.deref())
}, 2000)
6. FinalizationRegistry

清理器注册表功能 FinalizationRegistry,用来指定目标对象被垃圾回收机制清除以后,所要执行的回调函数。

首先,新建一个注册表实例。

1
2
3
const registry = new FinalizationRegistry(data => {
// ....
});
1
2
3
registry.register(obj, "some value");
registry.unregister(obj);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let like = new WeakRef(document.getElementById("like"))
let mymap = new WeakMap()
mymap.set(like.deref(), {
click: 0
})
like.deref().onclick = function () {
let times = mymap.get(like.deref())
times.click++
}

setTimeout(() => {
// registry.register(document.getElementById("like"), mymap.get(like.deref()));
registry.register(like.deref(), mymap.get(like.deref()));

document.body.removeChild(like.deref())
}, 2000)


const registry = new FinalizationRegistry(data => {
// ....
console.log("被销毁了", data)
});

八.ES13新特性

1. 私有属性和方法
1
2
3
4
5
6
7
8
9
10
11
12
13
class Cache{
#obj ={}

get(key){
return this.#obj[key]
}
set(key,value){
this.#obj[key] =value
}
}

let cache = new Cache()
cache.set("name","kerwin")
2.静态成员的私有属性和方法

我们还可以给类定义静态成员和静态私有函数。类的静态方法可以使用this关键字访问其他的私有或者公有静态成员,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 class Cache{
static #count = 0;

static getCount(){
return this.#count
}

#obj ={}

get(key){
return this.#obj[key]
}
set(key,value){
this.#obj[key] =value
}
}

let cache = new Cache()
cache.set("name","kerwin")

console.log(Cache.getCount())
3.静态代码块

ES13允许在类中通过static关键字定义一系列静态代码块,这些代码块只会在类被创造的时候执行一次。这其实有点像一些其他的如C#和Java等面向对象的编程语言的静态构造函数的用法。

一个类可以定义任意多的静态代码块,这些代码块会和穿插在它们之间的静态成员变量一起按照定义的顺序在类初始化的时候执行一次。我们还可以使用super关键字来访问父类的属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
 class Cache{
static obj = new Map()
static {
this.obj.set("name","kerwin")
this.obj.set("age",100)
}

static{
console.log(this.obj)
}
}

console.log(Cache.obj)
4. 使用in来判断某个对象是否拥有某个私有属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Cache {
#obj = {}

get(key) {
return this.#obj[key]
}
set(key, value) {
this.#obj[key] = value
}

hasObj(){
return #obj in this
}
}

let cache = new Cache()
console.log(cache.hasObj())
5.支持在最外层写await

顶层await只能用在 ES6 模块,不能用在 CommonJS 模块。这是因为 CommonJS 模块的require()是同步加载,如果有顶层await,就没法处理加载了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script type="module">
function ajax() {
return new Promise((resolve) => {
setTimeout(() => {
resolve("data-1111");
}, 1000);
})
}

let res = await ajax();
console.log(res)

</script>

6. at函数来索引元素
1
2
3
4
5
6
7
8
9
let arr = ["kerwin","tiechui","gangdan","xiaoming"]

console.log(arr[1])
console.log(arr[arr.length-1]) //变丑了
console.log(arr[arr.length-2]) //变丑了

console.log(arr.at(1))
console.log(arr.at(-1))
console.log(arr.at(-2))
7. 正则匹配的开始和结束索引
1
2
3
4
5
6
let str = "今天是2022-11-10"
let reg = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/d

//exec
let res = reg.exec(str)
console.log(res)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-67p98HC6-1681921901152)(%E7%AC%94%E8%AE%B0.assets/image-20220927105243515.png)]

8.findLast()和findLastIndex()函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let arr = [11,12,13,14,15]

// let res = arr.find(function(value){
// return value % 2 === 0
// })
// let res = arr.findIndex(function(value){
// return value % 2 === 0
// })
// let res = arr.findLast(function(value){
// return value % 2 === 0
// })
let res = arr.findLastIndex(function(value){
return value % 2 === 0
})
console.log(res)
9.Error对象的Cause属性

Error对象多了一个cause属性来指明错误出现的原因。这个属性可以帮助我们为错误添加更多的上下文信息,从而帮助使用者们更好地定位错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function getData(){
try{
console.log(kerwin)
}
catch(e){
throw new Error('New error 1111111',{cause:"这是因为,,,,,,,,,"});
}
}


try{
getData()
}catch(e){
console.log(e.cause)
}

关于Nodejs搭建服务器增删改查和登录操作实例

关于Nodejs搭建服务器增删改查和登录操作实例

在 node.js 中创建一个服务器非常简单,只需要使用 node.js 为我们提供的 http 模块及相关 API 即可创建一个麻雀虽小但五脏俱全的web 服务器,相比 Java/Python/Ruby 搭建web服务器的过程简单的很。

1.数据库

数据库名:crud_demo
表名:user
字段:id,name,sex,age

2.代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//引用mysql依赖
const mysql = require("mysql");
//引用express依赖
const express = require("express");
//引用body-parser 解析post传参
const bodyParser = require("body-parser");
//express实例化
const app = express();
//引用 cors
const cors = require("cors");
const { json } = require("body-parser");
//关闭Form表单传值
app.use(bodyParser.urlencoded({ extended: false }));
//使用Json传值
app.use(bodyParser.json());
//使用cors 解决跨域问题
app.use(cors());

const connection = mysql.createConnection({
host: "localhost",
user: "root",
password: "password",//修改成自己的密码
database: "crud_demo",//修改成自己的数据库名
});
// 建立数据库连接
connection.connect();

/**
* select
* http://127.0.0.1:3000/api
*/
app.get("/api", (req, res) => {
let sql = "select * from user";
connection.query(sql, function (error, results, fields) {
if (error) throw error;
console.log(results);
// return results;
return res.json(results);
});
});

/**
* delete
* http://127.0.0.1:3000/api/delete/4
*/
app.get("/api/delete/:id", (req, res) => {
console.log(req.params);
console.log(req.params.id);
let delSql = `DELETE FROM user where id= ${parseInt(req.params.id)}`;
console.log(delSql);
connection.query(delSql, function (error, results) {
if (error) throw error;
console.log(results);
// return results;
return res.json(results);
});
});

/**
* insert
* http://localhost:3000/api/insert?id=4&name=xiaohe&sex=%E7%94%B7&age=19
*/
app.get("/api/insert", (req, res) => {
console.log(req.query);
console.log(req.body);
console.log(req.query.id);
let insertSql = `insert into user VALUES(?,?,?,?)`;
console.log(insertSql);
let data = [req.query.id, req.query.name, req.query.sex, req.query.age];
connection.query(insertSql, data, function (error, results) {
if (error) throw error;
console.log(results);
// return results;
return res.json(results);
});
});

/**
* update
* http://localhost:3000/api/update?id=4&name=%E7%99%BE%E5%BA%A6&sex=%E7%94%B7&age=999
*/
app.get("/api/update", (req, res) => {
console.log(req.query);
console.log(req.body);
console.log(req.query.id);
let updateSql = `update user set name = ?, sex = ?, age = ? where id = ?`;
console.log(updateSql);
let data = [req.query.name, req.query.sex, req.query.age, req.query.id];
connection.query(updateSql, data, function (error, results) {
if (error) throw error;
console.log(results);
// return results;
return res.json(results);
});
});

/**
* login
* http://127.0.0.1:3000/api/login?name="1234567890"&pwd="123456"
*/
app.get("/api/login", (req, res) => {
let loginSql = `select * from user where name = ? and pwd = ?`;
console.log(loginSql);
let data = [req.query.name, req.query.pwd];
connection.query(loginSql, data, function (error, results, fields) {
if (error) throw error;
console.log(results);
// return results;
if (results.length) {
console.log("登录成功");
} else {
console.log("账号不存在");
}
return res.json(results);
});
});

app.listen(3000, () => {
console.log("服务器启动成功...");
});

3.运行

1
node 文件名字

4.测试

在浏览器中访问代码中对应的链接地址

数据结构之操作顺序表实战

数据结构之操作顺序表实战——C语言

序言

顺序表是在计算机内存中以数组的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素、使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系,采用顺序存储结构的线性表通常称为顺序表。顺序表是将表中的结点依次存放在计算机内存中一组地址连续的存储单元中。

一.数据结构概述

在这里插入图片描述

二.要求

本实训是对课本中关于顺序表的所有知识的综合实训,其内容包括了顺序表的各种基本操作。通过实训让读者掌握顺序表各种操作的算法编写,并理解各种操作之间的联系。

用户运行程序时,屏幕中显示选项菜单:


    --线性顺序表--
   1--------顺序表初始化     
   2--------插入             
   3--------删除             
   4--------求表长           
   5--------按值查找         
   6--------读取元素值       
   7--------显示线性表       
   0--------退出             

根据给出的问题,选择算法;正确画出流程图;正确输入、编译、链间、运行程序;运行结果用截图方式插入本报告;原程序有必要的注释;验证程序要全面。

代码包含顺序表的基本操作,包括顺序表的初始化、元素的插人、元素的删除、计算顺序表长度、在顺序表中查找某个关键字、读取指定位置的元素值和输出顺序表的元素信息等。

三.作答

1.代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
#include <stdio.h>
#include <malloc.h>

#define ElemType int //在此例中数据类型采用数字类型
#define MaxSize 100 //顺序表的最大长度

typedef struct
{
ElemType *data; //存储空间基址
int length; //线性表当前的长度
} SqList;

//初始化顺序表,创建一个空表
int InitList(SqList *L)
{
L->data = (ElemType *)malloc(MaxSize * sizeof(ElemType));
if (!L->data)
{
printf("存储空间分配失败!");
}
L->length = 0;
}

//创建指定大小的顺序表
int CreateSqList(SqList *L, int n) // n为需要创建顺序表的长度
{
int i = 0;
if (n > MaxSize || n < 1)
{
printf("顺序表的长度非法");
}
else
{
printf("请输入%d个数据:", n);
for (i = 0; i < n; i++)
{
scanf("%d", &L->data[i]);
L->length++;
}
}
}

//在指定位置插入数据
int InsertSqList(SqList *L, int n, ElemType e) // n为插入位置,e为要插入的数据
{
int i = 0;
if (n > L->length || n < 1)
{
printf("插入位置非法!");
}
else
{
for (i = L->length - 1; i >= n - 1; i--)
{
L->data[i + 1] = L->data[i];
}
L->data[n - 1] = e;
L->length++;
}
}

//删除指定位置的数据
int DeleteSqList(SqList *L, int n) // n为要删除的数据位置
{
int i = 0;
if (n > L->length || n < 1)
{
printf("删除位置非法!");
}
else
{
for (i = n; i < L->length; i++)
{
L->data[i - 1] = L->data[i];
}
L->length--;
}
}

//查看顺序表长度
int len_seqList(SqList *L) /*返回顺序表L的长度*/
{
return L->length;
// return L->last;
}

//按值查找
int location_seqlist(SqList *L, ElemType e) /*在线性表中查找值为e的数据元素*/
{
int i = 0;
while (i < L->length && L->data[i] != e)
i++;
if (i == L->length)
return 0; /*查找不成功*/
else
return i; /*返回的是存储位置*/
}

//读取元素值
int get_from_seqlist(SqList *L, int i) /*返回线性表中的第i个元素的值*/
{
if (i < 1 || i > L->length)
{
printf("输入的序号有误,请重新输入!\n");
// return L->data[i] = -1;
// return NULL;
}
else
return L->data[--i];
}

//显示顺序表
void PrintSqList(SqList *L)
{
int i;
printf("打印出的顺序表为:\n");
printf("*************************\n");
for (i = 0; i < L->length; i++)
{
printf(" %d", L->data[i]);
}
printf("\n*************************\n");
}

int main()
{
int choice;
SqList L;
int j = 1;

while (j)
{
printf("\n\n\n");
printf("\t\t\t --线性顺序表-- \n");
printf("\n\t\t\t********************************");
printf("\n\t\t\t* 1--------顺序表初始化(创建)*");
printf("\n\t\t\t* 2--------插入 *");
printf("\n\t\t\t* 3--------删除 *");
printf("\n\t\t\t* 4--------求表长 *");
printf("\n\t\t\t* 5--------按值查找 *");
printf("\n\t\t\t* 6--------读取元素值 *");
printf("\n\t\t\t* 7--------显示线性表 *");
printf("\n\t\t\t* 0--------退出 *");
printf("\n\t\t\t********************************\n");
printf("\t\t\t请选择菜单号 (0--7): ");
scanf("%d", &choice);
if (choice == 1)
{
//顺序表初始化
InitList(&L);

int n;
printf("请输入需要创建顺序表的长度:");
scanf("%d", &n);

CreateSqList(&L, n);
}
else if (choice == 2)
{
//插入
// printf("现默认在1号位置前插入数据0! \n");
// int n, ElemType e
// InsertSqList(&L, 1, 0);
int n;
printf("请输入需要插入顺序表的位置:");
scanf("%d", &n);
ElemType e;
printf("请输入需要插入顺序表的元素:");
scanf("%d", &e);

InsertSqList(&L, n, e);
}
else if (choice == 3)
{
//删除
// printf("现默认删除1号位置数据! \n");
// int n
int n;
printf("请输入需要删除顺序表的位置:");
scanf("%d", &n);

DeleteSqList(&L, n);
}
else if (choice == 4)
{
// printf("求表长\n");
printf("当前顺序表的表长为: %d\n", len_seqList(&L));
}
else if (choice == 5)
{
// printf("按值查找\n");
int i;
ElemType e;
printf("请输入要查找的值:");
scanf("%d", &e);

i = location_seqlist(&L, e);
if (i >= 0)
printf("要查找的元素下标为:%d\n", ++i);
else
printf("元素未找到");
}

else if (choice == 6)
{
// printf("读取元素值\n");
int i;
ElemType e;
printf("请输入要读取的结点的序号:");
scanf("%d", &i);
//读取节点的序号
e = get_from_seqlist(&L, i);

printf("要查找的下标为%d的元素值为:%d\n", i, e);
}
else if (choice == 7)
{
//显示线性表
PrintSqList(&L);
}
else if (choice == 0)
{
j = 0;
printf(" \t \t \t 程序结束! \n");
}
else
{
printf("\n\t\t\t输入错误!请重新输入! \n");
}
printf("操作成功!\n");
}
//测试
// InitList(&L); // 1--------顺序表初始化
// CreateSqList(&L, 5); // 1--------创建
// InsertSqList(&L, 1, 0); // 2--------插入
// DeleteSqList(&L, 1); // 3--------删除
// len_seqList(&L); // 4--------求表长
// location_seqlist(&L, 1); // 5--------按值查找
// get_from_seqlist(&L, 1); // 6--------读取元素值
// PrintSqList(&L); // 7--------显示线性表
return 0;
}

2.运行效果:

2.1 顺序表初始化:

在这里插入图片描述
在这里插入图片描述

2.2 插入

在这里插入图片描述
在这里插入图片描述

2.3 删除

在这里插入图片描述
在这里插入图片描述

2.4 求表长

在这里插入图片描述

2.5 按值查找

在这里插入图片描述

2.6 读取元素值

在这里插入图片描述

2.7 显示数据表

在这里插入图片描述

2.8 退出

在这里插入图片描述

初学Vue第一篇

初学Vue第一篇

1.打开vscode

在这里插入图片描述

2.新建一个终端

在这里插入图片描述

3.输入vue create 项目名称(我这里是vue-case)

在这里插入图片描述

4.配置vue项目(我这里选三个自主配置,第一个vue3默认配置,第二个vue2默认配置)

在这里插入图片描述

5.键盘上下键和空格键勾选vueRouter 和 vuex

在这里插入图片描述

6.选择vue版本

在这里插入图片描述

7.键入y需要为生产中的索引回退设置适当的服务器

在这里插入图片描述

8.一律键入y

9.创建完毕

在这里插入图片描述

10.项目结构目录

在这里插入图片描述

11.项目配置package.json

在这里插入图片描述

12.输入cd 项目名称

在这里插入图片描述

13.输入npm run serve运行

在这里插入图片描述

14.运行成功

在这里插入图片描述

如果对你有帮助,点赞、收藏、关注是我更新的动力!👋🌟🚀

Ubuntu上运行c语言教程

Ubuntu上运行c语言教程

0.序言

Ubuntu是一个以桌面应用为主的Linux操作系统,其名称来自非洲南部祖鲁语或豪萨语的“ubuntu”一词,意思是“人性”“我的存在是因为大家的存在”,是非洲传统的一种价值观。Ubuntu基于Debian发行版和Gnome桌面环境,而从11.04版起,Ubuntu发行版放弃了Gnome桌面环境,改为Unity。从前人们认为Linux难以安装、难以使用,在Ubuntu出现后这些都成为了历史。Ubuntu也拥有庞大的社区力量,用户可以方便地从社区获得帮助。

1.准备工作(检查网络&更新源)

1
2
3
4
5
6
7
8
9
10
11
12
ping www.baidu.com
-- 检查网络

services.msc
-- ???

VMware DHCP Service
VMware NAT Service
-- 若网络未连接,win+shift+esc打开控制台,在任意一项右键转到服务,找到上述两项重启,再重启虚拟机即可连接到网络。

sudo apt update
-- 更新源

2.安装ssh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
sudo ps -e |grep ssh
-- 检查是否安装ssh服务

sudo apt install openssh-server
-- 安装ssh服务

sudo ps -e |grep ssh
-- 再次检查

service ufw stop
-- 暂停防火墙

sudo apt install net-tools
-- 下载网络检查工具

ifconfig
-- 查看IP和网络配置

3.安装gcc g++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
sudo apt install gcc
-- 安装gcc

sudo apt install build-essential
-- 下载打包构建工具

sudo apt install g++
-- 下载g++
````

## 4.安装vim

```bat
sudo apt install vim
-- 下载vim
````

## 5.测试

```bat
vim hello.c
-- 创建hello.c文件

gcc -Wall hello.c -o hello
gcc hello.c -o hello
-- 将hello.c文件编译为hello

./hello
-- 运行hello可执行文件

6.切换用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
sudo passwd root
-- 为root用户设置密码

su root
-- 切换到root用户

sudo adduser he
-- 增加he用户

sudo deluser he
删除he用户

w
-- 查看所有用户

1
2
3
4
5
6
7
// 测试代码
#include<stdio.h>
int main(void)
{
printf("hello world!\n");
return 0;
}

如果对你有帮助,点赞、收藏、关注是我更新的动力!👋🌟🚀

  • Copyrights © 2022-2024 何福海
  • 访问人数: | 浏览次数:

请我喝杯奶茶吧~

支付宝
微信