一 ES6简介
1.ES6是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言
2.ES6 与 ECMAScript 2015 的关系
ES6 既是一个历史名词,也是一个泛指,含义是 5.1 版以后的 JavaScript 的下一代标准,涵盖了 ES2015、ES2016、ES2017 等等,而 ES2015 则是正式名称,特指该年发布的正式版本的语言标准。
3.Babel 转码器
Babel 是一个广泛使用的 ES6 转码器,可以将 ES6 代码转为 ES5 代码,从而在老版本的浏览器执行。这意味着,你可以用 ES6 的方式编写程序,又不用担心现有环境是否支持
// 转码前
input.map(item => item + 1);
// 转码后
input.map(function (item) {
return item + 1;
});
二 let 和 const 命令
1.ES6 新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。
{
let a = 10;
var b = 1;
console.log(a); //10
}
console.log(a); //undefined
console.log(b); // 1
2.let 不存在变量提升,let命令改变了语法行为,它所声明的变量一定要在声明后使用,否则报错。
console.log(a); // 输出undefined
var a = 2;
// let 的情况
console.log(b); // 报错ReferenceError
let b = 2;
3.暂时性死区
只要块级作用域内存在let命令,它所声明的变量就“绑定”这个区域,不再受外部的影响。
在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。
var num = 111;
if(num<122){
num = 122;
let num;
console.log(num); // Cannot access 'num' before initialization
}
4.不允许重复声明
function creat() {
let a = 10;
var a = 1;
console.log(a); // 报错'a' has already been declared
}
function creat() {
let a = 10;
let a = 1;
console.log(a); // 报错'a' has already been declared
}
// 不能在函数内部重新声明参数。
function creat(a ) {
let a = 10;
console.log(a); // 报错'a' has already been declared
}
creat();
//正确写法
function creat(a) {
console.log(a); //6
{
let a =5;
// console.log(a); //5
}
}
creat(6);
5.const 命令
const声明一个只读的常量。一旦声明,常量的值就不能改变。
const a = 323;
console.log(a); //323
//声明的常量不能 修改
a = 500;
console.log(a); //TypeError: Assignment to constant variable.
const的作用域与let命令相同:只在声明所在的块级作用域内有效。
const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。
if (true) {
console.log(num); // 报错
const num = 5;
}
//不能重复声明
let name = 'rose';
let age = 25;
const name = 'lisa';
const age = 23;
三 变量的解构赋值
1.变量赋值
//以前的写法
let a = 1;
let b = 2;
let c = 3;
// ES6 允许写成下面这样。
let [a, b, c] = [1, 2, 3];
2.数组解构
let [a, [[b], c]] = [1, [[2], 3]];
a //1
b //2
c //3
let [ , , 1] = ["a", "b", "c"];
c //1
let [x, , y] = [1, 2, 3];
x // 1
y // 3
let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]
let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []
如果解构不成功,变量的值就等于undefined。
let [a] = []; // undefined
let [a, b] = [1]; // undefined
2.对象的解构赋值
let { a, b} = { a: 'aaa', b: 'bbb' };
a// "aaa"
b// "bbb"
3.字符串的解构赋值
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
4.函数参数的解构赋值
function add([x, y]){
return x + y;
}
add([1, 2]); // 3
5.扩展运算符
...扩展运算符
function foo(a,b,c,d,e,f){
console.log(a+b+c+d+e+f);
}
let arr=[1,2,3,4,5,6];
foo(...arr);
6.rest 剩余参数
function foo(a,b,...rest){
console.log(rest) //345
}
foo(1,2,3,4,5);
四 字符串的扩展
1.字符串的遍历器接口
for (let ele of 'hello') {
console.log(ele)
}
// h
//e
//l
//l
//o
2.JSON.stringify() 的改造
根据标准,JSON 数据必须是 UTF-8 编码。但是,现在的JSON.stringify()方法有可能返回不符合 UTF-8 标准的字符串
为了确保返回的是合法的 UTF-8 字符,ES2019 改变了JSON.stringify()的行为。如果遇到0xD800到0xDFFF之间的单个码点,或者不存在的配对形式,它会返回转义字符串,留给应用自己决定下一步的处理。
JSON.stringify('\u{D834}') // ""\\uD834""
JSON.stringify('\uDF06\uD834') // ""\\udf06\\ud834""
3.模板字符串
模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
// 字符串中嵌入变量
let name = "lisa", time = "today";
console.log(`Hello ${name}, how are you ${time}?`) //Hello lisa, how are you today?
模板字符串,都是用反引号表示。如果在模板字符串中需要使用反引号,则前面要用反斜杠转义。
let hi = `\`hello\` World!`;
console.log(hi);
所有模板字符串的空格和换行,都是被保留的,比如
- 标签前面会有一个换行。如果你不想要这个换行,可以使用trim方法
$('#list').html(`
<ul>
<li>first</li>
<li>second</li>
</ul>
`.trim());
五 字符串的新增方法
1.实例方法 :normalize()
ES6 提供字符串实例的normalize()方法,用来将字符的不同表示方法统一为同样的形式,这称为 Unicode 正规化。
'\u01D1'.normalize() === '\u004F\u030C'.normalize()
// true
2.实例方法:includes(), startsWith(), endsWith()
传统上,JavaScript 只有indexOf方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6 又提供了三种新方法。
includes():返回布尔值,表示是否找到了参数字符串。
startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
let s = 'Hello world!';
s.startsWith('Hello') // true
s.endsWith('!') // true
s.includes('o') // true
六 函数的扩展
ES6 允许使用“箭头”(=>)定义函数。
1.箭头函数的创建以及使用
var f = v => v;
// 等同于
var f = function (v) {
return v;
};
function foo(){
console.log('hello');
}
foo();
// 使用箭头函数
let foo=()=>{
console.log('hello');
}
foo();
使用注意点
箭头函数有几个使用注意点。
(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
2.箭头函数 循环遍历 数组中的数据
let arr=[123,456,789];
arr.forEach(function(ele,index){
console.log(ele,index);
})
3.rest 参数
ES6 引入 rest 参数(形式为…变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3) // 10
七 Set 和 Map 数据结构
1.Set本身是一个构造函数,用来生成 Set 数据结构。
const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
for (let i of s) {
console.log(i); // 2 3 5 4
}
2.ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。
const m = new Map();
const o = {p: 'Hello World'};
m.set(o, 'content')
m.get(o) // "content"
m.has(o) // true
m.delete(o) // true
m.has(o) // false
与其他数据结构的互相转换
(1)Map 转为数组
new Map([
[true, 7],
[{foo: 3}, ['abc']]
])
(2)数组 转为 Map
function strMapToObj(strMap) {
let obj = Object.create(null);
for (let [k,v] of strMap) {
obj[k] = v;
}
return obj;
}
const myMap = new Map()
.set('yes', true)
.set('no', false);
strMapToObj(myMap)
(3)Map 转为对象
let obj = {"a":1, "b":2};
let map = new Map(Object.entries(obj));
八 Class 的基本语法
1.JavaScript 语言中,生成实例对象的传统方法是通过构造函数
构造函数
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function () {
return '(' + this.x + ', ' + this.y + ')';
};
var p = new Point(1, 2);
2.新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用 ES6 的class改写
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
3.constructor 方法
constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。
class Point {
}
// 等同于
class Point {
constructor() {}
}
九 Class 的继承
1.Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。
class Point {
}
class ColorPoint extends Point {
}
//ColorPoint类,该类通过extends关键字,继承了Point类的所有属性和方法
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // 调用父类的constructor(x, y)
this.color = color;
}
toString() {
return this.color + ' ' + super.toString(); // 调用父类的toString()
}
}
注意:只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,基于父类实例,只有super方法才能调用父类实例。
2.super 关键字
super这个关键字,既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同。
第一种情况,super作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数。
class A {
constructor() {
console.log(new.target.name);
}
}
class B extends A {
constructor() {
super();
}
}
new A() // A
new B() // B
3.类的 prototype 属性和__proto__属性
大多数浏览器的 ES5 实现之中,每一个对象都有__proto__属性,指向对应的构造函数的prototype属性。Class 作为构造函数的语法糖,同时有prototype属性和__proto__属性,因此同时存在两条继承链。
(1)子类的__proto__属性,表示构造函数的继承,总是指向父类。
(2)子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。