整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

让Chrome“滚”得更流畅:微软推荐Edge HTML风格滚动特性

T之家12月9日消息 微软对Chromium开源项目非常感兴趣,因为该项目对Edge和Chrome都有利。微软最新的功能请求之一就是希望通过部署Edge HTML风格的滚动特性从而让Chromium的滚动变得更加灵敏。

微软打算将Impulse样式(即EdgeHTML样式)滚动动画加入到Chromium中,微软已经将该滚动动画移植到了基于Chromium的Edge浏览器的Dev Canary通道中。默认情况下,Edge浏览器中启用了脉冲样式的滚动动画。对于Chrome,该功能可能会在未来几天内出现在试验版浏览器中。

微软表示,Impulse-style (也就是EdgeHTML-style)滚动动画将提供更灵敏的滚动体验,用户启用后鼠标滚轮的每个刻度都试图模仿基于物理的内容,内容会开始快速移动然后逐渐变慢。换句话说,由于开始时的快速加速,该模式会给人一种更灵敏的感觉。

此外微软还在推行另一项称之为“percent-based scrolling”(基于百分比的滚动)的滚动方案,该模式允许浏览器将鼠标滚轮或者键盘滚动解释为预期滚动条的百分比。微软目前正努力将经典版Edge浏览器的优秀特性移植到Chromium平台上,包括这项基于百分比的滚动方式。

码必须尽可能的清晰和易读。

这实际上是一种编程艺术 —— 以一种正确并且人们易读的方式编码来完成一个复杂的任务。一个良好的代码风格大大有助于实现这一点。

一、语法

下面是一个备忘单,其中列出了一些建议的规则(详情请参阅下文):

现在,让我们详细讨论一下这些规则和它们的原因吧。

没有什么规则是“必须”的

没有什么规则是“刻在石头上”的。这些是风格偏好,而不是宗教教条。

二、花括号

在大多数的 JavaScript 项目中,花括号以 “Egyptian” 风格(译注:“egyptian” 风格又称 K&R 风格 — 代码段的开括号位于一行的末尾,而不是另起一行的风格)书写,左花括号与相应的关键词在同一行上 — 而不是新起一行。左括号前还应该有一个空格,如下所示:

if (condition) {
  // do this
  // ...and that
  // ...and that
}

单行构造(如 if (condition) doSomething())也是一个重要的用例。我们是否应该使用花括号?如果是,那么在哪里?

下面是这几种情况的注释,你可以自己判断一下它们的可读性:

  1. 初学者常这样写。非常不好!这里不需要花括号:if (n < 0) {alert(`Power ${n} is not supported`);}
  2. 拆分为单独的行,不带花括号。永远不要这样做,添加新行很容易出错:if (n < 0) alert(`Power ${n} is not supported`);
  3. 写成一行,不带花括号 — 如果短的话,也是可以的:if (n < 0) alert(`Power ${n} is not supported`);
  4. 最好的方式:if (n < 0) { alert(`Power ${n} is not supported`); }

对于很短的代码,写成一行是可以接受的:例如 if (cond) return null。但是代码块(最后一个示例)通常更具可读性。

三、行的长度

没有人喜欢读一长串代码,最好将代码分割一下。

例如:

// 回勾引号 ` 允许将字符串拆分为多行
let str = `
  ECMA International's TC39 is a group of JavaScript developers,
  implementers, academics, and more, collaborating with the community
  to maintain and evolve the definition of JavaScript.
`;

对于 if 语句:

if (
  id === 123 &&
  moonPhase === 'Waning Gibbous' &&
  zodiacSign === 'Libra'
) {
  letTheSorceryBegin();
}

一行代码的最大长度应该在团队层面上达成一致。通常是 80 或 120 个字符。

四、缩进

有两种类型的缩进:

  • 水平方向上的缩进:2 或 4 个空格。一个水平缩进通常由 2 或 4 个空格或者 “Tab” 制表符(key Tab)构成。选择哪一个方式是一场古老的圣战。如今空格更普遍一点。选择空格而不是 tabs 的优点之一是,这允许你做出比 “Tab” 制表符更加灵活的缩进配置。例如,我们可以将参数与左括号对齐,像下面这样:
show(parameters,
     aligned, // 5 spaces padding at the left
     one,
     after,
     another
  ) {
  // ...
}
  • 垂直方向上的缩进:用于将代码拆分成逻辑块的空行。即使是单个函数通常也被分割为数个逻辑块。在下面的示例中,初始化的变量、主循环结构和返回值都被垂直分割了:
function pow(x, n) {
  let result = 1;
  //              <--
  for (let i = 0; i < n; i++) {
    result *= x;
  }
  //              <--
  return result;
}


插入一个额外的空行有助于使代码更具可读性。写代码时,不应该出现连续超过 9 行都没有被垂直分割的代码。

五、分号

每一个语句后面都应该有一个分号。即使它可以被跳过。

有一些编程语言的分号确实是可选的,那些语言中也很少使用分号。但是在 JavaScript 中,极少数情况下,换行符有时不会被解释为分号,这时代码就容易出错。更多内容请参阅 代码结构 一章的内容。

如果你是一个有经验的 JavaScript 程序员,你可以选择像 StandardJS 这样的无分号的代码风格。否则,最好使用分号以避免可能出现的陷阱。大多数开发人员都应该使用分号。

六、嵌套的层级

尽量避免代码嵌套层级过深。

例如,在循环中,有时候使用 continue 指令以避免额外的嵌套是一个好主意。

例如,不应该像下面这样添加嵌套的 if 条件:

for (let i = 0; i < 10; i++) {
  if (cond) {
    ... // <- 又一层嵌套
  }
}

我们可以这样写:

for (let i = 0; i < 10; i++) {
  if (!cond) continue;
  ...  // <- 没有额外的嵌套
} //多用这种风格。

使用 if/elsereturn 也可以做类似的事情。

例如,下面的两个结构是相同的。

第一个:

function pow(x, n) {
  if (n < 0) {
    alert("Negative 'n' not supported");
  } else {
    let result = 1;

    for (let i = 0; i < n; i++) {
      result *= x;
    }

    return result;
  }
}

第二个:

function pow(x, n) {
  if (n < 0) {
    alert("Negative 'n' not supported");
    return;
  }

  let result = 1;

  for (let i = 0; i < n; i++) {
    result *= x;
  }

  return result;
}

但是第二个更具可读性,因为 n < 0 这个“特殊情况”在一开始就被处理了。一旦条件通过检查,代码执行就可以进入到“主”代码流,而不需要额外的嵌套。

七、函数位置

如果你正在写几个“辅助”函数和一些使用它们的代码,那么有三种方式来组织这些函数。

  1. 在调用这些函数的代码的 上方 声明这些函数:
// function declarations
function createElement() {
  ...
}

function setHandler(elem) {
  ...
}

function walkAround() {
  ...
}

// the code which uses them
let elem = createElement();
setHandler(elem);
walkAround();
  1. 先写调用代码,再写函数
// the code which uses the functions
let elem = createElement();
setHandler(elem);
walkAround();

// --- helper functions ---
function createElement() {
  ...
}

function setHandler(elem) {
  ...
}

function walkAround() {
  ...
}
  1. 混合:在第一次使用一个函数时,对该函数进行声明。

大多数情况下,第二种方式更好。

这是因为阅读代码时,我们首先想要知道的是“它做了什么”。如果代码先行,那么在整个程序的最开始就展示出了这些信息。之后,可能我们就不需要阅读这些函数了,尤其是它们的名字清晰地展示出了它们的功能的时候。

八、风格指南

风格指南包含了“如果编写”代码的通用规则,例如:使用哪个引号、用多少空格来缩进、一行代码最大长度等非常多的细节。

当团队中的所有成员都使用相同的风格指南时,代码看起来将是统一的。无论是团队中谁写的,都是一样的风格。

当然,一个团队可以制定他们自己的风格指南,但是没必要这样做。现在已经有了很多制定好的代码风格指南可供选择。

一些受欢迎的选择:

  • Google JavaScript 风格指南
  • Airbnb JavaScript 风格指南
  • Idiomatic.JS
  • StandardJS
  • 还有很多……

如果你是一个初学者,你可以从本章中上面的内容开始。然后你可以浏览其他风格指南,并选择一个你最喜欢的。

九、自动检查器

检查器(Linters)是可以自动检查代码样式,并提出改进建议的工具。

它们的妙处在于进行代码风格检查时,还可以发现一些代码错误,例如变量或函数名中的错别字。因此,即使你不想坚持某一种特定的代码风格,我也建议你安装一个检查器。

下面是一些最出名的代码检查工具:

  • JSLint — 第一批检查器之一。
  • JSHint — 比 JSLint 多了更多设置。
  • ESLint — 应该是最新的一个。

