前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >三个JavaScript 新特性 值得期待哦~

三个JavaScript 新特性 值得期待哦~

作者头像
苏南
发布2020-12-16 10:04:07
4390
发布2020-12-16 10:04:07
举报
文章被收录于专栏:漫画前端

点击上方“IT平头哥联盟”,选择“置顶或者星标”

与你一起成长~

公众号回复[ 加群 ] 一起成长~

英文:Justin Fuller 作者:川木

一起来来看看JavaScript中一些新特性,本文将介绍它们的语法和相关链接,帮助读者及时了解它们的进度,我们将通过编写一个小型测试项目,展示如何快速上手使用这些新特性!

关于提案

提案有五个阶段,详看介绍文档 https://tc39.github.io/process-document/ 。每个提案最初提出的都是“strawman”或stage 0阶段,在这个阶段,它们要么尚未提交给技术委员会,要么尚未被拒绝,但仍未达到进入下一阶段。

作为个人建议,读者应避免生产环境中使用stage 0提案,它们处于不稳定的阶段。

下文的提案都不属于第0阶段

创建测试项目

新建一个目录并运行运行以下命令:

代码语言:javascript
复制
npm init -fnpm i ava@1.0.0-beta.3 @babel/preset-env@7.0.0-beta.42 @babel/preset-stage-0@7.0.0-beta.42 @babel/register@7.0.0-beta.42 @babel/polyfill@7.0.0-beta.42 @babel/plugin-transform-runtime@7.0.0-beta.42 @babel/runtime@7.0.0-beta.42 --save-dev`

然后将以下内容添加到package.json文件中:

代码语言:javascript
复制
{  "scripts": {    "test": "ava"  },  "ava": {    "require": [      "@babel/register",      "@babel/polyfill"    ]  }}

最后创建一个.babelrc文件:

代码语言:javascript
复制
{  "presets": [    [      "@babel/preset-env",      {        "targets": {          "node": "current"        }      }    ],    "@babel/preset-stage-0"  ],  "plugins": [    "@babel/plugin-transform-runtime"  ]}

现在可以开始写一些测试用例了!

1.可选运算符

在JavaScript中,我们一直在使用对象,但有时候对象里并不是我们期望的数据结构。假设下面是我们期望得到的数据,可能是通过调用API查询数据库得到的。

代码语言:javascript
复制
const data = {  user: {    address: {      street: "Pennsylvania Avenue"    }  }}

如果该用户没有完成注册,可能得到下面的数据:

代码语言:javascript
复制
const data = {  user: {}};

当尝试按下面的方式访问street时,会得到报错:

代码语言:javascript
复制
console.log(data.user.address.street);// Uncaught TypeError: Cannot read property 'street' of undefined

为避免这种情况,需要按如下方式访问“street”属性:

代码语言:javascript
复制
const street = data && data.user && data.user.address && data.user.address.street;console.log(street); // undefined`

在我看来,这种方法:

  1. 不美观
  2. 繁重
  3. 啰嗦

如果使用可选运算符,可以这样编码:

代码语言:javascript
复制
console.log(data.user?.address?.street);// undefined

这样看起来更简单了,现在我们已经看到了这个功能的用处,现在来写一个测试!

代码语言:javascript
复制
import test from 'ava';
const valid = {  user: {    address: {      street: 'main street',    },  },};
function getAddress(data) {  return data?.user?.address?.street;}
test('Optional Chaining returns real values', (t) => {  const result = getAddress(valid);  t.is(result, 'main street');});

我们看到了可选符号的正常使用,接下来是一些不规范数据的测试用例:

代码语言:javascript
复制
test('Optional chaining returns undefined for nullish properties.', (t) => {  t.is(getAddress(), undefined);  t.is(getAddress(null), undefined);  t.is(getAddress({}), undefined);});

用于访问数组元素的用例:

代码语言:javascript
复制
const valid = {  user: {    address: {      street: 'main street',      neighbors: [        'john doe',        'jane doe',      ],    },  },};
function getNeighbor(data, number) {  return data?.user?.address?.neighbors?.[number];}
test('Optional chaining works for array properties', (t) => {  t.is(getNeighbor(valid, 0), 'john doe');});
test('Optional chaining returns undefined for invalid array properties', (t) => {  t.is(getNeighbor({}, 0), undefined);});

有时我们不知道某个函数是否在对象中实现,一个常见的场景是,某些旧版浏览器可能没有某些功能,我们可以使用可选运算符接来检测函数是否已实现。看如下代码:

代码语言:javascript
复制
const data = {  user: {    address: {      street: 'main street',      neighbors: [        'john doe',        'jane doe',      ],    },    getNeighbors() {      return data.user.address.neighbors;    }  },};
function getNeighbors(data) {  return data?.user?.getNeighbors?.();}
test('Optional chaining also works with functions', (t) => {  const neighbors = getNeighbors(data);  t.is(neighbors.length, 2);  t.is(neighbors[0], 'john doe');});
test('Optional chaining returns undefined if a function does not exist', (t) => {  const neighbors = getNeighbors({});  t.is(neighbors, undefined);});

如果调用链不完整,函数将不会执行,它背后的逻辑应该是这样的:

代码语言:javascript
复制
value == null ? value[some expression here]: undefined;

