23.<Spring图书管理系统(强制登录版本)>
在前面两篇,我们基本上实现了图书管理系统所有的功能,但是我们发现没有登录也能对其进行修改。这是非常不安全的。因此这篇文章我们学习如何进行强制登录。只有登录进去才能进行操作。
这不是一个对外开放的项目
这篇文章我们将改写图书管理系统为强制登录版本。我们需要结合在统一功能中讲解的
拦截器、统一数据返回、统一异常处理。内容完整的再写一遍图书管理系统
一、实现强制登录
1.1自定义拦截器
1.新建interceptor目录。创建LoginInterceptor类去实现HandlerInterceptor接口
注意要加上@Component注解,代表已经交给Spring去管理。这个类才会被Spring检测到
2.重写preHandle方法。返回 true-放行。false拦截
3.验证用户是否登录。
//验证用户是否登录HttpSession session = request.getSession();UserInfo userInfo = (UserInfo) session.getAttribute(Constants.USER_SESSION_KEY);if(userInfo == null || userInfo.getId()<1){response.sendRedirect("/login.html");return false;}return true;
- 如果没有登录则跳转到/login.html登录页面进行登录。return false
- 如果已经登录那么直接return true。放行
1.2注册配置拦截器
1.新建Config目录,创建WebConfig类 实现 WebMvcConfigurer 接口
注意要加上@Configuration 才能生效。
2.新建我们刚刚自定义的拦截器成员变量
@Autowiredprivate LoginInterceptor loginInterceptor;
3.重写addInterceptors方法。
addInterceptor:添加拦截器
addPathPatterns:添加拦截路径
excludePathPatterns:除了这些路径不用拦截
@Overridepublic void addInterceptors(InterceptorRegistry registry) {// /**表示对所有路径生效registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns(excludePath);}
private List<String> excludePath = Arrays.asList("/user/login","/**/login.html","/css/**","/js/**","/pic/**");
自此。我们就实现了强制登录。
完整LoginInterceptor类代码
/*** 拦截器* 实现强制登录*/
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {/*** 处理请求前执行。* @param request* @param response* @param handler* @return* @throws Exception*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {log.info("目标方法执行前执行:LoginInterceptor.preHandle...");//true-放行。false拦截//验证用户是否登录HttpSession session = request.getSession();UserInfo userInfo = (UserInfo) session.getAttribute(Constants.USER_SESSION_KEY);if(userInfo == null || userInfo.getId()<1){response.sendRedirect("/login.html");return false;}return true;}/*** 接口处理完成之后执行的方法* @param request* @param response* @param handler* @param modelAndView* @throws Exception*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}
}
完整WebConfig类代码
package com.qiyangyang.springbook.demos.Config;import com.qiyangyang.springbook.demos.interceptor.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import java.util.Arrays;
import java.util.List;//配合拦截器进行拦截处理
//注意一定要加上@Configuration这个注解 才会生效
@Configuration
public class WebConfig implements WebMvcConfigurer {private List<String> excludePath = Arrays.asList("/user/login","/**/login.html","/css/**","/js/**","/pic/**");@Autowiredprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {// /**表示对所有路径生效registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns(excludePath);}// @Override
// public void addInterceptors(InterceptorRegistry registry) {
// // /** 表示对所有的路径生效
// registry.addInterceptor(loginInterceptor).addPathPatterns("/**")
// //排除一些路径
// .excludePathPatterns("/user/login")
// .excludePathPatterns("/**/**.html")
// .excludePathPatterns("/css/**")
// .excludePathPatterns("/js/**")
// .excludePathPatterns("/pic/**");
// }
}
二、实现统一返回数据
2.1创建Result类
统一返回数据
1.首先创建Result类,这个类帮我们更加细致化的对后端返回结果进行描述
package com.qiyangyang.springbook.demos.model;import lombok.Data;/*** 定义统一返回数据* 对返回结果进行细致化描述* @param <T>*/@Data
public class Result <T>{private Integer code;//后端响应状态码,业务状态码 200成功,-1失败,-2表示未登录private String Errmsg; //后端发生错误的原因private T data; //返回的数据public static <T>Result<T> success(T data){Result<T> result = new Result<T>();result.setCode(200);result.setData(data);return result;}public static <T>Result<T> fail(T data,String errmg){Result<T> result = new Result<T>();result.setCode(-1);result.setErrmsg(errmg);result.setData(data);return result;}public static <T> Result<T> fail(String errMsg){Result<T> result = new Result<T>();result.setCode(-1);result.setErrmsg(errMsg);return result;}/*** 未登录时*/public static <T> Result<T> unlogin(){Result<T> result = new Result<T>();result.setCode(-2);result.setErrmsg("用户未登录");return result;}}
2.2创建ResponseAdvice类
创建ResponseAdvice并实现ResponseBodyAdvice接口
* 统一数据返回格式 * 不需要改变程序任何接口,只需要写一个类。实现ResponseBodyAdvice。加上@ControllerAdvice注解
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {@Autowiredprivate ObjectMapper objectMapper;@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {//重写supports方法。看对哪些请求进行处理。返回true--进行处理 返回false。不进行处理return true;}@SneakyThrows@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {//重写beforeBodyWrite方法。如果body是Result类型。不再进行包装。如果不是。那么就进行包装//接口执行完成之后,返回结果之前会执行这个方法。//注意需要处理String类型的if (body instanceof Result){return body;}if (body instanceof String){return objectMapper.writeValueAsString(Result.success(body));}return Result.success(body);}
}
//重写beforeBodyWrite方法。如果body是Result类型。不再进行包装。如果不是。那么就进行包装
//接口执行完成之后,返回结果之前会执行这个方法。(beforeBodyWrite)
//注意需要处理String类型的
此时我们会发现。我们之前定义的后端接口的返回结果已经不再适用。
例如:
我们进行用户登录:
发现返回的数据是这种json类型的
三、修改前端代码
登录页代码
将判断条件修改为
if(result.code==200 && result.data ==true){//验证成功location.href = "book_list.html";}else {alert("登录失败,用户名不存在或密码错误!");}
图书列表显示页代码
将
var books = result.records;
修改为
var books = result.data.records;
function getBookList() {//ajax页面一加载就会被执行的程序。 我们还可以进行方法封装$.ajax({type: "get",url: "/book/getListByPage" + location.search,success: function (result) {var books = result.data.records;console.log(books); //如果前端没有报错,那么我们打印日志。观察后端返回结果对不对var findHtml = ""; //用这个变量来拼接HTMLfor (var book of books) {//拼接html。假如后端返回10个tr那么直接for循环拼接在这里面。findHtml//我们用单引号拼接,因为里面有双引号
图书分页代码
将
//翻页信息$("#pageContainer").jqPaginator({totalCounts: result.count, //总记录数pageSize: 10, //每页的个数visiblePages: 5, //可视页数currentPage: result.pageRequest.currentPage, //当前页码
修改为
//翻页信息$("#pageContainer").jqPaginator({totalCounts: result.data.count, //总记录数pageSize: 10, //每页的个数visiblePages: 5, //可视页数currentPage: result.data.pageRequest.currentPage, //当前页码
前端添加图书代码
注意这里有一个坑
我们照上面修改
将
success: function (result) {console.log(result.data);if (result == "成功添加图书") {alert("添加成功");location.href = "book_list.html";} else {alert(result.data);}
修改为
success: function (result) {console.log(result.data);if (result.code ==200 && result.data == "成功添加图书") {alert("添加成功");location.href = "book_list.html";} else {alert(result.data);}
会发现添加图书功能依然完成不了。且会执行else块的代码
并且报错为undefind。
这是由于返回的数据是String类型。
而上面我们进行处理的后端返回数据都是json类型。如果是json类型。那么前端会自动转换为对象进行处理。
而Sring就不会进行处理。因此代码没有按我们的预期执行。
我们通过Postman和Fiddler抓包也能看到返回的是text类型文本。而不是json。
而我们登录接口返回的是json
两种解决方式
1.前端处理,把字符串转为对象
2.后端处理,设置 content-type 返回结果 (更加合理)
这种问题也称作边界问题。
后端处理我们可以进行统一返回Json。这样更加合理。
为什么String类型会出现这种情况。
这个就是在我们统一返回结果类中进行处理的。
@SneakyThrows@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {//重写beforeBodyWrite方法。如果body是Result类型。不再进行包装。如果不是。那么就进行包装//接口执行完成之后,返回结果之前会执行这个方法。//注意需要处理String类型的if (body instanceof Result){return body;}if (body instanceof String){return objectMapper.writeValueAsString(Result.success(body));}return Result.success(body);}
发现body如果是String类型。我们将这个body进行了writeValueAsString转成了String
因此我们将
@RequestMapping("/addBook")public String addBook(BookInfo bookInfo){log.info("添加图书,bookInfo:{}",bookInfo);/**
修改为
@RequestMapping(value = "/addBook",produces = "application/json")public String addBook(BookInfo bookInfo){log.info("添加图书,bookInfo:{}",bookInfo);/**
这样我们返回的数据就是Json格式的了。再试试添加图书,就会发现,我们添加成功
因此我们不用返回String类型的就可以避免这个问题了
正确的返回我们本就应该返回为Result类型。试着修改代码如下。
@RequestMapping( "/addBook")public Result addBook(BookInfo bookInfo){log.info("添加图书,bookInfo:{}",bookInfo);/*** 参数校验*/if(!StringUtils.hasLength(bookInfo.getBookName())|| !StringUtils.hasLength(bookInfo.getAuthor())|| !StringUtils.hasLength(bookInfo.getPublish())|| bookInfo.getCount() <=0|| bookInfo.getPrice()==null){return Result.fail("参数错误!!!");}/*** 添加图书*//*** 出现错误比如参数错误。添加过程出现异常,因为MySQL会出现一些异常* 我们使用try/catch来捕获。*/try {bookService.insertBook(bookInfo);}catch (Exception e){log.error("添加图书失败,e:{}",e);return Result.fail("内部发生错误,请联系管理员!");}/*** 返回成功添加吐过表示图书插入成功。*/return Result.success("成功添加图书");}
修改图书代码
查询当前 Id 图书代码修改
//查询当前ID图书$.ajax({type: "get",url: "book/queryBookById"+location.search,success:function(book){if(book!=null){$("#bookId").val(book.id);$("#bookName").val(book.bookName);$("#bookAuthor").val(book.author);$("#bookStock").val(book.count);$("#bookPrice").val(book.price);$("#bookPublisher").val(book.publish);$("#bookStatus").val(book.status);}}
修改为
type: "get",url: "book/queryBookById"+location.search,success:function(result){//前端根据后端返回结果,针对不同的情况进行处理if(result.code == 200 && result.data != null){var book = result.data;if(book!=null){$("#bookId").val(book.id);$("#bookName").val(book.bookName);$("#bookAuthor").val(book.author);$("#bookStock").val(book.count);$("#bookPrice").val(book.price);$("#bookPublisher").val(book.publish);$("#bookStatus").val(book.status);}}
修改更新图书代码
type: "get",url: "/book/updateBook",data:$("#updateBook").serialize(),//提交整个表单success:function(result){if(result == true){alert("更新成功");location.href = "book_list.html"}else{alert("更新失败");}
修改为
type: "get",url: "/book/updateBook",data:$("#updateBook").serialize(),//提交整个表单success:function(result){if(result.code==200 && result.data == true){alert("更新成功");location.href = "book_list.html"}else{alert("更新失败");}
删除图书代码
if(result == true){alert("删除成功");location.href = "book_list.html";}
修改为
if(result.code ==200 && result.data == true){alert("删除成功");location.href = "book_list.html";}
批量删除图书代码
if(result == true){alert("批量删除成功");location.href = "book_list.html";}else{alert("删除失败,请联系管理员!");}
修改为
if(result.code ==200 && result.data == true){alert("批量删除成功");location.href = "book_list.html";}else{alert("删除失败,请联系管理员!");}
四、统一异常处理
@ControllerAdvice
public class ErrorHandler {/*** 捕获异常,返回统一结果*///通过@ExceptionHandler这个注解来捕获异常@ExceptionHandlerpublic Result handler(Exception e){return Result.fail(e.getMessage());}
}
相关文章:
23.<Spring图书管理系统(强制登录版本)>
在前面两篇,我们基本上实现了图书管理系统所有的功能,但是我们发现没有登录也能对其进行修改。这是非常不安全的。因此这篇文章我们学习如何进行强制登录。只有登录进去才能进行操作。 这不是一个对外开放的项目 这篇文章我们将改写图书管理系统为强制登…...
【插件】重复执行 pytest-repeat
安装 pip3 install pytest-repeat 用法 1.命令行 pytest --count num pytest --count 32.装饰器 pytest.mark.repeat(num) #num运行次数 pytest.mark.repeat(5)#执行结果如下:...
pip/conda install bugs汇总
DNSResolutionError 一直不行,惯防火墙还是不行,可能导致漏洞了; 解决方案: reboot下次try可以刷新DNS缓存: resolvectl flush-cachespip._vendor.urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(host‘mirrors.a…...
通过shell脚本分析部署nginx网络服务
通过shell脚本分析部署nginx网络服务 1.接收用户部署的服务名称 [rootlocalhost xzy]# vim 1.sh [rootlocalhost xzy]# chmod x 1.sh [rootlocalhost xzy]# ./1.sh2.判断服务是否安装 已安装;自定义网站配置路径为/www;并创建共享目录和网页文件&…...
Java基础——继承和多态
目录 一、继承 继承的定义: 继承的基本用法: 如何调用父类的方法? 二、多态 多态性的好处 多态中的强制类型转换: 包的命名规则——域名倒叙 一、继承 继承的定义: 继承是面向对象编程中的一种机制,…...
长江存储嵌入式面试题及参考答案
构造函数、析构函数可以为虚函数吗,为什么? 构造函数最好不要是虚函数。原因如下: 从对象的生命周期角度来看,虚函数的调用是通过虚函数表(vtable)来实现的。而在构造函数执行的时候,对象还没有完全构造好,vtable 可能还没有被正确地初始化。如果构造函数是虚函数,在…...
WordPress设置自动更新CSS版本号
WordPress 通常会在引用 CSS 文件时添加版本号参数(?verx.x.x)。如果版本号未更新,浏览器可能继续加载旧的文件。 解决方法:确保你在 functions.php 文件中正确加载了 CSS 文件,并动态更新版本号。例如在functions.p…...
【Qt聊天室】客户端实现总结
目录 1. 项目概述 2. 功能实现 2.1 主窗口设计 2.2 功能性窗口 2.3 主界面功能实现 2.4 聊天界面功能实现 2.5 个人信息功能开发 2.6 用户信息界面设置功能 2.7 单聊与群聊 2.8 登录窗口 2.9 消息功能 3. 核心设计逻辑 3.1 核心类 3.2 前后端交互与DataCenter 4…...
服务器数据恢复—raid5阵列故障导致上层系统分区无法识别的数据恢复案例
服务器数据恢复环境: 某品牌DL380服务器,服务器中三块SAS硬盘组建了一组raid5阵列。服务器安装Windows Server操作系统,划分了3个分区,D分区存放数据库,E分区存放数据库备份。 服务器故障: RAID5阵列中有一…...
mysql bin log分析
centos7 部署collabora office (yum版 与 docker)_collabora office部署-CSDN博客 1.下载polardb的bin log文件 show binary logs; mysqlbinlog -u 用户名 -p -h 地址 --read-from-remote-server --raw mysql-bin.001768 mysqlbinlog --no-defaults --databasexxx --base64-…...
深入理解 Vue 3 中的 emit
深入理解 Vue 3 中的 emit 在 Vue 3 中,组件通信是开发中非常重要的一部分,其中通过 emit 实现父子组件通信是最常见的方式之一。emit 的作用是:子组件可以通过触发自定义事件将数据传递给父组件。 在本篇文章中,我们将从以下几…...
#lwIP 的 Raw API 使用指南
1. 简介 lwIP(Lightweight IP)是一个为嵌入式系统设计的开源轻量级 TCP/IP 协议栈。它旨在提供尽可能小的内存占用和高效的性能,适用于资源受限的设备,如物联网设备、路由器和工业控制系统。lwIP 支持多种协议,包括 I…...
Elasticsearch开启认证及kibana密码登陆
Elasticsearch不允许root用户运行,使用root用户为其创建一个用户es,为用户es配置密码,并切换到es用户。 adduser elastic passwd elastic su elasticElasticsearch(简称ES)是一个基于Lucene的搜索服务器。它提供了一个分布式、多用户能力的全文搜索引擎,基于RESTful web…...
【论文阅读】Large Language Models for Equivalent Mutant Detection: How Far Are We?
阅读笔记:Large Language Models for Equivalent Mutant Detection: How Far Are We? 1. 来源出处 本文发表于《ISSTA’24, September 16–20, 2024, Vienna, Austria》会议,由Zhao Tian, Honglin Shu, Dong Wang, Xuejie Cao, Yasutaka Kamei和Junji…...
vue2 面试题带答案,万字总结
1. 什么是 vue Vue 是一套用于构建用户界面的渐进式框架。Vue.js 的主要特点:渐进式框架、声明式渲染、组件化、响应式数据绑定等; 2、MVC 和 MVVM 区别 MVC 是模型(model)-视图(view)-控制器(controller),控制器负责…...
git的常用用法(最简精华版)
一、工作区域(工作区,暂存区,本地仓库) 1、工作区 当前正在使用的文件 2、暂存区 已使用add命令提交的工作区的文件,会保存到暂存区 3、本地仓库 已使用commit命令提交的暂存区的文件,会保存到本地仓库。…...
哥德巴赫猜想渐行渐远
我现在的工作,表明经典分析可能出了问题,如此则连Vinogradov的三素数定理都不成立了,更别说基于L-函数方程的陈氏定理“12”了。事实上即使L-函数方程成立,由于我指出Siegel定理不成立,陈景润和张益唐的工作就不成立。…...
Spring Boot应用开发实战:构建高效、可维护的Web应用
Spring Boot应用开发实战:构建高效、可维护的Web应用 在当今快速迭代的软件开发环境中,Spring Boot凭借其“约定优于配置”的理念,迅速成为Java开发者构建微服务及Web应用的首选框架。它不仅简化了Spring应用的初始搭建以及开发过程,还通过自动配置、嵌入式服务器等特性,…...
keep-alive多级页面缓存实现
文章目录 keep-alive多级页面缓存实现只适用于页面是否缓存状态不变的情况对于上面的问题提供一种解决方案 keep-alive多级页面缓存实现 只适用于页面是否缓存状态不变的情况 网上有一种很普遍的教程,不使用keep-alive的include属性,而是通过在路由表中…...
ks 小程序sig3
前言 搞了app版的快手之后 (被风控麻了) 于是试下vx小程序版的 抓包调试 小程序抓包问题 网上很多教程, github也有开源的工具代码 自行搜索 因为我们需要调试代码,所以就用了下开源的工具 (可以用chrome的F12功能&a…...
图论之构造完全图
题目 2398: 信息学奥赛一本通T1489-构造完全图 时间限制: 2s 内存限制: 192MB 提交: 16 解决: 9 题目描述 对于完全图 G,若有且仅有一棵最小生成树为 T,则称完全图 G 是树 T 扩展出的。 给你一棵树 T,找出 T 能扩展出的边权和最小的完全图 G…...
RDD触发算子:一些常用的触发算子(count、foreach、saveAsTextFile、first)
文章目录 1、count算子功能语法 2、foreach算子功能语法 3、saveAsTextFile算子功能语法 4、first算子功能语法举例 1、count算子 功能 统计RDD集合中元素的个数,返回一个int值 语法 def count(self) -> int2、foreach算子 功能 对RDD中每个元素调用一次参数中…...
搭建RAGFlow
RAGFlow 是一款基于深度文档理解构建的开源 RAG(Retrieval-Augmented Generation)引擎。RAGFlow 可以为各种规模的企业及个人提供一套精简的 RAG 工作流程,结合大语言模型(LLM)针对用户各类不同的复杂格式数据提供可靠…...
css中的box-sizing,记录
border-box:最终高度为height,默认包含padding border等属性 content-box:box-sizing默认值,最终大小为heightpaddingborder 等...
使用useCallback引发对闭包的理解
一、先简单介绍一下闭包: 闭包是 JavaScript 中的重要概念,它指的是一个函数可以“记住”并访问其词法作用域,即使在这个函数的外部被执行。简单来说,闭包是由函数及其相关的环境组合而成的。 闭包的特性 函数内部可以访问外部变量: 闭包…...
gvim添加至右键、永久修改配置、放大缩小快捷键、ctrl + c ctrl +v 直接复制粘贴、右键和还原以前版本(V)冲突
一、将 vim 添加至右键 进入安装目录找到 vim91\install.exe 管理员权限执行 Install will do for you:1 Install .bat files to use Vim at the command line:2 Overwrite C:\Windows\vim.bat3 Overwrite C:\Windows\gvim.bat4 Overwrite C:\Windows\evim.bat…...
腾讯云-COS
COS 对象存储 是一种可扩展的云端数据存储服务。它适用于存储任意类型的文件,并且可以针对这些文件进行访问控制。 CORS 跨域资源共享 是一种机制,它使用额外的HTTP头来告诉浏览器允许一个域上的Web应用请求另一个域上的资源。当需要从一个域名下的网页向…...
蓝桥杯每日真题 - 第16天
题目:(卡牌) 题目描述(13届 C&C B组C题) 解题思路: 题目分析: 有 n 种卡牌,每种卡牌的现有数量为 a[i],所需的最大数量为 b[i],还有 m 张空白卡牌。 每…...
基因组之全局互作热图可视化
引言 PlotHiC 是一个专为 Hi-C 数据可视化分析而设计的 Python 包。Hi-C 技术是一种能够检测染色体三维结构的实验方法,它能揭示 DNA 在细胞核内的三维组织结构。为了更好地展示和解释这些复杂的数据,PlotHiC[1] 可以帮助用户方便地绘制Hi-C 数据的热图。…...
基于Lora通讯加STM32空气质量检测WIFI通讯
目录 目录 前言 一、本设计主要实现哪些很“开门”功能? 二、电路设计原理图 1.电路图采用Altium Designer进行设计: 2.实物展示图片 三、程序源代码设计 四、获取资料内容 前言 随着环境污染问题的日益严重,空气质量的监测与管理已经…...
如何用模板搭建网站/网络营销内容
原文:设计模式之二(简单工厂模式)前言 简单工厂模式根据提供的数据或者参数返回几个可能的类中的一个实例,说通俗点有点像面向对象编程中的多态性,一个基类,有多个派生类,在另外的调用程序中,根据参数来决定返回这个基…...
关于做视频网站的一些代码/徐州seo代理计费
斐波那契数列最常见的应用是青蛙跳台阶问题。青蛙跳台阶问题是这样的,总共有九级台阶,青蛙一次可以跳1级台阶,也可以跳两级台阶。请问青蛙总共有几种跳法。这个问题其实可以拆分。假设青蛙第一次跳一级,那么还剩下8级台阶…...
可以做初中地理题的网站/企业推广网站有哪些
2019独角兽企业重金招聘Python工程师标准>>> 小公司出身,被大厂竞争者 KO 以下是一个非常真实的案例,是一个大厂工程师和一个小公司工程师同时求职一个独角兽公司的职位的经历。 一个是985本科学历,出身互联网大厂,四五…...
深圳网站建设 网站制作 网站设计【迅美】旧版/交换链接是什么
在 JavaScript 中类型分为七种,分别是:null, undefined, boolean, string, number, object 和 ES6 新增的 symbol,下面就来介绍前面五种,剩下的会在后面展开。 null, undefined null 和 undefined 的类型很简单,在 JavaScript 中u…...
wordpress seo by yoast下载/网站建设高端公司
【1】项目 》 属性 》 C/C 》 常规 》 多处理器编译 【2】工具》选项》项目和解决方案》VC项目设置》最大并发C编译数...
农村做网站开发/seo关键词优化排名外包
2019独角兽企业重金招聘Python工程师标准>>> 假设有一个很耗时的运算,单台机器已经没法满足需求,这时你可以想到由多台计算机协作完成。具体怎么做呢。 举个很简单的例子,假设这个耗时的运算是从1加到100000,你现在有两…...