JavaScript 进阶之道

Python 和 JavaScript 在笔者看来是很相似的语言,本文归纳了 JavaScript 的各种 tricks。

函数

箭头函数

函数的简化写法,配合 map、filter、reduce 等高阶函数实现函数式编程

// function foo(parameters){
//     return expression
// }
let foo = parameters => expression;

map - 映射

let numbers = [1, 2, 3, 4, 5];
numbers.map(e => e ** 2);
// [1, 4, 9, 16, 25]

filter - 过滤

let values = [null, undefined, NaN, 0, "", true, "alphardex", 666];
values.filter(e => e);
// [true, "alphardex", 666]

reduce - 归并

let numbers = [1, 2, 3, 4, 5];
numbers.reduce((acc, cur) => acc + cur);
// 15

扩展运算符

数据结构的合并

let arr1 = ['kaguya', 'miyuki']
let arr2 = ['chika', 'ishigami']
[...arr1, ...arr2]
// ['kaguya', 'miyuki', 'chika', 'ishigami']
let obj1 = {'name': 'rimuru'}
let obj2 = {'kind': 'slime'}
{...obj1, ...obj2}
// {name: 'rimuru', kind: 'slime'}

函数参数的打包与解包

// 打包
let foo = (...args) => console.log(args);
foo(1, 2);
// [1, 2]
// JS只能打包位置参数,无法正确地打包关键词参数
// foo(name='hayasaka', job='maid')
// ["hayasaka", "maid"] (keyword没了)

// 解包
let divmod = (a, b) => [Math.floor(a / b), a % b];
let t = [10, 3];
let [quotient, remainder] = divmod(...t);
quotient;
// 商:3
remainder;
// 余:1

数据结构

数组

迭代

let li = ["umaru", "ebina", "tachibana"];
for (let e of li) {
  console.log(e);
}
// umaru ebina tachibana

同时迭代元素与其索引

相当于 Python 的 enumerate

let li = ["umaru", "ebina", "tachibana"];
li.map((e, i) => `${i + 1}. ${e}`);
// ['1. umaru', '2. ebina', '3. tachibana']

同时迭代 2 个以上的数组

相当于 Python 的 zip

let subjects = ["nino", "miku", "itsuki"];
let predicates = ["saikou", "ore no yome", "is sky"];
subjects.map((e, i) => `${e} ${predicates[i]}`);
// ["nino saikou", "miku ore no yome", "itsuki is sky"]

测试是否整体/部分满足条件

every 测试所有元素是否都满足于某条件,some 则是测试部分元素是否满足于某条件

[1, 2, 3, 4].every(e => e < 20);
// true

[1, 3, 4, 5].some(e => e % 2 === 0);
// true

解构赋值

最典型的例子就是 2 数交换

let [a, b] = [b, a];

用扩展运算符可以获取剩余的元素

let [first, ...rest] = [1, 2, 3, 4];
first;
// 1
rest;
// [2, 3, 4]

对象

迭代

let obj = { name: "sekiro", hobby: "blacksmithing", tendency: "death" };
Object.keys(obj);
// ["name", "hobby", "tendency"]
Object.values(obj);
// ["sekiro", "blacksmithing", "death"]
Object.entries(obj).map(([key, value]) => `${key}: ${value}`);
// ["name: sekiro", "hobby: blacksmithing", "tendency: death"]

排序

let data = [{ rank: 2, author: "beta" }, { rank: 1, author: "alpha" }];
data.sort((a, b) => a.rank - b.rank);
// [{'rank': 1, 'author': 'alpha'}, {'rank': 2, 'author': 'beta'}]

缺失键处理

如果键不在字典中,返回一个默认值,类似 Python 的 dict.get

let obj = { name: "okabe rintaro", motto: "elpsycongroo" };
obj.job || "mad scientist";
// mad scientist

如果键不在字典中,将会添加它并设置一个默认值,类似 Python 的 dict.setdefault

let obj = { name: "okabe rintaro", motto: "elpsycongroo" };
obj.job = obj.job || "mad scientist";
// "mad scientist"
obj;
// {name: "okabe rintaro", motto: "elpsycongroo", job: "mad scientist"}

语言专属特性

async

async 函数返回隐式的 Promise,是异步代码的理想的编写方式

let sleep = time => new Promise(resolve => setTimeout(resolve, time));

async function main() {
  console.log(`开始:${new Date()}`);
  await sleep(1000);
  console.log(`结束:${new Date()}`);
}

main();

假设定义了 3 个异步操作任务

async function task1() {
  console.log("task 1 start");
  await sleep(1000);
  console.log("task 1 end");
}

async function task2() {
  console.log("task 2 start");
  await sleep(1000);
  console.log("task 2 end");
}

async function task3() {
  console.log("task 3 start");
  await sleep(1000);
  console.log("task 3 end");
}

顺序执行

async function main() {
  const tasks = [task1, task2, task3];
  for (let i = 0; i < tasks.length; i++) {
    await tasks[i]();
  }
}

main();
// task 1 start
// task 1 end
// task 2 start
// task 2 end
// task 3 start
// task 3 end

并发执行

function main() {
  const tasks = [task1, task2, task3];
  tasks.forEach(task => task());
}

main();
// task 1 start
// task 2 start
// task 3 start
// task 1 end
// task 2 end
// task 3 end

任务完成后执行某操作

async function main() {
  await Promise.all([task1(), task2(), task3()]);
  console.log("all complete!");
}

main();
// task 1 start
// task 2 start
// task 3 start
// task 1 end
// task 2 end
// task 3 end
// all complete!

Proxy

如果了解 Python 类的描述符特性,那么 Proxy 也会变得很好理解

在 Python 中, 类的描述符可以给类属性定义相同的存取行为

而 JS 的 Proxy 也差不多,也是给对象属性定义相同的存取行为(官方说是拦截读取操作,其实意思也差不多)

以下实现一个简单的 Proxy,用来在读写属性时验证属性的正确性

let validator = {
  set: (obj, key, value) => {
    if (key === "age") {
      if (!Number.isInteger(value)) {
        throw new TypeError(`${key} must be an Integer`);
      }
      if (value > 200) {
        throw new RangeError(`${key} must be under 200`);
      }
    }
    obj[key] = value;
    return true;
  }
};

class Person {
  constructor(age) {
    this.age = age;
    return new Proxy(this, validator);
  }
}

let person = new Person((age = 100));
person.age = "young";
// Uncaught TypeError: age must be an Integer
person.age = 201;
// Uncaught RangeError: age must be under 200

Last updated

Was this helpful?