编码规范

一、阿里入职编码规范

1.1命名规约

(1)【强制】抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类 命名以它要测试的类的名称开始,以 Test 结尾。
(2)【强制】中括号是数组类型的一部分,数组定义如下:String[] args;

个人理解:禁止使用String args[]。同时数组创建又分为动态创建和静态创建,

静态:int intArray [] = {30,31,32};

动态:int[] intArray = new int[3];

动态数组能够在实际应用中改变长度,arrayList就是一个复杂的动态数组。静态不能改变长度。
(3)【强制】POJO 类中布尔类型的变量,都不要加 is.

个人理解:容易造成某些框架的或关键字的识别混乱。
(4)【推荐】接口类中的方法和属性不要加任何修饰符号(public 也不要加)

个人理解:因为接口中的方法等一定要被实现,所以除了public都没什么意义。既然只能加public那就直接省略了,将其默认为public
(5)【强制】很多人对实现类命名时使用的时imp。这里建议使用impl,就是为了统一标准。统一的命名规范能够更好的利用通配符,例如调包时xxx.com.test.*.impl。
(6)【参考】方法命名规则。统一方面命名规则后能够直接通过方面名就知道方法的大概通途。如:查询单个对象用get。

1.2常量定义

(1)【强制】不允许出现任何魔法值(即未经定义的常量)直接出现在代码中。反例: String key=“Id#taobao_”+tradeId;

个人理解:解决硬编码问题。方便后期维护。
(2)【推荐】不要使用一个常量类维护所有常量,应该按常量功能进行归类,分开维护。
(3)【推荐】如果变量值仅在一个范围内变化用 Enum 类。

正例:public Enum{ MONDAY(1), TUESDAY(2), WEDNESDAY(3), THURSDAY(4), FRIDAY(5), SATURDAY(6), SUNDAY(7);}

1.3格式约束

(1)【强制】缩进采用 4 个空格,禁止使用 tab 字符。
(2)【强制】单行字符数限制不超过 120 个
(3)【强制】方法参数在定义和传入时,多个参数逗号后边必须加空格。
(4)【推荐】不同业务逻辑和语义之间要插入一行(仅一行)空行。

1.4OOP约束

(1)【强制】相同参数类型,相同业务含义,才可以使用 Java 的可变参数
(2)【强制】关于基本数据类型与包装数据类型的使用标准如下: 1) 所有的 POJO 类属性必须使用包装数据类型。 2) RPC 方法的返回值和参数必须使用包装数据类型。 3) 所有的局部变量【推荐】使用基本数据类型。

个人理解(1)和(2)中使用基本类型时,初始化或返回值为null时会默认为0,如果0有特殊含义将会造成不必要的异常。对于(3)中的理解。局部变量随着方法的存在而调用,随着方法的销毁而销    毁。如果是封装类型,当我们多次调用这个方法时,就会创建很多次封装类型的变量,如果用基本类型则会避免。
(3)【强制】定义 DO/DTO/VO 等 POJO 类时,不要设定任何属性默认值。

个人理解:更新操作的时候可能产生错误数据,例如时间字段。
(4)【推荐】循环体内,字符串的联接方式,使用 StringBuilder 的 append 方法进行扩展。

个人理解:减少new String的次数,节省内存

1.5集合处理规则

(1)【强制】关于 hashCode 和 equals 的处理,遵循如下规则:

1) 只要重写 equals,就必须重写 hashCode。 2) 因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的 对象必须重写这两个方法。 3) 如果自定义对象做为      Map 的键,那么必须重写 hashCode 和 equals。

个人理解:避免equals相等时,hashCode不相等。equals()相等的两个对象,hashcode()一定相等;equals()不相等的两个对象,却并不能证明他们的hashcode()不相等。
(2)【强制】ArrayList的subList结果不可强转成ArrayList,否则会抛出ClassCastException 异常:

