1、简介

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

  ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现(另外的 ECMAScript 方言还有 JScript 和 ActionScript)。日常场合,这两个词是可以互换的。

2、let 和 const 命令

  在ES6以前,var关键字声明变量。无论声明在何处,都会被视为声明在函数的最顶部(不在函数内即在全局作用域的最顶部)。

  ES6新增了 let 和 const 命令,用来声明变量。let 表示变量、const表示常量,它们都是块级作用域。说白了,就是大括号 {} 内的代码块即为 let 和 const 的作用域。

  ES6 明确规定,如果区块中存在letconst命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。

  

  1. if (true) {
  2. // TDZ开始
  3. tmp = 'abc'; // ReferenceError
  4. console.log(tmp); // ReferenceError
  5.  
  6. let tmp; // TDZ结束
  7. console.log(tmp); // undefined
  8.  
  9. tmp = 123;
  10. console.log(tmp); //
  11. }

上面代码中,在let命令声明变量tmp之前,都属于变量tmp的“死区”。

“暂时性死区”也意味着typeof不再是一个百分之百安全的操作,如下:

  1. typeof x; // ReferenceError
  2. let x;

上面代码中,变量x使用let命令声明,所以在声明之前,都属于x的“死区”,只要用到该变量就会报错。因此,typeof运行时就会抛出一个ReferenceError。

  不允许重复声明

  let不允许在相同作用域内,重复声明同一个变量。如下:

  1. // 报错
  2. function func() {
  3. let a = 10;
  4. var a = 1;
  5. }
  6.  
  7. // 报错
  8. function func() {
  9. let a = 10;
  10. let a = 1;
  11. }

因此,不能在函数内部重新声明参数。

  1. function func(arg) {
  2. let arg;
  3. }
  4. func() // 报错
  5.  
  6. function func(arg) {
  7. {
  8. let arg;
  9. }
  10. }
  11. func() // 不报错

3、变量的解构赋值

  ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。

  • 数组的结构赋值

  以前,为变量赋值,只能直接指定值:

  1. let a = 1;
  2. let b = 2;
  3. let c = 3;

  ES6 写法:

  1. let [a, b, c] = [1, 2, 3];

  这种写法属于“模式匹配”,等号两边的模式相同。也有嵌套数组进行结构的例子,如下:

  1. let [foo, [[bar], baz]] = [1, [[2], 3]];
  2. foo //
  3. bar //
  4. baz //
  5.  
  6. let [ , , third] = ["foo", "bar", "baz"];
  7. third // "baz"
  8.  
  9. let [x, , y] = [1, 2, 3];
  10. x //
  11. y //
  12.  
  13. let [head, ...tail] = [1, 2, 3, 4];
  14. head //
  15. tail // [2, 3, 4]
  16.  
  17. let [x, y, ...z] = ['a'];
  18. x // "a"
  19. y // undefined
  20. z // []

如果结构不成功,变量的值则为 undefined.

  1. let [foo] = [];
  2. let [bar, foo] = [1];

以上两种情况都属于解构不成功,foo的值都会等于undefined

另一种情况是不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功。

  1. let [x, y] = [1, 2, 3];
  2. x //
  3. y //
  4.  
  5. let [a, [b], d] = [1, [2, 3], 4];
  6. a //
  7. b //
  8. d //
  • 对象的解构赋值

  结构不仅可以用于数组,还可以用于对象。

  1. let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
  2. foo // "aaa"
  3. bar // "bbb"

  对象与数组的解构有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性的同名,才能取到正确的值。

  

  1. let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
  2. foo // "aaa"
  3. bar // "bbb"
  4.  
  5. let { baz } = { foo: 'aaa', bar: 'bbb' };
  6. baz // undefined

  对象的解构赋值,可以很方便的将现有对象的方法赋值到某个变量。

  1. // 例一
  2. let { log, sin, cos } = Math;
  3.  
  4. // 例二
  5. const { log } = console;
  6. log('hello') // hello

  上面代码的例一将Math对象的对数、正弦、余弦三个方法,赋值到对应的变量上,使用起来就会方便很多。例二将console.log赋值到log变量。

  如果变量名与属性名不一致,必须写成下面这样:

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

