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

关于提案

提案分为五个阶段。有关详细信息,请参阅介绍文档https://tc39.github.io/process-document/。每项提案最初都以“斯特劳曼”或第0阶段休闲鹿提出,在这一阶段,它们要么没有提交给技术委员会,要么没有被否决,但尚未进入下一阶段。

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

以下提案均未进入第0阶段

创建测试项目

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

npm init -f

npm 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文件中:

{

"scripts": {

"test": "ava"

},

"ava": {

"require": [

"@babel/register",

"@babel/polyfill"

]

}

}

最后创建一个.babelrc文件:

{

"presets": [

[

"@babel/preset-env",

{

"targets": {

"node": "current"

}

}

],

"@babel/preset-stage-0"

],

"plugins": [

"@babel/plugin-transform-runtime"

]

}

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

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

const data = {

user: {

address: {

street: "Pennsylvania Avenue"

}

}

}

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

const data = {

user: {}

};

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

console.log(data.user.address.street);

// Uncaught TypeError: Cannot read property 'street' of undefined

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

const street = data && data.user && data.user.address && data.user.address.street;

console.log(street); // undefined`

在我看来,这种方法:

不美观

繁重

啰嗦

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

console.log(data.user?.address?.street);

// undefined

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

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

});

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

test('Optional chaining returns undefined for nullish properties.', (t) => {

t.is(getAddress(), undefined);

t.is(getAddress(null), undefined);

t.is(getAddress({}), undefined);

});

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

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

});

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

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

});

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

value == null ? value[some expression here]: undefined;

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

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中看到的一些常见操作:

检查 null 或 undefined

给变量设置默认值

确保0,false和''不设置默认值

像这样:

value != null ? value : 'default value';

或者这样:

value || 'default value'

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

value != null

和上面相同:

value !== null && value !== undefined

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

value ?? 'default value';

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

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

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中讨论的一个示例:

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都有实现。

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

let result = "hello"

|> doubleSay

|> capitalize

|> exclaim;

result //=> "Hello, hello!"`

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

let result = 1

|> (_ => Math.max(0, _));

result //=> 1

let result = -5

|> (_ => Math.max(0, _));

result //=> 0

编写如下测试用例:

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,但尚未实现。

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

javascript新特性的更多相关文章

  1. ES6:JavaScript 新特性

    我相信,在ECMAScript.next到来的时候,我们现在每天都在写的JavaScript代码将会发生巨大的变化.接下来的一年将会是令JavaScript开发者们兴奋的一年,越来越多的特性提案将被最 ...

  2. 7 个令人兴奋的 JavaScript 新特性

    前言 一个ECMAScript标准的制作过程,包含了Stage 0到Stage 4五个阶段,每个阶段提交至下一阶段都需要TC39审批通过.本文介绍这些新特性处于Stage 3或者Stage 4阶段,这 ...

  3. 七种武器:JavaScript 新特性闪亮登场

    JavaScript(或ECMA Script) 是一门不断发展的语言,有许多关于如何前进的建议和想法.TC39(技术委员会39)是负责定义JS标准和特性的委员会,今年他们非常活跃.以下是目前处于&q ...

  4. 6个小而美的es6新特性

    译者:动静若参商 译文:http://www.zcfy.cc/article/1795 原文:https://davidwalsh.name/es6-features JavaScript社区中的每个 ...

  5. ArcGIS API for JavaScript 4.2学习笔记[0] AJS4.2概述、新特性、未来产品线计划与AJS笔记目录

    放着好好的成熟的AJS 3.19不学,为什么要去碰乳臭未干的AJS 4.2? 4.2全线基础学习请点击[直达] 4.3及更高版本的补充学习请关注我的博客. ArcGIS API for JavaScr ...

  6. ECMAScript和JavaScript的区别,ECMAScript发展更新历史,ECMAScript5和ECMAScript6的新特性及浏览器支持情况,ECMAScript 5/ECMAScript 2015正式发布

    ECMAScript和JavaScript的区别 ECMA是European Computer Manufacturers Association的缩写,即欧洲计算机制造商协会.欧洲计算机制造商协会是 ...

  7. Atitit.js模块化 atiImport 的新特性javascript import

    Atitit.js模块化 atiImport 的新特性javascript import 1. 常见的js import规范amd ,cmd ,umd1 1.1. Require更多流行3 2. at ...

  8. 细解JavaScript ES7 ES8 ES9 新特性

    题记:本文提供了一个在线PPT版本,方便您浏览 细解JAVASCRIPT ES7 ES8 ES9 新特性 在线PPT ver 本文的大部分内容译自作者Axel Rauschmayer博士的网站,想了解 ...

  9. ArcGIS API for JavaScript 4.4学习笔记[新] AJS4.4和AJS3.21新特性

    ESRI官网悄无声息突然更新4.4和3.21,公众号也没有什么消息.照例,给大家看看这次更新有什么新特性吧. 1. AJS 4.4 官方更新日志:点我,比较详细.我在这里抽一些主干作为说明. 1.1 ...

随机推荐

  1. OpenNebula概述

    OpenNebula概述 OpenNebula是专门为云计算打造的开源系统,用户可以使用Xen.KVM.VMware等虚拟化软件一起打造企业云.利用OpenNebula可以轻松构建私有云.混合云.公开 ...

  2. 机器学习 - 算法示例 - Xgboost

    安装 能直接安装就再好不过 pip install xgboost 如果不能就下载之后本地安装 安装包下载地址 这里 想要啥包都有 数据集 pima-indians-diabetes.csv 文件 调 ...

  3. js实现文本框支持加减运算的方法

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/T ...

  4. Java多线程中thread.getname()和thread.currentThread().getName();的去别

    首先要明白 this.XXX 的使用场景 使用Thread.currentThread().getName()和使用this.getName()和对象实例.getName(),都可以得到线程的名称,但 ...

  5. IfcRelDefinesByProperties IfcPropertySetDefinitionSelect IfcPropertySetDefinition IfcPropertySetDefinitionSet

    private void updateObject(IfcObject ifcObject, PropertyNode root) { /* if(ifcObject.getClassName().t ...

  6. Greenwich.SR2版本的Spring Cloud Config+BUS实例

    Spring Cloud Config统一的配置中心同注册中心Eureka一样,也分服务端和客户端.服务端用来保存配置信息,客户端用来读取.它的优势是基于Git仓库,支持多环境.多分支配置.动态刷新. ...

  7. 【c# 学习笔记】构造函数

    构造函数 主要用于创建类的实例对象.当调用构造函数创建一个对象时,构造函数会为对象分配内存空间,并初始化类的成员.构造函数分为实例构造函数和静态构造函数两种. 1.实例构造函数 实例构造函数用于创建和 ...

  8. [Python[CV2]] -- 352: error -- 来自截取图片

    今天想用CV2 截个ROI ( Region Of Interest ) 的时候, 是这样写的 结果, 发现是截取img 那句出了错 应该为: 先纵后横 这样就OK了 cv2.selectROI 用法 ...

  9. 移动架构-IOC架构设计

    控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度.其中最常见的方式叫做依赖注入(Dependency Inject ...

  10. git学习链接---收藏----

    Github 创建新分支:https://blog.csdn.net/top_code/article/details/51931916Git易百教程:https://www.yiibai.com/g ...