[译]<<Effective TypeScript>> 技巧58:编写现代的js

201 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情

本文的翻译于<<Effective TypeScript>>, 特别感谢!! ps: 本文会用简洁, 易懂的语言描述原书的所有要点. 如果能看懂这文章,将节省许多阅读时间. 如果看不懂,务必给我留言, 我回去修改.

技巧58:编写现代的js

ts的编译器可以将你的ts代码编译成任意版本的js。ts支持最新的js语法,以为你可以使用最新的js的功能。

使用 ECMAScript Modules

在es2015版本之前,js没有标准划分模块的方法。市面上有许多解决方案:

  1. 多个<script> 标签
  2. Makefiles 的node风格的 require声明
  3. AMD风格的 define回调
  4. ts的风格的模块(见技巧53) 现在有了标准的es modules: import 和export法。现在是时候转成es modules。因为webpack,ts,ts-node等工具都非常好的兼容es modules。

如果你的CommonJS长这样:

// CommonJS
// a.js
const b = require('./b');
console.log(b.name);

// b.js
const name = 'Module B';
module.exports = {name};

那你的es modules长这样:

// ECMAScript module
// a.ts
import * as b from './b';
console.log(b.name);

// b.ts
export const name = 'Module B';

使用Classes而不是Prototypes

class在es2015之后被引入,如果你的代码使用了prototypes:

function Person(first, last) {
  this.first = first;
  this.last = last;
}

Person.prototype.getName = function() {
  return this.first + ' ' + this.last;
}

const marie = new Person('Marie', 'Curie');
const personName = marie.getName();

转成class:

class Person {
  first: string;
  last: string;

  constructor(first: string, last: string) {
    this.first = first;
    this.last = last;
  }

  getName() {
    return this.first + ' ' + this.last;
  }
}

const marie = new Person('Marie', 'Curie');
const personName = marie.getName();

TypeScript与Person的prototype版本存在矛盾,但理解基于class的版本时只需要很少的注释。

ts提供了工具让你快速的将prototype转成class:

image.png

使用let、const而不是var

js的var有非常奇怪的作用域规则。最好使用let,const代替var。 嵌套的function申明和var有同样的作用域规则:

function foo() {
  bar();
  function bar() {
    console.log('hello');
  }
}

当你调用foo(),会打印 hello 因为bar被变量提升到了foo()的上面。所以最好采用箭头函数。

使用 for-of 或者数组方法,而不是for(;;)

传统js的循环用的是C语言的风格:

for (var i = 0; i < array.length; i++) {
  const el = array[i];
  // ...
}

现代的js,你应该用for-of代替:

for (const el of array) {
  // ...
}

也可以用数组的遍历方法:

array.forEach((el, i) => {
  // ...
});

优先使用箭头函数而不function

this是js著名的糟粕。他有着不同的scoping规则:

class Foo {
  method() {
    console.log(this);
    [1, 2].forEach(function(i) {
      console.log(this);
    });
  }
}
const f = new Foo();
f.method();
// Prints Foo, undefined, undefined in strict mode
// Prints Foo, window, window (!) in non-strict mode

一般而言你想要this指向class的实例,箭头函数可以实现这一点:

class Foo {
  method() {
    console.log(this);
    [1, 2].forEach(i => {
      console.log(this);
    });
  }
}
const f = new Foo();
f.method();
// Always prints Foo, Foo, Foo

尽可能使用箭头函数。打开noImplicitThis的编译选项。ts将帮助你this有正确的绑定。

使用压缩对象文字和结构赋值

不要这么写:

const x = 1, y = 2, z = 3;
const pt = {
  x: x,
  y: y,
  z: z
};

而是应该:

const x = 1, y = 2, z = 3;
const pt = { x, y, z };

为了变得更精确,应该对变量和属性应该采用一致性命名。 为了返回对象问题在箭头函数中,应该用括号包裹起来:

['A', 'B', 'C'].map((char, idx) => ({char, idx}));
// [ { char: 'A', idx: 0 },  { char: 'B', idx: 1 }, { char: 'C', idx: 2 } ]

属性是函数也应该简写:

const obj = {
  onClickLong: function(e) {
    // ...
  },
  onClickCompact(e) {
    // ...
  }
};

不要这么写:

const props = obj.props;
const a = props.a;
const b = props.b;

解构赋值的写法:

const {props} = obj;
const {a, b} = props;

甚至:

const {props: {a, b}} = obj;

需要对解构赋值设置默认值,不要这么写:

let {a} = obj.props;
if (a === undefined) a = 'default';

而是这么写:

const {a = 'default'} = obj.props;

也可以对数组进行解构赋值:

const point = [1, 2, 3];
const [x, y, z] = point;
const [, a, b] = point;  // Ignore the first one

函数参数中也可以使用解构赋值:

const points = [
  [1, 2, 3],
  [4, 5, 6],
];
points.forEach(([x, y, z]) => console.log(x + y + z));
// Logs 6, 15

使用默认的函数参数

在js中参数是可传可不传:

function log2(a, b) {
  console.log(a, b);
}
log2();

输出:

undefined undefined

常常这么添加参数默认值:

function parseNum(str, base) {
  base = base || 10;
  return parseInt(str, base);
}

在现代的js中,这么添加默认值:

function parseNum(str, base=10) {
  return parseInt(str, base);
}

这种写法还有助于ts进行类型推断

使用async/await而不是Promises或者回调函数

技巧25解释了为什么要使用async,await。不要这么写:

function getJSON(url: string) {
  return fetch(url).then(response => response.json());
}
function getJSONCallback(url: string, cb: (result: unknown) => void) {
  // ...
}

而是这么写:

async function getJSON(url: string) {
  const response = await fetch(url);
  return response.json();
}

不要在ts中使用strict

es5引入了「strict mode」 ,让一些可疑模式报错:

use strict';
function foo() {
  x = 10;  // Throws in strict mode, defines a global in non-strict.
}

不要在ts中使用「strict mode」 ,因为ts执行的检查比它更完善。 \