对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。

  1. let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
  2. baz // "aaa"
  3. foo // error: foo is not defined

上面代码中,foo是匹配的模式,baz才是变量。真正被赋值的是变量baz,而不是模式foo

与数组一样,解构也可以用于嵌套结构的对象。

  1. let obj = {
  2. p: [
  3. 'Hello',
  4. { y: 'World' }
  5. ]
  6. };
  7.  
  8. let { p: [x, { y }] } = obj;
  9. x // "Hello"
  10. y // "World"

注意,这时p是模式,不是变量,因此不会被赋值。如果p也要作为变量赋值,可以写成下面这样。

  1. let obj = {
  2. p: [
  3. 'Hello',
  4. { y: 'World' }
  5. ]
  6. };
  7.  
  8. let { p, p: [x, { y }] } = obj;
  9. x // "Hello"
  10. y // "World"
  11. p // ["Hello", {y: "World"}]
  • 字符串的解构赋值

  字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象。

  1. const [a, b, c, d, e] = 'hello';
  2. a // "h"
  3. b // "e"
  4. c // "l"
  5. d // "l"
  6. e // "o"

类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值。

  1. let {length : len} = 'hello';
  2. len //
  • 数值与布尔值的解构赋值

  解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。

  1. let {toString: s} = 123;
  2. s === Number.prototype.toString // true
  3.  
  4. let {toString: s} = true;
  5. s === Boolean.prototype.toString // true

上面代码中,数值和布尔值的包装对象都有toString属性,因此变量s都能取到值。

解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefinednull无法转为对象,所以对它们进行解构赋值,都会报错。

  1. let { prop: x } = undefined; // TypeError
  2. let { prop: y } = null; // TypeError
  • 函数参数的解构赋值
  1. function add([x, y]){
  2. return x + y;
  3. }
  4.  
  5. add([1, 2]); //
  1. [[1, 2], [3, 4]].map(([a, b]) => a + b);
  2. // [ 3, 7 ]
  1. function move({x = 0, y = 0} = {}) {
  2. return [x, y];
  3. }
  4.  
  5. move({x: 3, y: 8}); // [3, 8]
  6. move({x: 3}); // [3, 0]
  7. move({}); // [0, 0]
  8. move(); // [0, 0]

变量解构赋值的用途:

  • 交换变量的值
  1. let x = 1;
  2. let y = 2;
  3.  
  4. [x, y] = [y, x];
  • 从函数返回多个值
  1. // 返回一个数组
  2.  
  3. function example() {
  4. return [1, 2, 3];
  5. }
  6. let [a, b, c] = example();
  7.  
  8. // 返回一个对象
  9.  
  10. function example() {
  11. return {
  12. foo: 1,
  13. bar: 2
  14. };
  15. }
  16. let { foo, bar } = example();
  • 函数参数的定义
  1. // 参数是一组有次序的值
  2. function f([x, y, z]) { ... }
  3. f([1, 2, 3]);
  4.  
  5. // 参数是一组无次序的值
  6. function f({x, y, z}) { ... }
  7. f({z: 3, y: 2, x: 1});
  • 提取JSON数据
  1. let jsonData = {
  2. id: 42,
  3. status: "OK",
  4. data: [867, 5309]
  5. };
  6.  
  7. let { id, status, data: number } = jsonData;
  8.  
  9. console.log(id, status, number);
  10. // 42, "OK", [867, 5309]
  • 函数参数的默认值
  1. jQuery.ajax = function (url, {
  2. async = true,
  3. beforeSend = function () {},
  4. cache = true,
  5. complete = function () {},
  6. crossDomain = false,
  7. global = true,
  8. // ... more config
  9. } = {}) {
  10. // ... do stuff
  11. };
  • 遍历Map解构

  任何部署了 Iterator 接口的对象,都可以用for...of循环遍历。Map 结构原生支持 Iterator 接口,配合变量的解构赋值,获取键名和键值就非常方便。

  1. const map = new Map();
  2. map.set('first', 'hello');
  3. map.set('second', 'world');
  4.  
  5. for (let [key, value] of map) {
  6. console.log(key + " is " + value);
  7. }
  8. // first is hello
  9. // second is world