个人理解:subList后只是在原ArrayList基础上拿我想要的部分。对原集合和subList视图集合操作时会相互影响。
(3)【强制】在JDK7版本以上,Comparator要满足自反性,传递性,对称性。
  个人理解:特别注意相等情况下的处理。
(4)【推荐】使用 entrySet 遍历 Map 类集合 KV ,而不是 keySet 方式进行遍历。
  个人理解:个人感觉要根据实际情况,例如有些时候我们只需要key,当key-value都取出后要进一步处理,如果集合足够小,遍历时不是很吃内存等。可以考虑使用keyset。

1.6异常日志处理

【强制】不要捕获 Java 类库中定义的继承自 RuntimeException 的运行时异常类

个人理解:运行时异常是编程错误,一般是编程逻辑错误引起的,应该从编程角度尽量避免次类异常的产生。
【强制】应用中不可直接使用日志系统 (Log 4 j 、 Logback) 中的 API ,而应依赖使用日志框架SLF 4 J 中的 API

二、spring boot 后端编码注意事项

2.1ResTful API 风格

HTTP方法
GET(SELECT):从服务器取出资源(一项或多项)。
POST(CREATE):在服务器新建一个资源。
PUT(UPDATE):在服务器更新全部资源(客户端提供改变后的完整资源)。
PATCH(UPDATE):在服务器更新部分资源(客户端提供改变的属性)。
DELETE(DELETE):从服务器删除资源。
响应状态码
100~199:信息状态码,代表请求已被接受,需要继续处理。

200~299:成功状态码,代表请求已成功被服务器接收、理解、并接受。

300~399:重定向状态码,代表需要客户端采取进一步的操作才能完成请求。

400~499:客户端错误状态码,代表了客户端看起来可能发生了错误,妨碍了服务器的处理。

500~599:服务器错误状态码,代表了服务器在处理请求的过程中有错误或者异常状态发生,也有可能是服务器意识到以当前的软硬件资源无法完成对请求的处理
特别注意几个常用的状态码:
200 请求已成功,请求所希望的响应头或数据体将随此响应返回。

400 1、语义有误,当前请求无法被服务器理解。除非进行修改,否则客户端不应该重复提交这个请求。   
2、请求参数有误。

500 服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。一般来说,这个问题都会在服务器的程序码出错时出现。

2.2常见的文本、邮件、不能为空等,spring boot自带注解可以快速实现:@Valid注解和BindingResult验证请求参数的合法性并处理校验结果。(此块内容在开发中应用较为广泛,认真学习)

例子:创建用户时,接口参数中用户名不能为空。@NotBlank(message = “用户名不能为空”)
在这里插入图片描述提示:在请求方法的字段上加上@valid注解时,以上的注解将生效。如果请求接口的参数无法通过校验,将返回状态码为400(此处认真理解学习)
在这里插入图片描述使用postman测试结果如下:
在这里插入图片描述使用postman测试结果如下:
在这里插入图片描述常见的注解如下图:
在这里插入图片描述已经能够按照RestFulApi的方式返回400的错误信息,但是返回的异常信息不是很友好(有很多乱七八糟的字段,与我们自定义的400错误信息json结构不一致,那么前端接受到此json时还需特殊处理),并且错误信息也没有进行统一的维护。所以 我们重构注解的异常方法,让其错误信息按着我们统一格式返回。
第一步:定义错误消息枚举
在这里插入图片描述第二步:在dto中加入相应注解并在message中传入定义的枚举
在这里插入图片描述第三步: Postman测试结果

在这里插入图片描述第四步: 前端接收到后端返回的json对象,做统一的拦截处理,判断 status==400弹出统一的提示(提示框为警告黄色状态,提示语为返回json对象中“error”信息)

2.3默认的校验注解只能够满足一小部分校验要求。例如需要校验一个字段是否唯一,就无法通过默认的注解完成。还有可能有更多的复杂校验逻辑,比如西仓项目需要校验一个询价单中所有的物料是否已经报价等(此处的开发很关键,每个项目都有大量这样的代码)

