我不曾见过的 JS 特性

我不曾见过的 JS 特性

有一天我正在阅读 MDN 文档,发现了一些我以前压根没有意识到在 JS 中存在的特性和 API。这里我罗列了一些,无论它们是否有用,JS 的学习永无止境。前端

标记语句

有多少人知道在 JS 里你能够给 for 循环和语句块命名?反正我不知道…… 命名完新名称以后你能够在 for 循环中的 breakcontinue 以后、语句块中的 break 以后使用新名称。android

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}`);
   }
}

/* 
 * # 输出
 * 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 以后使用新命名。ios

foo: {
  console.log('one');
  break foo;
  console.log('这句打印不会被执行');
}
console.log('two');

/*
 * # 输出
 * one
 * two
 */
复制代码

"void" 运算符

我一度觉得我已经了解了全部的运算符,直到我看到了这一个。它从 1996 年 起就存在于 JS 了。全部的浏览器都支持,而且它也很容易理解,引用自 MDN:git

void 运算符对给定的表达式进行求值,而后返回 undefined。github

使用它,你能够换一种方式来写当即调用的函数表达式(IIFE),就像这样:后端

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"
复制代码

你也能够和 async 一块儿使用 void,这样你就能把函数做为异步代码的入口:bash

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

// 或者保持下面的写法

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

逗号运算符

在学习了逗号运算符以后,我意识到了以前我并不彻底清楚其工做原理。下面是来自 MDN 的引用:babel

逗号运算符对它的每一个操做数求值(从左到右),并返回最后一个操做数的值。异步

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

y = false, true; // console 中获得 true
console.log(y); // false,逗号优先级低于赋值

z = (false, true); // console 中获得 true
console.log(z); // true,括号中总体返回 true
复制代码

配合 条件运算符

逗号运算符中的最后一个值做为返回给条件运算符的值,所以你能够在最后一个值前面听任意多个表达式。在下面的例子中,我在返回的布尔值以前放了打印语句。

const type = 'man';

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

console.log(`isMale is "${isMale}"`);
复制代码

国际化 API

即便在最有利的状况下,国际化仍是很难作好。幸亏还有一套大部分浏览器都支持得不错的 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 及以上版本经过传入启动参数来支持,不过 Babel 已经有一个针对它的 插件提议。它看起来应该是受到 bash 的启发,我以为很棒!

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

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

// 使用管道操做符
2 |> square |> increment |> square; // 25
复制代码

值得一提

Atomics

当数据被多个线程共享时,原子操做确保正在读和写的数据是符合预期的,即下一个原子操做必定会在上一个原子操做结束以后才会开始。这有利于保持不一样线程间的数据同步(好比主线程和另外一条 WebWorker 线程)。

我很喜欢如 Java 等其它语言中的原子性。我预感当愈来愈多的人使用 WebWorkers,将操做从主线程分离出来时,原子操做的使用会愈来愈普遍。

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!');

/*
 * # alert 输出
 * Hello World!
 */

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

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

/*
 * # 输出
 * Hello World! And Mars!
 */
复制代码

HTMLElement.dataset

在此以前我一直对 HTML 元素使用自定义数据属性 data-*,由于我未曾意识到存在一个 API 来方便地查询它们。除了个别的命名限制以外(见上面的连接),它的做用基本就是在 JS 中查询的时候容许你使用驼峰命名法(camelCase)来查询「减号-命名」(dash-case)的属性。因此属性名 data-birth-planet 在 JS 中就变成了 birthPlanet

<div id='person' data-name='john' data-birth-planet='earth'></div>
复制代码

查询:

let personEl = document.querySelector('#person');

console.log(personEl.dataset) // DOMStringMap {name: "john", birthPlanet: "earth"}
console.log(personEl.dataset.name) // john
console.log(personEl.dataset.birthPlanet) // earth

// 你也能够在程序中添加属性
personEl.dataset.foo = 'bar';
console.log(personEl.dataset.foo); // bar
复制代码

结束语

我但愿你和我同样在这里学到了一些新知识。在此也赞一下 Mozila 新的 MDN 站点,看起来很是棒,我花了比想象中更多的时间来阅读文档。

修订: 修正几处命名而且为 async 函数添加 try, catch。感谢 Reddit!

2018 新年快乐!


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索