关于一些我不知道的 js 的东西

本文翻译自 https://air.ghost.io/js-things-i-never-knew-existed/ ,作者 Nick, 翻译:Thomas Chan

那天我通读了一遍 MDN 文档然后发现了一些我从来不知道的功能和 API。我简单列了一些有用的没用的 - 看来 js 永远学不完啊。

Label 语句

我从来不知道 js 还能给 for 循环和代码块命名…! 命名可以被使用在 for 循环里的 break, continue 或者代码块里的 break 后边。

loop1: // 命名 "loop1"
for (let i = 0; i < 3; i++) { // "loop1"
   loop2: // 命名 "loop2"
   for (let j = 0; j < 3; j++) { // "loop2"
      if (i === 1) {
         continue loop1; // 继续 "loop1"
         // break loop1; // 结束 "loop1"
      }
      console.log(`i = ${i}, j = ${j}`);
   }
}

/*
 * # Output
 * i = 0, j = 0
 * i = 0, j = 1
 * i = 0, j = 2
 * i = 2, j = 0
 * i = 2, j = 1
 * i = 2, j = 2
 */

下边是给代码块命名的例子,在代码块里只能使用 break

foo: {
  console.log('one');
  break foo;
  console.log('this log will not be executed');
}
console.log('two');

/*
 * # Output
 * one
 * two
 */

“void” 运算符

我以为我了解所有的运算符直到我看见这个 1996 就已经在 js 里的运算符。所有的浏览器都支持 void,这个运算符很好理解,引用 MDN 的解释:

void 运算符执行后边给出的表达式并且返回 undefined

所以用 void 也可以像这样写一个自执行函数:

void function iife() {
  console.log('hello');
}();

// 等价于...

(function iife() {
    console.log('hello');
})()

有一点不好是 void 执行表达式相当于 void (undefined)!

const word = void function iife() {
  return 'hello';
}();

// word 等于 "undefined"

const word = (function iife() {
  return 'hello';
})();

// word 等于 "hello"

void 也可以跟 async 一起用:

void async function() {
    try {
        const response = await fetch('air.ghost.io');
        const text = await response.text();
        console.log(text);
    } catch(e) {
        console.error(e);
    }
}()

// or just stick to this :)

(async () => {
    try {
        const response = await fetch('air.ghost.io');
        const text = await response.text();
        console.log(text);
    } catch(e) {
        console.error(e);
    }
})();

逗号运算符

读了文档才知道以前并不是都了解了。看 MDN 的解释:

逗号运算符从左到右执行每个表达式,并且返回最后一个表达式的值。

function myFunc() {
  let x = 0;
  return (x += 1, x); // 等价于 ++x;
}

y = false, true; // console 里打印出来 true
console.log(y); // false

z = (false, true); // console 里打印出来 true
console.log(z); // true

跟三目运算符一起用

因为逗号运算符返回最后一个表达式的值这个特性,在三目运算符里你就可以在返回之前执行 n 个表达式,下边的例子里我放了一个 console.log 在返回布尔值之前:

const type = 'man';

const isMale = type === 'man' ? (
    console.log('Hi Man!'),
    true
) : (
    console.log('Hi Lady!'),
    false
);

console.log(`isMale is "${isMale}"`);

译者注:逗号运算符在实际写代码中不太常用,因为代码是写给人看的,你可以在压缩后的代码里看到大把大把的逗号运算。

国际化 API

国际化还没有完全完善,不过现在绝大部分浏览器已经支持。其中我最喜欢的一个功能是日期的格式化,看例子:

const date = new Date();

const options = {
  year: 'numeric',
  month: 'long',
  day: 'numeric'
};

const formatter1 = new Intl.DateTimeFormat('es-es', options);
console.log(formatter1.format(date)); // 22 de diciembre de 2017

const formatter2 = new Intl.DateTimeFormat('en-us', options);
console.log(formatter2.format(date)); // December 22, 2017

管道操作符

写这篇文章的时候只有 Firefox 58+ 支持(要开启实验性 API),不过 Babel 已经提议了一个转换插件。写起来很像 bash,我很喜欢!

const square = (n) => n * n;
const increment = (n) => n + 1;

// 不使用管道符
square(increment(square(2))); // 25

// 使用管道符
2 |> square |> increment |> square; // 25

值得注意的几个

原子操作

多个共享内存的线程能够同时读写同一位置上的数据。原子操作会确保正在读或写的数据的值是符合预期的,即下一个原子操作一定会在上一个原子操作结束后才会开始,其操作过程不会中断。常用在线程之间保持数据同步,比如浏览器主线程和 WebWorker。
我很喜欢 Java 里的原子操作。我觉得等有更多的人把繁重操作从主线程移到 WebWorker 的时候这个功能才会用的比较多吧。

Array.prototype.reduceRight

好吧我从来没见用过这个函数,基本上就是 Array.prototype.reduce() + Array.prototype.reverse(),而且这个操作很少见。reduceRight 也可以用来展开数组(译者注:这真是挺鸡肋的...):

const flattened = [[0, 1], [2, 3], [4, 5]].reduceRight(function(a, b) {
    return a.concat(b);
}, []);

// flattened array is [4, 5, 2, 3, 0, 1]

setTimeout() 参数

知道了这个可能会让我少些一两个 .bind(...)

setTimeout(alert, 1000, 'Hello world!');

/*
 * # Output (alert)
 * Hello World!
 */

function log(text, textTwo) {
    console.log(text, textTwo);
}

setTimeout(log, 1000, 'Hello World!', 'And Mars!');

/*
 * # Output
 * Hello World! And Mars!
 */

结语

希望你在这个简短的列表里像我一样学到了一点新的东西。顺便给 Mozilla 的新 MDN 网站打 call,我觉得比以前好看多了 - 通读一遍花的时间比我想的要多的多。

新年快乐!2018 你好!