常见的实现思路有二种,①重构校验注解(自定义注解);②在service层【特别注意代码逻辑不允许写在controller层】代码逻辑判断后做异常抛出。我们采用后者,所以重点介绍与掌握第二点的写法。
如:根据一个用户id查找此用户,如果用户不存在返回此用户不存在。
Service层代码实现:
public Object getUser(String id)
{
User currentInstance = userRepository.findOne(id);
//判断获取的用户为空
if (currentInstance == null)
{
Map<String, Object> map;
map.put(“error”, "user " + id + “is exit!”);
map.put(“code”, “0001”);

        return   map;
    }
    return userRepository.findOne(id);
}

使用postman测试结果
在这里插入图片描述返回结果是我们之前项目经常使用的方式,但这种方式维护起来很烦,错误信息与错误状态码不能集中管理,HTTP的响应码也是200,应该是400(这是由客户端传参不正确—用户id传输错误导致的错误根据RestfulApi的规范此处应该返回400)。所以,我们需要使用更加严谨的规范。
第一步:定义错误返回消息的实体类
在这里插入图片描述第二步:定义错误消息枚举(方便统一维护错误消息,书写错误信息与错误码在此处维护)
在这里插入图片描述第三步 :定义全局错误消息处理类,重构400错误异常方法
在这里插入图片描述在这里插入图片描述通过使用注解@ControllerAdvice,类RestExceptionHandler就可以实现全局异常的拦截处理功能。自定义的方handleResourceBadRequestException旨在拦截BadRequestException异常,一旦拦截成功后,我们可以进行各种处理操作,并且返回自己想要的结果。
第四步:Service代码实现
① 在枚举中加入此错误消息
在这里插入图片描述② Service层中直接抛出自己重构过的400方法

public Object getUser(String id) throws NotFoundException
{
User currentInstance = userRepository.findOne(id);
//判断获取的用户为空
if (currentInstance == null)
{
throw new BadRequestException(ResultErrorEnum.USER_ID_NULL);

    }
    return userRepository.findOne(id);
}

第五步:Postman测试结果
在这里插入图片描述第六步: 前端接收到后端返回的json对象,做统一的拦截处理,判断 status==400弹出统一的提示(提示框为警告黄色状态,提示语为返回json对象中“error”信息)

2.4定义全局的异常处理方法,避免异常未经处理直接暴露给前端,前端无法处理未知错误的异常,经常给客户造成系统bug。(此处很关键,在日常开发中不需要程序员在此处编写逻辑代码,但是需要理解实现原理,以及重视对异常的处理,方便在一些大型项目中重构我在此封装的方法。)

解决方案:我们比较传统的解决办法是用try catch去捕获异常并按照相应的逻辑状态输出json错误码,但是初级程序员,经常遗漏或者不重视此块的代码编写,往往造成大量的程序漏洞。所以 采用spring boot 全局异常重构的方法处理此问题。

提示:基于前后端分离,重构ErrorController方法实现全局异常的捕获,所以 下面的方法对于不是前后端分离的项目不起作用,需要重新封装.特别注意----可以捕获到Controller层的异常,前提是Controller层没有对异常进行catch处理,如果Controller层对异常进行了catch处理,那么在这里就不会捕获到Controller层的异常了,所以这一点要特别注意(不是特殊业务需求,不用书写try catch)。
第一步 在yml配置文件中定义异常后跳转路由
在这里插入图片描述第二步 重构ErrorController方法,此方法最大的作用是将所有http错误状态 加入code错误码方便前端获取此类状态,所有未定义的错误 前端code都将收到500的错误状态码,但是也将系统异常信息返回给前端,方便程序员日常维护与定位错误.
在这里插入图片描述第三步:Postman测试结果:
在这里插入图片描述第四步: 前端接收到后端返回的json对象,做统一的拦截处理,判断 status==500弹出统一的提示(提示框为危险红色状态,提示语为“系统错误,请联系管理员处理!”,在浏览器中console.log出json对象信息)
总结:如postman测试结果找不到此路由本来系统返回的是status 404状态,但通过重构全局的异常方法,将所有http错误状态统一返回status为500服务器内部错误状态(返回的status同样保留系统原始异常抛出的状态方便程序员定位错误原因),方便前端获取此状态后做统一的判断处理。避免由于系统开发造成的未知bug,引起前端做无状态的处理或者处理起来容易遗漏。