如果只想获取键名,或者只想获取键值,可以写成下面这样。

  1. // 获取键名
  2. for (let [key] of map) {
  3. // ...
  4. }
  5.  
  6. // 获取键值
  7. for (let [,value] of map) {
  8. // ...
  9. }
  • 输入模块的指定方法

加载模块时,往往需要指定输入哪些方法。解构赋值使得输入语句非常清晰。

  1. const { SourceMapConsumer, SourceNode } = require("source-map");

4、字符串扩展

  • includes(), startsWith(), endsWith() 

传统上,JavaScript 只有indexOf方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6 又提供了三种新方法。

  1. includes():返回布尔值,表示是否找到了参数字符串。
  2. startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
  3. endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
  1. let s = 'Hello world!';
  2.  
  3. s.startsWith('Hello') // true
  4. s.endsWith('!') // true
  5. s.includes('o') // true

这三个方法都支持第二个参数,表示开始搜索的位置。

  1. let s = 'Hello world!';
  2.  
  3. s.startsWith('world', 6) // true
  4. s.endsWith('Hello', 5) // true
  5. s.includes('Hello', 6) // false
  • repeat()

repeat方法返回一个新字符串,表示将原字符串重复n次.

  1. 'x'.repeat(3) // "xxx"
  2. 'hello'.repeat(2) // "hellohello"
  3. 'na'.repeat(0) // ""

如果repeat的参数是字符串,则会先转换成数字。

  1. 'na'.repeat(2.9) // "nana"
  2. 'na'.repeat(Infinity)
  3. // RangeError
  4. 'na'.repeat(-1)
  5. // RangeError
  6. 'na'.repeat(-0.9) // ""
  7. 'na'.repeat('na') // ""
  8. 'na'.repeat('3') // "nanana"
  • padStart() padEnd()

ES2017 引入了字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。padStart()用于头部补全,padEnd()用于尾部补全。

  1. 'x'.padStart(5, 'ab') // 'ababx'
  2. 'x'.padStart(4, 'ab') // 'abax'
  3.  
  4. 'x'.padEnd(5, 'ab') // 'xabab'
  5. 'x'.padEnd(4, 'ab') // 'xaba'

padStart()的常见用途是为数值补全指定位数。下面代码生成 10 位的数值字符串。

  1. '1'.padStart(10, '0') // "0000000001"
  2. '12'.padStart(10, '0') // "0000000012"
  3. '123456'.padStart(10, '0') // "0000123456"

另一个用途是提示字符串格式。

  1. '12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12"
  2. '09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"
  • 模板字符串(template string)

传统的 JavaScript 语言,输出模板通常是这样写的(下面使用了 jQuery 的方法)。

  1. $('#result').append(
  2. 'There are <b>' + basket.count + '</b> ' +
  3. 'items in your basket, ' +
  4. '<em>' + basket.onSale +
  5. '</em> are on sale!'
  6. );

上面这种写法相当繁琐不方便,ES6 引入了模板字符串解决这个问题。

  1. $('#result').append(`
  2. There are <b>${basket.count}</b> items
  3. in your basket, <em>${basket.onSale}</em>
  4. are on sale!
  5. `);

模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。

  1. // 普通字符串
  2. `In JavaScript '\n' is a line-feed.`
  3.  
  4. // 多行字符串
  5. `In JavaScript this is
  6. not legal.`
  7.  
  8. console.log(`string text line 1
  9. string text line 2`);
  10.  
  11. // 字符串中嵌入变量
  12. let name = "Bob", time = "today";
  13. `Hello ${name}, how are you ${time}?`

上面代码中的模板字符串,都是用反引号表示。如果在模板字符串中需要使用反引号,则前面要用反斜杠转义。

  1. let greeting = `\`Yo\` World!`;

如果使用模板字符串表示多行字符串,所有的空格和缩进都会被保留在输出之中。

  1. $('#list').html(`
  2. <ul>
  3. <li>first</li>
  4. <li>second</li>
  5. </ul>
  6. `);

