持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情
本文的翻译于<<Effective TypeScript>>, 特别感谢!! ps: 本文会用简洁, 易懂的语言描述原书的所有要点. 如果能看懂这文章,将节省许多阅读时间. 如果看不懂,务必给我留言, 我回去修改.
技巧58:编写现代的js
ts的编译器可以将你的ts代码编译成任意版本的js。ts支持最新的js语法,以为你可以使用最新的js的功能。
使用 ECMAScript Modules
在es2015版本之前,js没有标准划分模块的方法。市面上有许多解决方案:
- 多个<script> 标签
- Makefiles 的node风格的 require声明
- AMD风格的 define回调
- 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:
使用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执行的检查比它更完善。 \