2.5开发过程中需要注意如数据库密码、用户名等可配置信息请存放在配置文件中,读取配置文件参数,配置类在包config/DemoConfig.java(根据需求新增新的配置类)下面书写。配置文件 统一使用yml的方式,并且书写四个yml配置文件,用于不同环境下面的参数书写。在这里插入图片描述## 2.6注解@JsonView的使用,有时候我们希望在不同的请求中隐藏一些字段。可以用@JsonView控制输出内容。

例子:条件查询时候不返回用户的密码,查看详情时候返回用户的密码。

2.7使用前后端分离开发的项目,需要定义统一的前置路由规范例子:http://localhost:8080/api/v1/user/1 api–代表后端接口,v1–代表版本

Yml中的实现:
server:
port: 8080
servlet:
context-path: “/api/v1”

2.8代码逻辑必须写在service层,除非极为特殊情况,可以将少量逻辑放在controller

2.9SpringBoot Web项目的参数绑定:URL传参及默认参数设置

第一种情况:(参数类型必须书写包装类型)
@RequestMapping(value = “/hello1.htm”)
public String hello1(ModelMap modelMap,Integer param1, int param2) {
modelMap.addAttribute(“param1”, param1);
modelMap.addAttribute(“param2”, param2);
return “hello”;
}
这里前面的参数时包装型(Integer),后面的参数时原始类型(int)
如果不传param2:  
http://localhost:8080/hello1.htm?param1=1
直接就报错了
因为无法将null转换为原始类型
所以:所有的参数都用包装类型,别用原始类型
提示: JAVA原始数据类型与包装类类型(参考博文地址:https://blog.csdn.net/u013201439/article/details/79781845)
Java语言中默认定义了8个原始数据类型,大致可以分为4类:

整型:包含byte、short、int、long

浮点型:float、double

布尔型:boolean

字符:char

这8个基本的数据类型如果在声明的时候没有初始化,编译器会赋予默认值,引用类型的对象(如String)默认值为null。但如果是局部变量(Local Variables,方法体内申明),编译器不会自动赋予初始值,会报编译错误。
第二种情况:
如果页面上表单里的参数和代码里的参数名不一样怎么办,这时候就可以用注解了:@RequestParam(value = “paramTest”)
@RequestMapping(value = “/hello1.htm”)
public String hello1(ModelMap modelMap, @RequestParam(value = “paramTest”) Integer param1, Integer param2) {
modelMap.addAttribute(“param1”, param1);
modelMap.addAttribute(“param2”, param2);
return “hello”;
}
第三种情况:
有时候页面上的表单客户不填任何值,但是在控制器里希望它有默认值
可以这样:@RequestParam(defaultValue = “5”)
@RequestMapping(value = “/hello1.htm”)
public String hello1(ModelMap modelMap, @RequestParam(defaultValue = “5”) Integer param1, Integer param2) {
modelMap.addAttribute(“param1”, param1);
modelMap.addAttribute(“param2”, param2);
return “hello”;
}
第四种情况:
RequestParam还有个属性:required(必填)
@RequestMapping(value = “/hello1.htm”)
public String hello1(ModelMap modelMap, @RequestParam(required = true) Integer param1, Integer param2) {
modelMap.addAttribute(“param1”, param1);
modelMap.addAttribute(“param2”, param2);
return “hello”;
}
但是当required=true,和defaultValue= 同时出现时,required失效,可传可不传
此处注意:需要拦截异常处理方法,让返回的错误码是我们的格式。
第一步:定义错误消息枚举
在这里插入图片描述第二步:拦截异常
在这里插入图片描述第三步:postman测试结果
在这里插入图片描述第四步 前端处理(由于在校验方法中定义过400的错误拦截,此处不需要特殊处理)前端判断400,弹出危险黄色提示框并将错误消息显示给用户。

2.10理解spring boot 自带tomcat

(1)明白是哪一个依赖让我们使用了springboot自带的tomcat
在这里插入图片描述接下来我们把自带的容器去除:找到spring-boot-starter-web节点,修改:

org.springframework.boot
spring-boot-starter-web



org.springframework.boot
spring-boot-starter-tomcat

(2)然后再做常规的war项目配置:
( 2.1) 给war项目做一个声明:

在pom.xml里设置 war
(2.2)添加servlet-api依赖:

javax.servlet
javax.servlet-api
3.1.0
provided

(2.3)修改启动类,并重写启动方法
我们平常用main方法启动的方式,都有一个App的启动类,代码如下
在这里插入图片描述我们需要类似于web.xml的配置方式来启动spring上下文了,在Application类的同级添加一个SpringBootStartApplication类,其代码如下:
在这里插入图片描述(3)测试操作是否成功
配置我们自己的tomcat,因为我们使用的JDK1.8,所以下载最新的tomcat9版本,把项目部署上去启动测试是否能够正常访问。

2.11AOP切面的理解与应用

例子:① 若是需要一个记录日志的功能,首先想到的是在方法中通过log4j或其他框架来进行记录日志,但写下来发现一个问题,在每个方法都需要写log日志的输出代码。②判断用户是否有登录过,登录后才能让其访问我们的controller层,也需要在每个controller方法前写统一的判断方法。所以 我们使用AOP切面进行操作。
使用AOP实现 http请求中验证http签名
实现思路:
if (webAppConfing.isAutoGraph()) {//开启签名认证
//1.前端在Header中加入请求时间参数
String timestamp = request.getHeader(“timestamp”);
//2.前端与后端使用同一种加密算法加密出sign 并在Header中加入
String sign = request.getHeader(“sign”);
//3.后端使用同样的算法计算sign
String _sign = MD5Util.MD5string(URLEncoder.encode(webAppConfing.getAppKey() + timestamp, “UTF-8”));
if (_sign.equals(sign) == false) {
// log.error("【签名错误】{}", sign);
//4.如果计算出来的sign不一致,则抛出异常,不让其访问接口
throw new RuntimeException("【签名错误】" + sign);
}
第一步:在yml文件中加入配置参数,并实现参数的调用(参考上面内容如何写配置文件)
在这里插入图片描述在这里插入图片描述第二步:新建AOP切面,并写切面逻辑
在这里插入图片描述第三步:postman测试
在这里插入图片描述总结: 使用切面可以轻松完成签名认证,防止我们后端接口没有任何安全策略的情况下暴露给任何人,从而提高系统的健壮性。
使用AOP完成sql注入的筛选
实现方法较为简单,自己查看源码包中的实现方式。

2.12统一日志管理

如何做基本日志文件配置以及使用spring boot 自带的日志模块有什么弊端,参考博文(https://www.jianshu.com/p/780a1bf46a1f)
开发过程中我们需要调试项目,希望打印项目输入与输出内容,监控异常消息,接下来我们实现统一日志打印。
第一步 配置yml文件控制打印的范围
在这里插入图片描述第二步 编写日志输出公用类

/**

  • @Author: 黄明

  • @Date: 2019/4/18 9:09

  • @Description: 日志输出公用类
    */
    @Component
    public class LogUtil {
    private final Logger logger = LogManager.getLogger(this.getClass());

    private static LogUtil logUtil;
    /**

    • 读取webapp参数配置 //类不是controller在使用autowired注入的类显示为空
      */
      @Autowired
      private WebAppConfing webAppConfing;

    /**

    • 在类初始化时主动注入被Autowired的类
      */
      @PostConstruct
      public void init(){
      logUtil=this;
      logUtil.webAppConfing=this.webAppConfing;
      }

    /**

    • 正常日志信息输出
    • @param msg
      */
      public void LogInfo(String msg){
      if (logUtil.webAppConfing.isLogInfo())
      logger.info(msg);
      }

    /**

    • 警告信息打印
    • @param msg
      */
      public void LogWarn(String msg){
      if (logUtil.webAppConfing.isLogWarn())
      logger.warn(msg);
      }

    /**

    • 错误信息打印
    • @param msg
      */
      public void LogError(String msg){
      if (logUtil.webAppConfing.isLogError())
      logger.error(msg);
      }
      }

第三步: 使用AOP切面将访问接口信息打印出来
在这里插入图片描述第四步 :打印正常返回信息
在这里插入图片描述第五步: 打印异常信息
在这里插入图片描述在这里插入图片描述第六步 postman测试结果
在这里插入图片描述总结:可以从控制台看到整个接口调用周期产生的日志信息,清晰的看到系统产生的异常信息、校验信息、正常的输入输出信息,实现了统一的日志输出,不需要在每个方法中书写日志打印,这样的日志文件对于后续运维是有很大帮助的。

三、前端编码规范

3.1一般规范

以下列举了一些可应用在 HTML, JavaScript 和 CSS/SCSS 上的通用规则。
(1)文件/资源命名
在 web 项目中,所有的文件名应该都遵循同一命名约定。以可读性而言,减号(-)是用来分隔文件名的不二之选。同时它也是常见的 URL 分隔符(i.e. //example.com/blog/my-blog-entry or //s.example.com/images/big-black-background.jpg),所以理所当然的,减号应该也是用来分隔资源名称的好选择。

请确保文件命名总是以字母开头而不是数字。而以特殊字符开头命名的文件,一般都有特殊的含义与用处(比如 compass[1] 中的下划线就是用来标记跳过直接编译的文件用的)。

资源的字母名称必须全为小写,这是因为在某些对大小写字母敏感的操作系统中,当文件通过工具压缩混淆后,或者人为修改过后,大小写不同而导致引用文件不同的错误,很难被发现。

还有一些情况下,需要对文件增加前后缀或特定的扩展名(比如 .min.js, .min.css),抑或一串前缀(比如 3fa89b.main.min.css)。这种情况下,建议使用点分隔符来区分这些在文件名中带有清晰意义的元数据。
不推荐
在这里插入图片描述推荐
在这里插入图片描述(2)协议
不要指定引入资源所带的具体协议。

当引入图片或其他媒体文件,还有样式和脚本时,URLs 所指向的具体路径,不要指定协议部分(http:, https:),除非这两者协议都不可用。

不指定协议使得 URL 从绝对的获取路径转变为相对的,在请求资源协议无法确定时非常好用,而且还能为文件大小节省几个字节。
不推荐
在这里插入图片描述推荐
在这里插入图片描述不推荐

在这里插入图片描述推荐
在这里插入图片描述(3)文本缩进
一次缩进两个空格。
在这里插入图片描述在这里插入图片描述在这里插入图片描述(4)注释
注释是你自己与你的小伙伴们了解代码写法和目的的唯一途径。特别是在写一些看似琐碎的无关紧要的代码时,由于记忆点不深刻,注释就变得尤为重要了。

编写自解释代码只是一个传说,没有任何代码是可以完全自解释的。而代码注释,则是永远也不嫌多。

当你写注释时一定要注意:不要写你的代码都干了些什么,而要写你的代码为什么要这么写,背后的考量是什么。当然也可以加入所思考问题或是解决方案的链接地址。
不推荐
在这里插入图片描述推荐
在这里插入图片描述一些注释工具可以帮助你写出更好的注释。JSDoc 或 YUIDoc 就是用来写 JavaScript 注释用的。你甚至可以使用工具来为这些注释生成文档,这也是激励开发者们写注释的一个好方法,因为一旦有了这样方便的生成文档的工具,他们通常会开始花更多时间在注释细节上。

3.2HTML 规范

(1)文档类型

推荐使用 HTML5 的文档类型申明: .

(建议使用 text/html 格式的 HTML。避免使用 XHTML。XHTML 以及它的属性,比如 application/xhtml+xml 在浏览器中的应用支持与优化空间都十分有限)。

HTML 中最好不要将无内容元素 的标签闭合,例如:使用
而非

(2)HTML 验证
一般情况下,建议使用能通过标准规范验证的 HTML 代码,除非在性能优化和控制文件大小上不得不做出让步。

使用诸如 W3C HTML validator 这样的工具来进行检测。

规范化的 HTML 是显现技术要求与局限的显著质量基线,它促进了 HTML 被更好地运用。
不推荐
在这里插入图片描述推荐

在这里插入图片描述(3)省略可选标签
HTML5 规范中规定了 HTML 标签是可以省略的。但从可读性来说,在开发的源文件中最好不要这样做,因为省略标签可能会导致一些问题。

省略一些可选的标签确实使得页面大小减少,这很有用,尤其是对于一些大型网站来说。为了达到这一目的,我们可以在开发后期对页面进行压缩处理,在这个环节中这些可选的标签完全就可以省略掉了
(4)脚本加载
出于性能考虑,脚本异步加载很关键。一段脚本放置在 内,比如 ,其加载会一直阻塞 DOM 解析,直至它完全地加载和执行完毕。这会造成页面显示的延迟。特别是一些重量级的脚本,对用户体验来说那真是一个巨大的影响。

异步加载脚本可缓解这种性能影响。如果只需兼容 IE10+,可将 HTML5 的 async 属性加至脚本中,它可防止阻塞 DOM 的解析,甚至你可以将脚本引用写在 里也没有影响。

如需兼容老旧的浏览器,实践表明可使用用来动态注入脚本的脚本加载器。你可以考虑 yepnope 或 labjs。注入脚本的一个问题是:一直要等到 CSS 对象文档已就绪,它们才开始加载(短暂地在 CSS 加载完毕之后),这就对需要及时触发的 JS 造成了一定的延迟,这多多少少也影响了用户体验吧。

终上所述,兼容老旧浏览器(IE9-)时,应该遵循以下最佳实践。

脚本引用写在 body 结束标签之前,并带上 async 属性。这虽然在老旧浏览器中不会异步加载脚本,但它只阻塞了 body 结束标签之前的 DOM 解析,这就大大降低了其阻塞影响。而在现代浏览器中,脚本将在 DOM 解析器发现 body 尾部的 script 标签才进行加载,此时加载属于异步加载,不会阻塞 CSSOM(但其执行仍发生在 CSSOM 之后)
(5)多媒体回溯

对页面上的媒体而言,像图片、视频、canvas 动画等,要确保其有可替代的接入接口。图片文件我们可采用有意义的备选文本(alt),视频和音频文件我们可以为其加上说明文字或字幕。

提供可替代内容对可用性来说十分重要。试想,一位盲人用户如何能知晓一张图片是什么,要是没有 @alt 的话。

(图片的 alt 属性是可不填写内容的,纯装饰性的图片就可用这么做:alt="")。

不推荐

在这里插入图片描述推荐
在这里插入图片描述尽量用 alt 标签去描述图片,设想你需要对于那些只能通过语音或者看不见图片的用户表达图片到底是什么。
在这里插入图片描述(6)Type 属性
省略样式表与脚本上的 type 属性。鉴于 HTML5 中以上两者默认的 type 值就是 text/css 和 text/javascript,所以 type 属性一般是可以忽略掉的。甚至在老旧版本的浏览器中这么做也是安全可靠的。
不推荐
在这里插入图片描述推荐
在这里插入图片描述

3.3JavaScript 规范

(1)全局命名空间污染与 IIFE
总是将代码包裹成一个 IIFE(Immediately-Invoked Function Expression),用以创建独立隔绝的定义域。这一举措可防止全局命名空间被污染。

IIFE 还可确保你的代码不会轻易被其它全局命名空间里的代码所修改(i.e. 第三方库,window 引用,被覆盖的未定义的关键字等等)。
不推荐
在这里插入图片描述推荐
在这里插入图片描述(2)IIFE(立即执行的函数表达式)
无论何时,想要创建一个新的封闭的定义域,那就用 IIFE。它不仅避免了干扰,也使得内存在执行完后立即释放。

所有脚本文件建议都从 IIFE 开始。

立即执行的函数表达式的执行括号应该写在外包括号内。虽然写在内还是写在外都是有效的,但写在内使得整个表达式看起来更像一个整体,因此推荐这么做。
不推荐
在这里插入图片描述推荐
在这里插入图片描述so,用下列写法来格式化你的 IIFE 代码:
在这里插入图片描述(3)变量声明
总是使用 var 来声明变量。如不指定 var,变量将被隐式地声明为全局变量,这将对变量难以控制。如果没有声明,变量处于什么定义域就变得不清(可以是在 Document 或 Window 中,也可以很容易地进入本地定义域)。所以,请总是使用 var 来声明变量。

采用严格模式带来的好处是,当你手误输入错误的变量名时,它可以通过报错信息来帮助你定位错误出处。
不推荐
在这里插入图片描述推荐
在这里插入图片描述(3)理解 JavaScript 的定义域和定义域提升
在 JavaScript 中变量和方法定义会自动提升到执行之前。JavaScript 只有 function 级的定义域,而无其他很多编程语言中的块定义域,所以使得你在某一 function 内的某语句和循环体中定义了一个变量,此变量可作用于整个 function 内,而不仅仅是在此语句或循环体中,因为它们的声明被 JavaScript 自动提升了。

我们通过例子来看清楚这到底是怎么一回事:
原 function
在这里插入图片描述被 JS 提升过后
在这里插入图片描述
(4)总是使用带类型判断的比较判断
总是使用 === 精确的比较操作符,避免在判断的过程中,由 JavaScript 的强制类型转换所造成的困扰。

如果你使用 === 操作符,那比较的双方必须是同一类型为前提的条件下才会有效。

如果你想了解更多关于强制类型转换的信息,你可以读一读 Dmitry Soshnikov 的这篇文章。

在只使用 == 的情况下,JavaScript 所带来的强制类型转换使得判断结果跟踪变得复杂,下面的例子可以看出这样的结果有多怪了:
在这里插入图片描述(5)明智地使用真假判断
当我们在一个 if 条件语句中使用变量或表达式时,会做真假判断。if(a == true) 是不同于 if(a) 的。后者的判断比较特殊,我们称其为真假判断。这种判断会通过特殊的操作将其转换为 true 或 false,下列表达式统统返回 false:false, 0, undefined, null, NaN, ‘’(空字符串).

这种真假判断在我们只求结果而不关心过程的情况下,非常的有帮助。

以下示例展示了真假判断是如何工作的:
在这里插入图片描述(6)变量赋值时的逻辑操作
逻辑操作符 || 和 && 也可被用来返回布尔值。如果操作对象为非布尔对象,那每个表达式将会被自左向右地做真假判断。基于此操作,最终总有一个表达式被返回回来。这在变量赋值时,是可以用来简化你的代码的。
不推荐
在这里插入图片描述推荐
在这里插入图片描述
未完待续。。。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值