我未曾见过的 JS 特性

我未曾见过的 JS 特性

大家好,又见面了,我是全栈君。

我未曾见过的 JS 特性

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

标记语句

有多少人知道在 JS 里你可以给 for 循环和语句块命名?反正我不知道…… 命名完新名称之后你可以在 for 循环中的 breakcontinue 之后、语句块中的 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}`);
   }
}

/* 
 * # 输出
 * 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('这句打印不会被执行');
}
console.log('two');

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

“void” 运算符

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

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

使用它,你可以换一种方式来写立即调用的函数表达式(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,这样你就能把函数作为异步代码的入口:

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 的引用:

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

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前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/107722.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号