上面代码中,所有模板字符串的空格和换行,都是被保留的,比如<ul>标签前面会有一个换行。如果你不想要这个换行,可以使用trim方法消除它。

  1. $('#list').html(`
  2. <ul>
  3. <li>first</li>
  4. <li>second</li>
  5. </ul>
  6. `.trim());

模板字符串中嵌入变量,需要将变量名写在${}之中。

  1. function authorize(user, action) {
  2. if (!user.hasPrivilege(action)) {
  3. throw new Error(
  4. // 传统写法为
  5. // 'User '
  6. // + user.name
  7. // + ' is not authorized to do '
  8. // + action
  9. // + '.'
  10. `User ${user.name} is not authorized to do ${action}.`);
  11. }
  12. }

大括号内部可以放入任意的 JavaScript 表达式,可以进行运算,以及引用对象属性。

  1. let x = 1;
  2. let y = 2;
  3.  
  4. `${x} + ${y} = ${x + y}`
  5. // "1 + 2 = 3"
  6.  
  7. `${x} + ${y * 2} = ${x + y * 2}`
  8. // "1 + 4 = 5"
  9.  
  10. let obj = {x: 1, y: 2};
  11. `${obj.x + obj.y}`
  12. // "3"

模板字符串之中还能调用函数。

  1. function fn() {
  2. return "Hello World";
  3. }
  4.  
  5. `foo ${fn()} bar`
  6. // foo Hello World bar

如果大括号中的值不是字符串,将按照一般的规则转为字符串。比如,大括号中是一个对象,将默认调用对象的toString方法。

  1. // 变量place没有声明
  2. let msg = `Hello, ${place}`;
  3. // 报错
  4.  
  5. `Hello ${'World'}`
  6. // "Hello World"

模板字符串甚至还能嵌套。

  1. const tmpl = addrs => `
  2. <table>
  3. ${addrs.map(addr => `
  4. <tr><td>${addr.first}</td></tr>
  5. <tr><td>${addr.last}</td></tr>
  6. `).join('')}
  7. </table>
  8. `;

上面代码中,模板字符串的变量之中,又嵌入了另一个模板字符串,使用方法如下。

  1. const data = [
  2. { first: '<Jane>', last: 'Bond' },
  3. { first: 'Lars', last: '<Croft>' },
  4. ];
  5.  
  6. console.log(tmpl(data));
  7. // <table>
  8. //
  9. // <tr><td><Jane></td></tr>
  10. // <tr><td>Bond</td></tr>
  11. //
  12. // <tr><td>Lars</td></tr>
  13. // <tr><td><Croft></td></tr>
  14. //
  15. // </table>

如果需要引用模板字符串本身,在需要时执行,可以写成函数。

  1. let func = (name) => `Hello ${name}!`;
  2. func('Jack') // "Hello Jack!"

ES6 入门的更多相关文章

  1. ES6入门笔记

    ES6入门笔记 02 Let&Const.md 增加了块级作用域. 常量 避免了变量提升 03 变量的解构赋值.md var [a, b, c] = [1, 2, 3]; var [[a,d] ...

  2. es6入门4--promise详解

    可以说每个前端开发者都无法避免解决异步问题,尤其是当处理了某个异步调用A后,又要紧接着处理其它逻辑,而最直观的做法就是通过回调函数(当然事件派发也可以)处理,比如: 请求A(function (请求响 ...

  3. es6入门3--箭头函数与形参等属性的拓展

    对函数拓展兴趣更大一点,优先看,前面字符串后面再说,那些API居多,会使用能记住部分就好. 一.函数参数可以使用默认值 1.默认值生效条件 在变量的解构赋值就提到了,函数参数可以使用默认值了.正常我们 ...

  4. Vue+koa2开发一款全栈小程序(1.课程介绍+2.ES6入门)

    1.课程介绍 1.课程概述 1.做什么? Vue+koa2开发一款全栈小程序 2.哪些功能? 个人中心.图书列表.图书详情.图书评论.个人评论列表 3.技术栈 小程序.Vue.js.koa2.koa- ...

  5. es6入门5--class类的基本用法

    在ES6之前,准确来说JavaScript语言并无类的概念,却有模拟类的做法.相比在类似java这类传统面向对象语言中通过类来生成实例,js则通过构造函数模拟类来生成实例. 这是因为在JS设计初期,作 ...

  6. es6入门6--数组拓展运算符,Array.from()基本用法

    本文只是作为ES6入门第九章学习笔记,在整理知识点的同时,会加入部分个人思考与解答,若想知道更详细的介绍,还请阅读阮一峰大神的ES6入门 一.拓展运算符 ES6中新增了拓展运算(...)三个点,它的作 ...

  7. ES6入门之let和const命令

    前言 大家好,我是一只流浪的kk,当你看到这边博客的时候,说明你已经进入了ES6学习的领域了,从本篇博客开始,我将会将自己学习到ES6的相关知识进行整理,方便大家参考和学习,那么我将带你进入第一节的内 ...

  8. ES6入门之变量的解构赋值(二)

    前言 在上一章 ES6入门之let和const命令中我们对ES6的相关语法已经有了初步了解,上一章中我们主要学习了三大部分的内容,let命令的使用,块级作用域,const命令的使用,那么从本篇博客将进 ...

  9. ES6入门十二:Module(模块化)

    webpack4打包配置babel7转码ES6 Module语法与API的使用 import() Module加载实现原理 Commonjs规范的模块与ES6模块的差异 ES6模块与Nodejs模块相 ...

  10. es6入门7--Set Map数据结构

    本文作为ES6入门第十三章的学习整理笔记,可能会包含少部分个人的理解推测,若想阅读更详细的介绍,还请阅读原文ES6入门 一.set数据结构 1.set不接受重复值 ES6新增了Set构造函数用于创建s ...

随机推荐

  1. 使用webpack将es6 es7转换成es2015

    第一步:安装模块化包 cnpm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react 第二 ...

  2. HDFS-Architecture剖析

    1.概述 从HDFS的应用层面来看,我们可以非常容易的使用其API来操作HDFS,实现目录的创建.删除,文件的上传下载.删除.追加(Hadoop2.x版本以后开始支持)等功能.然而仅仅局限与代码层面是 ...

  3. SQL 必知必会·笔记<7>汇总数据——使用聚合函数

    有时候我们需要对表中的数据进行汇总,而不需要数据本身,为了方便这些类型的检索,SQL给出了5个聚合函数,SQL聚合函数在各主要的SQL实现中得到了相当一致的支持.如下: 1.1 AVG()函数 AVG ...

  4. 读vue-0.6-text-parser.js源码

    提取字符串中的表达式 var BINDING_RE = /\{\{(.+?)\}\}/; function parse(text) { // 找不到返回null if (!BINDING_RE.tes ...

  5. PostgreSQL 数据类型

    数值类型 数值类型由两个字节,4字节和8字节的整数,4字节和8字节的浮点数和可选精度的小数.下表列出了可用的类型. www.yiibai.com Name Storage Size Descripti ...

  6. Java设计模式学习记录-外观模式

    前言 这次要介绍的是外观模式(也称为门面模式),外观模式也属于结构型模式,其实外观模式还是非常好理解的,简单的来讲就是将多个复杂的业务封装成一个方法,在调用此方法时可以不必关系具体执行了哪些业务,而只 ...

  7. c语言中阶乘的精确值

    对于大数的操作,可能超出int,甚至long的表示范围,对此,可以使用数组来存储大数,下列代码为求1000以内数的阶乘的代码,代码如下: #include <stdio.h> #inclu ...

  8. python取余

    a=-7,b=3, a % b = 2  #取余 a-((a/b)*b) a / b= -3 #整除 int(math.floor(-7/3.0))

  9. 【手记】sql报“聚合或其他set操作消除了null值”处理

    这个警告在常规场景中没什么影响,但如果是用excel跑SQL,它会因为该警告阻止你的后续操作~事实上excel执行sql限制多多,需要更多的奇技淫巧,之前我就写过一篇.言归正传,要解决这个警告,一种当 ...

  10. vb.bet 控件

    TextBox1.BackColor = Color.White'设置控件的背景色(白色) TextBox1.BackColor = Color.Yellow'设置控件的背景色(黃色) TextBox ...