它们都能够做好代码检查。我使用的是 ESLint。

大多数检查器都可以与编辑器集成在一起:只需在编辑器中启用插件并配置代码风格即可。

例如,要使用 ESLint 你应该这样做:

  1. 安装 Node.JS。
  2. 使用 npm install -g eslint 命令(npm 是一个 JavaScript 包安装工具)安装 ESLint。
  3. 在你的 JavaScript 项目的根目录(包含该项目的所有文件的那个文件夹)创建一个名为 .eslintrc 的配置文件。
  4. 在集成了 ESLint 的编辑器中安装/启用插件。大多数编辑器都有这个选项。

下面是一个 .eslintrc 文件的例子:

{
  "extends": "eslint:recommended",
  "env": {
    "browser": true,
    "node": true,
    "es6": true
  },
  "rules": {
    "no-console": 0,
    "indent": 2
  }
}

这里的 "extends" 指令表示我们是基于 “eslint:recommended” 的设置项而进行设置的。之后,我们制定我们自己的规则。

你也可以从网上下载风格规则集并进行扩展。有关安装的更多详细信息

此外,某些 IDE 有内置的检查器,这非常方便,但是不像 ESLint 那样可自定义。

十、总结

本文描述的(和提到的代码风格指南中的)所有语法规则,都旨在帮助你提高代码可读性。它们都是值得商榷的。

当我们思考如何写“更好”的代码的时候,我们应该问自己的问题是:“什么可以让代码可读性更高,更容易被理解?”和“什么可以帮助我们避免错误?”这些是我们讨论和选择代码风格时要牢记的主要原则。

阅读流行的代码风格指南,可以帮助你了解有关代码风格的变化趋势和最佳实践的最新想法。

篇文章我们将讨论如何将新的语法应用在编码实践当中,与传统的 JavaScript 语法结合在一起,写出合理的、易于阅读和维护的代码。

所谓"编程风格",指的是编写代码的规则。不同的程序员,往往有不同的编程风格。而且这里的风格包括语法上的编程风格格式上的编程风格。

一.块级作用域let 取代 var - 语法上的编程风格

1.ES6 提出了两个新的声明变量的命令:let和const。

其中,let完全可以取代var,因为两者语义相同,而且let没有副作用。

if (true) {
    let x = 'hello';
}

for (let y = 0; y <= 10; y++) {
    console.log(y);
}


if (true) {
    var x = 'hello';//全局变量x
}

for (var y = 0; y <= 10; y++) {//全局变量y
    console.log(y);
}

上面代码如果用var替代let,实际上就声明了两个全局变量,这显然不是理想的设计方式。变量应该只在其声明的代码块内有效,var命令做不到这一点。


2.var命令存在变量提升效用,let命令没有这个问题。

if (true) {
    console.log(x); // ReferenceError引用错误
    let x = 'hello';
}

if (true) {
    console.log(x); // undefined,变量提升
    var x = 'hello';
}


3.在let和const之间,建议优先使用const,尤其是在全局环境,不应该设置变量,应设置常量。原因是const可以提醒阅读程序的人,这个变量不能改变,比较符合函数式编程思想,并且JavaScript 编译器会对const进行优化,所以多使用const,有利于提高程序的运行效率。

//传统的方式
var a = 1,
b = 2,
c = 3;

//新的方式
const a = 1;
const b = 2;
const c = 3;

//更优的方式
const [a, b, c] = [1, 2, 3];


二、字符串 静态字符串一律使用单引号或反引号,不使用双引号。动态字符串使用反引号。

//不建议
const a = "foobar";
const b = 'foo' + a + 'bar';

//建议
const a = 'foobar';
const b = `foo${a}bar`;


三.解构赋值

ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构

使用数组成员对变量赋值时,优先使用解构赋值。
const arr = [1, 2, 3, 4];
// 不建议
const first = arr[0];
const second = arr[1];

// 建议
const [first, second] = arr;


函数的参数如果是对象的成员,优先使用解构赋值。
// 不建议
function getFullName(user) {
  const firstName = user.firstName;
  const lastName = user.lastName;
}
// 建议
function getFullName(obj) {
  const { firstName, lastName } = obj;
}
// 建议
function getFullName({ firstName, lastName }) {
}


四.其他操作

1.对象的操作

对象尽量静态化,一旦定义,就不得随意添加新的属性。如果添加属性不可避免,要使用Object.assign方法。

// 不建议
const a = {};
a.x = 3;

// 建议
const a = {};
Object.assign(a, { x: 3 });
// 或者这样
const a = { x: null };
a.x = 3;

对象的属性和方法,尽量采用简洁表达法

var ref = 'some value';
// 不推荐
const atom = {
  ref: ref,
  value: 1,
  addValue: function (value) {
    return atom.value + value;
  },
};

// 推荐
const atom = {
  ref,
  value: 1,
  addValue(value) {
    return atom.value + value;
  },
};

2.使用扩展运算符(...)拷贝数组

let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
let arr3 = [7, 8, 9];
console.log(arr1.concat(arr2, arr3)); //[1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log([...arr1, ...arr2, ...arr3]); //[1, 2, 3, 4, 5, 6, 7, 8, 9]

3.立即执行函数可以写成箭头函数的形式。

(() => {
  console.log('Welcome to the Internet');
})();

4.用 Class取代prototype 的操作。因为 Class 的写法更简洁,更易于理解。

// 构造函数+原型
function Person(name, age, sex) {
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.showinfo = function() { 
        return this.name;
    }
}

Person.prototype.showinfo = function() { 
    return this.name + this.age + this.sex
}

// class
class Person {
    constructor(name, age, sex) {
        this.name = name; 
        this.age = age;
        this.sex = sex;
    }
    showinfo() { 
        return this.name
    }
}

5.ESLint 的使用

ESLint 是一个语法规则和代码风格的检查工具,可以用来保证写出语法正确、风格统一的代码。

首先,在项目的根目录安装 ESLint。

$ npm install --save-dev eslint

然后,安装 Airbnb 语法规则,以及 import、a11y、react 插件。

$ npm install --save-dev eslint-config-airbnb
$ npm install --save-dev eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-react

最后,在项目的根目录下新建一个.eslintrc文件,配置 ESLint。

{
  "extends": "eslint-config-airbnb"
}

现在就可以检查,当前项目的代码是否符合预设的规则。

index.js文件的代码如下。

var unused = 'I have no purpose!';

function greet() {
    var message = 'Hello, World!';
    console.log(message);
}

greet(); 使用 ESLint 检查这个文件,就会报出错误。

$ npx eslint index.js
index.js
  1:1  error  Unexpected var, use let or const instead          no-var
  1:5  error  unused is defined but never used                 no-unused-vars
  4:5  error  Expected indentation of 2 characters but found 4  indent
  4:5  error  Unexpected var, use let or const instead          no-var
  5:5  error  Expected indentation of 2 characters but found 4  indent

✖ 5 problems (5 errors, 0 warnings)

上面代码说明,原文件有五个错误,其中两个是不应该使用var命令,而要使用let或const;一个是定义了变量,

却没有使用;另外两个是行首缩进为 4 个空格,而不是规定的 2 个空格。


五.括号的位置 - 语法格式上的编程风格

1.大括号的位置

绝大多数的编程语言,都用大括号({})表示代码块。对于起首的大括号的位置,有许多不同的写法。

最流行的有两种。

第一种是起首的大括号另起一行 - 推荐

block{

}

第二种是起首的大括号跟在关键字的后面,但是Javascript会自动添加句末的分号,有可能会导致一些难以察觉的错误。

//下面的情况就会产生问题。
function fn() {
    return 
    {  
        key: value
    };
}

2.圆括号

圆括号在Javascript中有两种作用,一种表示调用函数,另一种表示不同的值的组合。我们可以用空格,区分这两

种不同的括号。

调用函数的时候,函数名与左括号之间没有空格。

function fn(){}
fn()

函数名与参数序列之间,没有空格。

function fn(x,y){
    return x + y;
}
fn(1,2)

所有其他语法元素与左括号之间,都有一个空格

if (a === 0){...}


3.分号

分号表示语句的结束。大多数情况下,如果你省略了句尾的分号,Javascript会自动添加。

但麻烦的是,如果下一行的第一个符号是下面这五个字符之一,Javascript将不对上一行句尾添加分号:"("、"["、"/"、"+"和"-"。


4.相等和严格相等(恒等)

Javascript有两个表示"相等"的运算符:"相等"(==)和"严格相等"(===)。

因为"相等"运算符会自动转换变量类型(隐式转换),这样写会造成很多意想不到的情况。


所有变量声明都放在函数的头部。

所有函数都在使用之前定义。