ES6 入门
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 明确规定,如果区块中存在let
和const
命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。总之,在代码块内,使用let
命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。
- if (true) {
- // TDZ开始
- tmp = 'abc'; // ReferenceError
- console.log(tmp); // ReferenceError
- let tmp; // TDZ结束
- console.log(tmp); // undefined
- tmp = 123;
- console.log(tmp); //
- }
上面代码中,在let
命令声明变量tmp
之前,都属于变量tmp
的“死区”。
“暂时性死区”也意味着typeof
不再是一个百分之百安全的操作,如下:
- typeof x; // ReferenceError
- let x;
上面代码中,变量x
使用let
命令声明,所以在声明之前,都属于x
的“死区”,只要用到该变量就会报错。因此,typeof
运行时就会抛出一个ReferenceError。
不允许重复声明
let
不允许在相同作用域内,重复声明同一个变量。如下:
- // 报错
- function func() {
- let a = 10;
- var a = 1;
- }
- // 报错
- function func() {
- let a = 10;
- let a = 1;
- }
因此,不能在函数内部重新声明参数。
- function func(arg) {
- let arg;
- }
- func() // 报错
- function func(arg) {
- {
- let arg;
- }
- }
- func() // 不报错
3、变量的解构赋值
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
- 数组的结构赋值
以前,为变量赋值,只能直接指定值:
- let a = 1;
- let b = 2;
- let c = 3;
ES6 写法:
- let [a, b, c] = [1, 2, 3];
这种写法属于“模式匹配”,等号两边的模式相同。也有嵌套数组进行结构的例子,如下:
- let [foo, [[bar], baz]] = [1, [[2], 3]];
- foo //
- bar //
- baz //
- let [ , , third] = ["foo", "bar", "baz"];
- third // "baz"
- let [x, , y] = [1, 2, 3];
- x //
- y //
- let [head, ...tail] = [1, 2, 3, 4];
- head //
- tail // [2, 3, 4]
- let [x, y, ...z] = ['a'];
- x // "a"
- y // undefined
- z // []
如果结构不成功,变量的值则为 undefined.
- let [foo] = [];
- let [bar, foo] = [1];
以上两种情况都属于解构不成功,foo
的值都会等于undefined
。
另一种情况是不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功。
- let [x, y] = [1, 2, 3];
- x //
- y //
- let [a, [b], d] = [1, [2, 3], 4];
- a //
- b //
- d //
- 对象的解构赋值
结构不仅可以用于数组,还可以用于对象。
- let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
- foo // "aaa"
- bar // "bbb"
对象与数组的解构有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性的同名,才能取到正确的值。
- let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
- foo // "aaa"
- bar // "bbb"
- let { baz } = { foo: 'aaa', bar: 'bbb' };
- baz // undefined
对象的解构赋值,可以很方便的将现有对象的方法赋值到某个变量。
- // 例一
- let { log, sin, cos } = Math;
- // 例二
- const { log } = console;
- log('hello') // hello
上面代码的例一将Math
对象的对数、正弦、余弦三个方法,赋值到对应的变量上,使用起来就会方便很多。例二将console.log
赋值到log
变量。
如果变量名与属性名不一致,必须写成下面这样:
- let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
- baz // "aaa"
- let obj = { first: 'hello', last: 'world' };
- let { first: f, last: l } = obj;
- f // 'hello'
- l // 'world'
对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。
- let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
- baz // "aaa"
- foo // error: foo is not defined
上面代码中,foo
是匹配的模式,baz
才是变量。真正被赋值的是变量baz
,而不是模式foo
。
与数组一样,解构也可以用于嵌套结构的对象。
- let obj = {
- p: [
- 'Hello',
- { y: 'World' }
- ]
- };
- let { p: [x, { y }] } = obj;
- x // "Hello"
- y // "World"
注意,这时p
是模式,不是变量,因此不会被赋值。如果p
也要作为变量赋值,可以写成下面这样。
- let obj = {
- p: [
- 'Hello',
- { y: 'World' }
- ]
- };
- let { p, p: [x, { y }] } = obj;
- x // "Hello"
- y // "World"
- p // ["Hello", {y: "World"}]
- 字符串的解构赋值
字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象。
- const [a, b, c, d, e] = 'hello';
- a // "h"
- b // "e"
- c // "l"
- d // "l"
- e // "o"
类似数组的对象都有一个length
属性,因此还可以对这个属性解构赋值。
- let {length : len} = 'hello';
- len //
- 数值与布尔值的解构赋值
解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。
- let {toString: s} = 123;
- s === Number.prototype.toString // true
- let {toString: s} = true;
- s === Boolean.prototype.toString // true
上面代码中,数值和布尔值的包装对象都有toString
属性,因此变量s
都能取到值。
解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined
和null
无法转为对象,所以对它们进行解构赋值,都会报错。
- let { prop: x } = undefined; // TypeError
- let { prop: y } = null; // TypeError
- 函数参数的解构赋值
- function add([x, y]){
- return x + y;
- }
- add([1, 2]); //
- [[1, 2], [3, 4]].map(([a, b]) => a + b);
- // [ 3, 7 ]
- function move({x = 0, y = 0} = {}) {
- return [x, y];
- }
- move({x: 3, y: 8}); // [3, 8]
- move({x: 3}); // [3, 0]
- move({}); // [0, 0]
- move(); // [0, 0]
变量解构赋值的用途:
- 交换变量的值
- let x = 1;
- let y = 2;
- [x, y] = [y, x];
- 从函数返回多个值
- // 返回一个数组
- function example() {
- return [1, 2, 3];
- }
- let [a, b, c] = example();
- // 返回一个对象
- function example() {
- return {
- foo: 1,
- bar: 2
- };
- }
- let { foo, bar } = example();
- 函数参数的定义
- // 参数是一组有次序的值
- function f([x, y, z]) { ... }
- f([1, 2, 3]);
- // 参数是一组无次序的值
- function f({x, y, z}) { ... }
- f({z: 3, y: 2, x: 1});
- 提取JSON数据
- let jsonData = {
- id: 42,
- status: "OK",
- data: [867, 5309]
- };
- let { id, status, data: number } = jsonData;
- console.log(id, status, number);
- // 42, "OK", [867, 5309]
- 函数参数的默认值
- jQuery.ajax = function (url, {
- async = true,
- beforeSend = function () {},
- cache = true,
- complete = function () {},
- crossDomain = false,
- global = true,
- // ... more config
- } = {}) {
- // ... do stuff
- };
- 遍历Map解构
任何部署了 Iterator 接口的对象,都可以用for...of
循环遍历。Map 结构原生支持 Iterator 接口,配合变量的解构赋值,获取键名和键值就非常方便。
- const map = new Map();
- map.set('first', 'hello');
- map.set('second', 'world');
- for (let [key, value] of map) {
- console.log(key + " is " + value);
- }
- // first is hello
- // second is world
如果只想获取键名,或者只想获取键值,可以写成下面这样。
- // 获取键名
- for (let [key] of map) {
- // ...
- }
- // 获取键值
- for (let [,value] of map) {
- // ...
- }
- 输入模块的指定方法
加载模块时,往往需要指定输入哪些方法。解构赋值使得输入语句非常清晰。
- const { SourceMapConsumer, SourceNode } = require("source-map");
4、字符串扩展
- includes(), startsWith(), endsWith()
传统上,JavaScript 只有indexOf
方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6 又提供了三种新方法。
- includes():返回布尔值,表示是否找到了参数字符串。
- startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
- endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
- let s = 'Hello world!';
- s.startsWith('Hello') // true
- s.endsWith('!') // true
- s.includes('o') // true
这三个方法都支持第二个参数,表示开始搜索的位置。
- let s = 'Hello world!';
- s.startsWith('world', 6) // true
- s.endsWith('Hello', 5) // true
- s.includes('Hello', 6) // false
- repeat()
repeat
方法返回一个新字符串,表示将原字符串重复n
次.
- 'x'.repeat(3) // "xxx"
- 'hello'.repeat(2) // "hellohello"
- 'na'.repeat(0) // ""
如果repeat
的参数是字符串,则会先转换成数字。
- 'na'.repeat(2.9) // "nana"
- 'na'.repeat(Infinity)
- // RangeError
- 'na'.repeat(-1)
- // RangeError
- 'na'.repeat(-0.9) // ""
- 'na'.repeat('na') // ""
- 'na'.repeat('3') // "nanana"
- padStart() padEnd()
ES2017 引入了字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。padStart()
用于头部补全,padEnd()
用于尾部补全。
- 'x'.padStart(5, 'ab') // 'ababx'
- 'x'.padStart(4, 'ab') // 'abax'
- 'x'.padEnd(5, 'ab') // 'xabab'
- 'x'.padEnd(4, 'ab') // 'xaba'
padStart()
的常见用途是为数值补全指定位数。下面代码生成 10 位的数值字符串。
- '1'.padStart(10, '0') // "0000000001"
- '12'.padStart(10, '0') // "0000000012"
- '123456'.padStart(10, '0') // "0000123456"
另一个用途是提示字符串格式。
- '12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12"
- '09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"
- 模板字符串(template string)
传统的 JavaScript 语言,输出模板通常是这样写的(下面使用了 jQuery 的方法)。
- $('#result').append(
- 'There are <b>' + basket.count + '</b> ' +
- 'items in your basket, ' +
- '<em>' + basket.onSale +
- '</em> are on sale!'
- );
上面这种写法相当繁琐不方便,ES6 引入了模板字符串解决这个问题。
- $('#result').append(`
- There are <b>${basket.count}</b> items
- in your basket, <em>${basket.onSale}</em>
- are on sale!
- `);
模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
- // 普通字符串
- `In JavaScript '\n' is a line-feed.`
- // 多行字符串
- `In JavaScript this is
- not legal.`
- console.log(`string text line 1
- string text line 2`);
- // 字符串中嵌入变量
- let name = "Bob", time = "today";
- `Hello ${name}, how are you ${time}?`
上面代码中的模板字符串,都是用反引号表示。如果在模板字符串中需要使用反引号,则前面要用反斜杠转义。
- let greeting = `\`Yo\` World!`;
如果使用模板字符串表示多行字符串,所有的空格和缩进都会被保留在输出之中。
- $('#list').html(`
- <ul>
- <li>first</li>
- <li>second</li>
- </ul>
- `);
上面代码中,所有模板字符串的空格和换行,都是被保留的,比如<ul>
标签前面会有一个换行。如果你不想要这个换行,可以使用trim
方法消除它。
- $('#list').html(`
- <ul>
- <li>first</li>
- <li>second</li>
- </ul>
- `.trim());
模板字符串中嵌入变量,需要将变量名写在${}
之中。
- function authorize(user, action) {
- if (!user.hasPrivilege(action)) {
- throw new Error(
- // 传统写法为
- // 'User '
- // + user.name
- // + ' is not authorized to do '
- // + action
- // + '.'
- `User ${user.name} is not authorized to do ${action}.`);
- }
- }
大括号内部可以放入任意的 JavaScript 表达式,可以进行运算,以及引用对象属性。
- let x = 1;
- let y = 2;
- `${x} + ${y} = ${x + y}`
- // "1 + 2 = 3"
- `${x} + ${y * 2} = ${x + y * 2}`
- // "1 + 4 = 5"
- let obj = {x: 1, y: 2};
- `${obj.x + obj.y}`
- // "3"
模板字符串之中还能调用函数。
- function fn() {
- return "Hello World";
- }
- `foo ${fn()} bar`
- // foo Hello World bar
如果大括号中的值不是字符串,将按照一般的规则转为字符串。比如,大括号中是一个对象,将默认调用对象的toString
方法。
- // 变量place没有声明
- let msg = `Hello, ${place}`;
- // 报错
- `Hello ${'World'}`
- // "Hello World"
模板字符串甚至还能嵌套。
- const tmpl = addrs => `
- <table>
- ${addrs.map(addr => `
- <tr><td>${addr.first}</td></tr>
- <tr><td>${addr.last}</td></tr>
- `).join('')}
- </table>
- `;
上面代码中,模板字符串的变量之中,又嵌入了另一个模板字符串,使用方法如下。
- const data = [
- { first: '<Jane>', last: 'Bond' },
- { first: 'Lars', last: '<Croft>' },
- ];
- console.log(tmpl(data));
- // <table>
- //
- // <tr><td><Jane></td></tr>
- // <tr><td>Bond</td></tr>
- //
- // <tr><td>Lars</td></tr>
- // <tr><td><Croft></td></tr>
- //
- // </table>
如果需要引用模板字符串本身,在需要时执行,可以写成函数。
- let func = (name) => `Hello ${name}!`;
- func('Jack') // "Hello Jack!"
ES6 入门的更多相关文章
- ES6入门笔记
ES6入门笔记 02 Let&Const.md 增加了块级作用域. 常量 避免了变量提升 03 变量的解构赋值.md var [a, b, c] = [1, 2, 3]; var [[a,d] ...
- es6入门4--promise详解
可以说每个前端开发者都无法避免解决异步问题,尤其是当处理了某个异步调用A后,又要紧接着处理其它逻辑,而最直观的做法就是通过回调函数(当然事件派发也可以)处理,比如: 请求A(function (请求响 ...
- es6入门3--箭头函数与形参等属性的拓展
对函数拓展兴趣更大一点,优先看,前面字符串后面再说,那些API居多,会使用能记住部分就好. 一.函数参数可以使用默认值 1.默认值生效条件 在变量的解构赋值就提到了,函数参数可以使用默认值了.正常我们 ...
- Vue+koa2开发一款全栈小程序(1.课程介绍+2.ES6入门)
1.课程介绍 1.课程概述 1.做什么? Vue+koa2开发一款全栈小程序 2.哪些功能? 个人中心.图书列表.图书详情.图书评论.个人评论列表 3.技术栈 小程序.Vue.js.koa2.koa- ...
- es6入门5--class类的基本用法
在ES6之前,准确来说JavaScript语言并无类的概念,却有模拟类的做法.相比在类似java这类传统面向对象语言中通过类来生成实例,js则通过构造函数模拟类来生成实例. 这是因为在JS设计初期,作 ...
- es6入门6--数组拓展运算符,Array.from()基本用法
本文只是作为ES6入门第九章学习笔记,在整理知识点的同时,会加入部分个人思考与解答,若想知道更详细的介绍,还请阅读阮一峰大神的ES6入门 一.拓展运算符 ES6中新增了拓展运算(...)三个点,它的作 ...
- ES6入门之let和const命令
前言 大家好,我是一只流浪的kk,当你看到这边博客的时候,说明你已经进入了ES6学习的领域了,从本篇博客开始,我将会将自己学习到ES6的相关知识进行整理,方便大家参考和学习,那么我将带你进入第一节的内 ...
- ES6入门之变量的解构赋值(二)
前言 在上一章 ES6入门之let和const命令中我们对ES6的相关语法已经有了初步了解,上一章中我们主要学习了三大部分的内容,let命令的使用,块级作用域,const命令的使用,那么从本篇博客将进 ...
- ES6入门十二:Module(模块化)
webpack4打包配置babel7转码ES6 Module语法与API的使用 import() Module加载实现原理 Commonjs规范的模块与ES6模块的差异 ES6模块与Nodejs模块相 ...
- es6入门7--Set Map数据结构
本文作为ES6入门第十三章的学习整理笔记,可能会包含少部分个人的理解推测,若想阅读更详细的介绍,还请阅读原文ES6入门 一.set数据结构 1.set不接受重复值 ES6新增了Set构造函数用于创建s ...
随机推荐
- 使用webpack将es6 es7转换成es2015
第一步:安装模块化包 cnpm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react 第二 ...
- HDFS-Architecture剖析
1.概述 从HDFS的应用层面来看,我们可以非常容易的使用其API来操作HDFS,实现目录的创建.删除,文件的上传下载.删除.追加(Hadoop2.x版本以后开始支持)等功能.然而仅仅局限与代码层面是 ...
- SQL 必知必会·笔记<7>汇总数据——使用聚合函数
有时候我们需要对表中的数据进行汇总,而不需要数据本身,为了方便这些类型的检索,SQL给出了5个聚合函数,SQL聚合函数在各主要的SQL实现中得到了相当一致的支持.如下: 1.1 AVG()函数 AVG ...
- 读vue-0.6-text-parser.js源码
提取字符串中的表达式 var BINDING_RE = /\{\{(.+?)\}\}/; function parse(text) { // 找不到返回null if (!BINDING_RE.tes ...
- PostgreSQL 数据类型
数值类型 数值类型由两个字节,4字节和8字节的整数,4字节和8字节的浮点数和可选精度的小数.下表列出了可用的类型. www.yiibai.com Name Storage Size Descripti ...
- Java设计模式学习记录-外观模式
前言 这次要介绍的是外观模式(也称为门面模式),外观模式也属于结构型模式,其实外观模式还是非常好理解的,简单的来讲就是将多个复杂的业务封装成一个方法,在调用此方法时可以不必关系具体执行了哪些业务,而只 ...
- c语言中阶乘的精确值
对于大数的操作,可能超出int,甚至long的表示范围,对此,可以使用数组来存储大数,下列代码为求1000以内数的阶乘的代码,代码如下: #include <stdio.h> #inclu ...
- python取余
a=-7,b=3, a % b = 2 #取余 a-((a/b)*b) a / b= -3 #整除 int(math.floor(-7/3.0))
- 【手记】sql报“聚合或其他set操作消除了null值”处理
这个警告在常规场景中没什么影响,但如果是用excel跑SQL,它会因为该警告阻止你的后续操作~事实上excel执行sql限制多多,需要更多的奇技淫巧,之前我就写过一篇.言归正传,要解决这个警告,一种当 ...
- vb.bet 控件
TextBox1.BackColor = Color.White'设置控件的背景色(白色) TextBox1.BackColor = Color.Yellow'设置控件的背景色(黃色) TextBox ...