ECMAScript 6 新特性简介

简介

ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,正式发布与2015年6月。它的目标,是使得JavaScript语言可以用来编写复杂的大型应用程序,成为企业级开发语言。

今天我们将会讲解一下ES6中引入的语法新特性。

ECMAScript和JavaScript的关系

1996年11月,JavaScript 的创造者 Netscape 公司,决定将 JavaScript 提交给国际标准化组织ECMA.

1997年, ECMA 发布262号标准文件 ECMAScript 1.0。

ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现。

我们看一下ECMAScript的发行历史:

从2015年ES2015,也就是ES6发布以来,ECMAScript以每年一个版本的发行速度发行到了ES2020。

后面的文章我们会讲解一下这些新版本的ECMAScript的新特性。

let和const

ES6中引入了let和const,是为了解决之前的var变量的种种问题。

在ES6之前,JS中变量的作用域有两种:全局作用域和函数作用域。

全局作用域很好理解,我们在浏览器控制台或者 Node.js 交互终端中开始编写 JavaScript 时,即进入了所谓的全局作用域。

全局作用域的变量可以在任何其他作用域中访问。

函数作用域就是定义在函数内部的变量,在函数内部都可以访问到该变量。

这两种作用域会有一些问题:

  • 变量提升

var命令会发生”变量提升”现象,即变量可以在声明之前使用,值为undefined.

1// var 的情况 2console.log(foo); // 输出undefined 3var foo = 2;
  • 变量覆盖

当我们在函数作用域使用全局变量的时候,如果函数作用域中定义了同样名字的变量,不管是在哪里定义的,都会覆盖掉全局的变量。如下所示:

1var tmp = new Date(); 2function f() { 3console.log(tmp); 4if (false) { var tmp = "hello world"; 5} } 6f(); // undefined
  • 变量泄露

变量泄露的意思是,我们本来只希望在小范围作用域使用的变量,结果泄露到了范围外面,如下所示:

1var s = 'hello'; 2for (var i = 0; i < s.length; i++) { 3console.log(s[i]); 4} 5console.log(i); // 5 6~~

为了解决上面两个问题,ES6引入了let和const。
这两个都是块级作用域。不同的是const定义的变量初始化之后就不能变化了。
什么是块级作用域呢?类似于 if、switch 条件选择或者 for、while 这样的循环体即是所谓的块级作用域,或者更简单一点使用大括号括起来的就叫做块级作用域。
块级作用域的最大好处就是不会产生作用域提升,如下所示:

1{ 2let a = 10; 3var b = 1; 4} 5a // ReferenceError: a is not defined. 6b // 1

解构赋值

什么是解构赋值呢?
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。
如下所示:

1let [a, b, c] = [1, 2, 3]; 2let [ , , third] = ["foo", "bar", "baz"]; 3let [x, , y] = [1, 2, 3]; 4let [head, ...tail] = [1, 2, 3, 4]; 5let [x, y] = [1, 2, 3];

解构赋值还可以设定默认值,我们来看下面的几个例子:

1let [foo = true] = []; 2foo // true 3let [x, y = 'b'] = ['a']; 4// x='a', y='b' 5let [x, y = 'b'] = ['a', undefined]; 6// x='a', y='b’ 7let [x = 1] = [undefined]; 8x // 1 9let [x = 1] = [null]; 10x // null

如果解构的默认值是一个函数,那么可以触发惰性赋值:

1function f() { 2console.log('aaa'); 3} 4let [x = f()] = [1];

上面的例子中,f函数将不会被执行。
除了结构变量之外,还可以结构对象:

1let { bar, foo } = { foo: "aaa", bar: "bbb" }; 2foo // "aaa" 3bar // "bbb" 4let { baz } = { foo: "aaa", bar: "bbb" }; 5baz // undefined 6var { foo: baz } = { foo: 'aaa', bar: 'bbb' }; 7baz // "aaa" 8let obj = { first: 'hello', last: 'world' }; 9let { first: f, last: l } = obj; 10f // 'hello' 11l // 'world'

解构还支持嵌套的结构:

1let obj = { p: [ 'Hello', { y: 'World' } ] }; 2let { p: [x, { y }] } = obj; 3x // "Hello" 4y // "World"

解构赋值有两个非常重要的作用。
第一就是交换变量:

1let x = 1; 2let y = 2; 3[x, y] = [y, x];

我们就可以不再使用中间变量,直接进行两个变量值的交互。
第二个作用就是从函数中返回多个值:

1// 返回一个数组 2function example() { return [1, 2, 3]; } 3let [a, b, c] = example(); 4// 返回一个对象 5function example() { return { foo: 1, bar: 2 }; } 6let { foo, bar } = example(); 7//提取JSON数据 8let jsonData = { id: 42, status: "OK", data: [867, 5309] }; 9let { id, status, data: number } = jsonData;

数组的扩展

ES6中的Array.from方法用于将下面两类对象转为真正的数组:
类似数组的对象(array-like object)
可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map)。
什么是类似数组对象呢?
所谓类似数组的对象,本质特征只有一点,即必须有length属性。因此,任何有length属性的对象,都可以通过Array.from方法转为数组。
下面的变量就是类数组变量:

let arrayLike = { '0': 'a', '1': 'b', '2': 'c', length: 3 };

这个类数组对象怎么转换成为数组呢?

1// ES5的写法 2var arr1 = [].slice.call(arrayLike); 3// ['a', 'b', 'c'] 4// ES6的写法 let arr2 = Array.from(arrayLike); 5// ['a', 'b', 'c']

我们看下通常的使用场景:

1// NodeList对象 2let ps = document.querySelectorAll('p'); 3Array.from(ps).forEach(function (p) { console.log(p); }); 4// arguments对象 5function foo() { var args = Array.from(arguments); 6// ... 7}

什么是可遍历对象呢?
只要是部署了Iterator接口的数据结构,都叫做可遍历对象。
我们看下下面的例子:

1Array.from('hello') // ['h', 'e', 'l', 'l', 'o'] 2let namesSet = new Set(['a', 'b']) 3Array.from(namesSet) // ['a', 'b']

同时还引入了扩展运算符(...),通过扩展运算符,也可以很方便的转换为数组对象:

1function foo() { var args = [...arguments]; } // arguments对象 2[...document.querySelectorAll('div')] // NodeList对象

Array.from方法还可以接收第二个参数,用来对数组中的元素进行操作:

1Array.from(arrayLike, x => x * x); 2// 等同于 3Array.from(arrayLike).map(x => x * x); 4Array.from([1, 2, 3], (x) => x * x) 5// [1, 4, 9]

Array.of方法可以很方便的创建新的数组:

1Array.of(3, 11, 8) // [3,11,8] 2Array.of(3) // [3] 3Array.of(3).length // 1 4Array() // [] 5Array(3) // [, , ,] 6Array(3, 11, 8) // [3, 11, 8]

函数的扩展

ES6,可以支持函数的默认值了:

1function log(x, y = 'World') { console.log(x, y); } 2function Point(x = 0, y = 0) { this.x = x; this.y = y; }

函数的默认值可以和解构赋值默认值组合起来使用:

1function foo({x, y = 5}) { console.log(x, y); } 2foo({}) // undefined, 5 3foo({x: 1}) // 1, 5 4foo({x: 1, y: 2}) // 1, 2 5foo() // TypeError: Cannot read property 'x' of undefined

接下来,我们看一个复杂的例子:

1// 写法一 2function m1({x = 0, y = 0} = {}) 3{ return [x, y]; } 4// 写法二 5function m2({x, y} = { x: 0, y: 0 }) 6{ return [x, y]; }

我们来看一下,上面的两种写法有什么不同呢?
当函数没有参数的情况:

1m1() // [0, 0] 2m2() // [0, 0]

当x和y都有值的情况:

1m1({x: 3, y: 8}) // [3, 8] 2m2({x: 3, y: 8}) // [3, 8]

当x有值,y无值的情况 :

1m1({x: 3}) // [3, 0] 2m2({x: 3}) // [3, undefined]

当x和y都无值的情况:

1m1({}) // [0, 0]; 2m2({}) // [undefined, undefined] 3m1({z: 3}) // [0, 0] 4m2({z: 3}) // [undefined, undefined]

看出区别了吗? m1的解构赋值,对于x,y来说是有默认值0的。而m2的解构赋值对于x,y来说是没有默认值的。

原文链接

本文为阿里云原创内容,未经允许不得转载。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )