什么是ES6?
ES6:全称 ECMAScript 6.0 ,是 JavaScript 的下一个版本标准,2015.06 发版。
ES6 主要是为了解决 ES5 的先天不足,比如 JavaScript 里并没有类的概念
目前存在少数低版本浏览器的 JavaScript 是 ES5 版本,大多数的浏览器已经支持 ES6
【tips:JavaScript 的正式名称是 ECMAScript 】
1. let 与 const(声明变量)
1.1 let 与 var
- let:ES6新增,用于声明变量,有块级作用域
- var:ES5中用于声明变量的关键字,存在各种问题(例如:套牌车、红杏出墙~)
var存在的问题:
// 1.声明提升
// 此处会正常打印,但这是错误的(属于先上车后买票了!)
console.log(name); // 当脚本开始运行时,name已经存在,但还没赋值,所以会输出undefined
var name = "帅小伙";
// 2.变量覆盖
var demo = "小明";
var demo = "小红";
// 此处会打印小红,这也是错误的(属于套牌车,违法的)
// 同一方法内,发生变量覆盖可能会导致数据丢失以及各种不可预知的bug
// 原则上说:变量不能重名
console.log(demo);
// 3.没有块级作用域
function fn2() {
for(var i = 0; i < 5; i++) {
// do something
}
// 此处会正常打印出 i 的值,这是错误的
// i是定义在循环体之内的,只能在循环体内打印(属于红杏出墙!!!)
console.log(i);
}
fn2();
let不会存在上述问题:
// 1.不会存在声明提前
// 此处会报错(这里必须报错,原则上来说不能先上车后买票)
console.log(name);
let name = "帅小伙";
// 2.不会有变量覆盖
let demo = "小明";
let demo = "小红";
// 此处会报错(不能使用套牌车!)告诉你已经定义了此变量。避免了项目中存在变量覆盖的问题
console.log(demo);
// 3.有块级作用域
function fn2() {
for(let i = 0; i < 5; i++) {
// do something
}
// 此处会报错,无法打印(防止红杏出墙!!!)
// i 是定义在循环体之内的,循环体外当然无法打印
console.log(i);
}
fn2();
1.2 const
- const 声明一个只读的常量,一旦声明,常量的值就不能改变
- 一般用于全局变量
- 通常变量名全部大写(请按照规则来,不要乱搞,容易出事情)
const PI = "3.1415926";
2. 解构赋值
- 解构赋值是对赋值运算符的扩展
- 针对数组或者对象进行模式匹配,然后对其中的变量进行赋值
- 代码简洁且易读,语义更加清晰明了,方便了复杂对象中数据字段获取
2.1 用在数组上
let [a, b, c] = [1, 2, 3];
// a = 1,b = 2,c = 3 相当于重新定义了变量a,b,c,取值也更加方便
// , = 占位符
let arr = ["小明", "小花", "小鱼", "小猪"];
let [,,one] = arr; // 这里会取到小鱼
// 解构整个数组
let strArr = [...arr];
// 得到整个数组
console.log(strArr);
2.2 用在对象上
let obj = {
className: "卡西诺",
age: 18
}
let { className } = obj; // 得到卡西诺
let { age } = obj; // 得到18
// 剩余运算符
let {a, b, ...demo} = {a: 1, b: 2, c: 3, d: 4};
// a = 1
// b = 2
// demo = {c: 3, d: 4}
3. 模板字符串
- 模板字符串相当于加强版的字符串,用反引号 ``
- 除了作为普通字符串,还可以用来定义多行字符串,可以在字符串中加入变量和表达式
3.1 普通字符串
// 普通字符串
let string = "hello"+"小兄弟"; // hello小兄弟
// 如果想要换行
let string = "hello'\n'小兄弟";
// hello
// 小兄弟
3.2 模板字符串(用一对反引号搞定)
let str1 = "穿堂而过的";
let str2 = "风";
// 模板字符串
let newStr = `我是${str1}${str2}`;
console.log(newStr); // 我是穿堂而过的风
// 字符串中调用方法
function fn3() {
return "帅的不行!";
}
let string2= `我真是${fn3 ()}`;
console.log(string2); // 我真是帅的不行!
4. 函数
4.1 箭头函数
- 箭头函数是一种更加简洁的函数书写方式
- 箭头函数本身没有作用域(无this)
- 箭头函数的this指向上一层,上下文决定其this
- 基本语法:参数 => 函数体
4.1.1 基本用法
let fn = val => val;
//等价于
let fn = function(num) {
return num;
}
fn(100); // 输出100
4.1.2 带参数的写法
let fn = (num1, num2) => {
let res = num1 + num2;
return res;
}
fn(3, 2); // 输出5
4.1.3 箭头函数中的this指向问题
- 箭头函数体中的 this 对象,是定义函数时的对象,而不是使用函数时的对象。
- 在函数定义的时候就已经决定了。
function fn3() {
setTimeout(()=> {
// 定义时,this 绑定的是 fn3 中的 this 对象
console.log(this.a);
}, 0)
}
var a = 10;
// fn3 的 this 对象为 {a: 10},因为它指向全局: window.a
fn3.call({a: 18}); // 改变this指向,此时 a = 18
4.1.4 箭头函数适用的场景
- 当我们代码里存在这样的代码:let self = this; 时需要新建变量去保存this的时候
let person = {
'age': 18,
'sayHello': function () {
setTimeout(()=> {
console.log(this.age);
});
}
};
var age = 20;
person.sayHello(); // 18
4.2 函数参数的扩展
4.2.1 默认参数
只有未传递参数或者参数为 undefined 时,才会使用默认参数,null 值被认为是有效的值传递
// num为默认参数,如果不传,则默认为10
function fn(type, num=10) {
console.log(type, num);
}
fn(1); // 打印 1,10
fn(1, 2); // 打印 1,2(此值会覆盖默认参数10)
4.2.2 不定参数
// 此处的values是不定的,且无论你传多少个
function f(...values) {
console.log(values.length);
}
f(1, 2); // 2
f(1, 2, 3, 4); // 4
5. Class类
- class (类)作为对象的模板被引入,可以通过 class 关键字定义类
- class 的本质是 function,同样可以看成一个块
- 可以看作一个语法糖,让对象原型的写法更加清晰
- 更加标准的面向对象编程语法
5.1 类的定义
// 匿名类
let Demo = class {
constructor(a) {
this.a = a;
}
}
// 命名类
let Demo = class Demo {
constructor(a) {
this.a = a;
}
}
5.2 类的声明
- 类不能重复声明
- 类定义不会被提升,必须在访问前对类进行定义,否则就会报错
- 类中方法不需要 function 关键字
- 方法间不能加分号
class Demo {
constructor(a) {
this.a = a;
}
}
5.3 类的主体
// 公共属性(依然可以定义在原型上)
class Demo{}
Demo.prototype.a = 2;
// 实例属性
class Demo {
a = 2;
constructor () {
console.log(this.a);
}
}
// 方法:constructor
class Demo{
constructor(){
console.log('我是构造器');
}
}
new Demo(); // 我是构造器
// 如果不写constructor,也会默认添加
5.4 实例化对象
class Demo {
constructor(a, b) {
this.a = a;
this.b = b;
console.log('Demo');
}
sum() {
return this.a + this.b;
}
}
let demo1 = new Demo(2, 1);
let demo2 = new Demo(3, 1);
// 两者原型链是相等的
console.log(demo1._proto_ == demo2._proto_); // true
demo1._proto_.sub = function() {
return this.a - this.b;
}
console.log(demo1.sub()); // 1
console.log(demo2.sub()); // 2
6. Map()
6.1 Maps 和 Objects 的区别
- 一个 Object 的键只能是字符串或者 Symbols,但一个 Map 的键可以是任意值
- Map 中的键值是有序的(FIFO 原则),而添加到对象中的键则不是
- Map 的键值对个数可以从 size 属性获取,而 Object 的键值对个数只能手动计算
6.2 Map中的key
// 1. key是字符串
let myMap = new Map();
let keyString = "string";
myMap.set(keyString, "和键'string'关联的值");
// keyString === 'string'
myMap.get(keyString); // "和键'string'关联的值"
myMap.get("string"); // "和键'string'关联的值"
// 2.key是对象
let myMap = new Map();
let keyObj = {};
myMap.set(keyObj, "和键 keyObj 关联的值");
myMap.get(keyObj); // "和键 keyObj 关联的值"
myMap.get({}); // undefined, 因为 keyObj !== {}
// 3. key也可以是函数或者NaN
6.3 Map 的迭代
// 1.使用 forEach
let myMap = new Map();
myMap.set(0, "zero");
myMap.set(1, "one");
// 0 = zero , 1 = one
myMap.forEach((value, key) => {
console.log(key + " = " + value);
}, myMap)
// 2. 也可以使用 for...of
for…of 循环可参考:理解 JavaScript 中的 for…of 循环
6.4 Map 与 Array的转换
letkvArray = [["key1", "value1"], ["key2", "value2"]];
// Map 构造函数可以将一个 二维 键值对数组转换成一个 Map 对象
let myMap = new Map(kvArray);
// 使用 Array.from 函数可以将一个 Map 对象转换成一个二维键值对数组
let outArray = Array.from(myMap);
6.5 关于map的重点面试题
Map 和 forEach 的区别
- forEach() 方法不会返回执行结果,而是undefined
- map() 方法会得到一个新的数组并返回
- 在特定场景下,map() 比 forEach() 快一些
性质决定了两者应用场景的不同
// forEach() 适合于你并不打算改变数据的时候,而只是想用数据做一些事情(如存入数据库)
let arr = ['a', 'b', 'c', 'd'];
arr.forEach((val) => {
console.log(val); // 依次打印出 a,b,c,d
});
// map() 适用于你要改变数据值的时候,它更快,而且返回一个新的数组
let arr = [1, 2, 3, 4, 5];
let arr2 = arr.map(num => num * 2).filter(num => num > 5);
// arr2 = [6, 8, 10]
更多内容请参考:阮一峰ES6 入门教程、ES6新特性整理、ES6笔记大全(一篇学完ES6)