如果在可选链操作符之后是 undefined 或者 null则什么都不会执行,我们可以在以下测试中看到该规则的实际应用:

代码语言:javascript
复制
let neighborCount = 0;
function getNextNeighbor(neighbors) {  return neighbors?.[++neighborCount];}
test('It short circuits expressions', (t) => {  const neighbors = getNeighbors(data);  t.is(getNextNeighbor(neighbors), 'jane doe');  t.is(getNextNeighbor(undefined), undefined);  t.is(neighborCount, 1);});

有了可选运运算符,我们的代码中可以减少if语句、lodash等库以及&&进行链式调用的使用。

2.空值合并

以下是我们在JavaScript中看到的一些常见操作:

  1. 检查 null 或 undefined
  2. 给变量设置默认值
  3. 确保0,false和''不设置默认值

像这样:

代码语言:javascript
复制
value != null ? value : 'default value';

或者这样:

代码语言:javascript
复制
value || 'default value'

问题是,对于第二个实现,在值为0、false和''时都被视为false,所以我们必须明确检查null和undefined。

代码语言:javascript
复制
value != null

和上面相同:

代码语言:javascript
复制
value !== null && value !== undefined

这就是新提案的用武之地,现在我们可以这样做:

代码语言:javascript
复制
value ?? 'default value';

这可以保护我们不会为0、false和''设置默认值,在不使用三元运算符和!= null检查的情况下捕获null和undefined。

接下来编写一个简单的测试来验证它是如何工作的:

代码语言:javascript
复制
import test from 'ava';
test('Nullish coalescing defaults null', (t) => {  t.is(null ?? 'default', 'default');});
test('Nullish coalescing defaults undefined', (t) => {  t.is(undefined ?? 'default', 'default');});
test('Nullish coalescing defaults void 0', (t) => {  t.is(void 0 ?? 'default', 'default');});
test('Nullish coalescing does not default 0', (t) => {  t.is(0 ?? 'default', 0);});
test('Nullish coalescing does not default empty strings', (t) => {  t.is('' ?? 'default', '');});
test('Nullish coalescing does not default false', (t) => {  t.is(false ?? 'default', false);});

在测试中看到,??为null,undefined和void 0设置了默认值,没有为0,''和false设置默认值。

3.管道运算符

在函数式编程中,我们有一个概念叫compose,它多个函数调用合并在一起,调用时从右到左执行每个函数,函数接收前一个函数的输出作为其输入,以下是我们在纯JavaScript中讨论的一个示例:

代码语言:javascript
复制
function doubleSay (str) {  return str + ", " + str;}
function capitalize (str) {  return str[0].toUpperCase() + str.substring(1);}
function exclaim (str) {  return str + '!';}
let result = exclaim(capitalize(doubleSay("hello")));result //=> "Hello, hello!"

这种合并使用函数的方式很常见常见,以至于在于大多数功能库中,如lodash和ramda都有实现。

使用新的管道运算符,可以不使用第三方库并按如下所示编写上述内容:

代码语言:javascript
复制
let result = "hello"  |> doubleSay  |> capitalize  |> exclaim;result //=> "Hello, hello!"`

这个提案目的是使链式调用函数更具可读性,在未来结合函数部分应用也可以很好的工作,类似下面这种使用方式:

代码语言:javascript
复制
let result = 1  |> (_ => Math.max(0, _));result //=> 1

代码语言:javascript
复制
let result = -5  |> (_ => Math.max(0, _));result //=> 0

编写如下测试用例:

代码语言:javascript
复制
import test from 'ava';
function doubleSay (str) {  return str + ", " + str;}
function capitalize (str) {  return str[0].toUpperCase() + str.substring(1);}
function exclaim (str) {  return str + '!';}
test('Simple pipeline usage', (t) => {  let result = "hello"    |> doubleSay    |> capitalize    |> exclaim;
  t.is(result, 'Hello, hello!');});
test('Partial application pipeline', (t) => {  let result = -5    |> (_ => Math.max(0, _));
  t.is(result, 0);});
test('Async pipeline', async (t) => {  const asyncAdd = (number) => Promise.resolve(number + 5);  const subtractOne = (num1) => num1 - 1;  const result = 10    |> asyncAdd    |> (async (num) => subtractOne(await num));
  t.is(await result, 14);});

需要注意,一旦将async函数添加到管道,必须await该返回值,因为此时返回值是promise。有一提案开始支持|> await asyncFunction,但尚未实现。

最后,既然你已经看到了这些提案的实际应用,我希望你能够尝试一下这些提案!

相关链接

  • proposal-optional-chaining https://github.com/TC39/proposal-optional-chaining
  • proposal-nullish-coalescing https://github.com/tc39/proposal-nullish-coalescing
  • proposal-partial-application https://github.com/tc39/proposal-partial-application
  • proposal-pipeline-operator https://github.com/tc39/proposal-pipeline-operator
  • 测试代码 https://github.com/JustinDFuller/javascript-proposals-tests

- end -

用心分享 一起成长 做有温度的攻城狮

每天记得对自己说:你是最棒的!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-02-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 画漫画的程序员 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 与你一起成长~
    • 关于提案
      • 创建测试项目
        • 1.可选运算符
          • 2.空值合并
            • 3.管道运算符
              • 相关链接
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档