ES6是什么
ECMAScript ,是由网景公司制定的一种脚本语言的标准化规范;最初命名为 Mocha ,后来改名为 LiveScript ,最后重命名为 JavaScript
ECMAScript 2015(ES2015),第 6 版,最早被称作 ECMAScript 6(ES6),添加了新的特性
块级作用域
let
- let:ES6新增,用于声明变量,有块级作用域
// 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();
const
- const 声明一个只读的常量,一旦声明,常量的值就不能改变
- 一般用于全局变量
- 通常变量名全部大写(请按照规则来,不要乱搞,容易出事情)
const PI = "3.1415926";
解构赋值
- 解构赋值是对赋值运算符的扩展
- 针对数组或者对象进行模式匹配,然后对其中的变量进行赋值
- 代码简洁且易读,语义更加清晰明了,方便了复杂对象中数据字段获取
数组中使用
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);
对象中使用
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}
模板字符串
- 模板字符串相当于加强版的字符串,用反引号 ``
- 可以定义多行字符串,在字符串中加入变量和表达式
let str1 = "亦~";
let str2 = "乘风";
// 模板字符串
let newStr = `我是${str1}${str2}`;
// 我是亦~乘风
console.log(newStr)
// 字符串中调用方法
function fn3(){
return "帅的掉渣!";
}
let string2= `我真是${fn3 ()}`;
console.log(string2); // 我真是帅的掉渣!
箭头函数
- 是一种更加简洁的函数书写方式
- 箭头函数本身没有作用域
- 箭头函数的this指向上一层
- 语法:参数 => 函数体
//基本用法
let fn = v => v;
//等价于
let fn = function(num){
return num;
}
fn(100); // 输出100
//带参
let fn2 = (num1,num2) => {
let result = num1 + num2;
return result;
}
fn2(3,2); // 输出5
//案例
let Person1 = {
'age': 18,
'sayHello': function () {
setTimeout(()=>{
console.log(this.age);
});
}
};
var age = 25;
Person1.sayHello(); // 18
Class类
1.基本用法
1.1简介:
它是基于原型的面向对象语言。原型对象特点就是将自身的属性共享给新对象。ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。
类中有:constructor构造,有static关键字(声明静态方法),也可以通过 extends 关键字实现继承,也有super 关键字。基本上,ES6 的class可以看作是一个语法糖,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
ES5写法
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);
ES6写法
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
console.log(typeof Point); // "function"
console.log(Point === Point.prototype.constructor); // true
//类完全可以看作构造函数的另一种写法:Point === Point.prototype.constructor
//实际上,类中所有的方法都定义在类的prototype属性上
使用class关键字需要注意的点:
- class内部定义的方法都是不可枚举的,ES5中函数的写法是可以枚举的;
- 生成类的写法需使用 new 命令,否则会报错;
- constructor 方法默认返回实例对象(即 this );
- 类和模块的内部,默认就是严格模式,所以不需要使用 use strict 指定运行模式;
- 类不存在变量提升;
- 类的方法内部如果含有this,它默认指向类的实例。
class Point {
constructor(x, y) {
// ...
}
toString() {
// ...
}
}
Object.keys(Point.prototype)
// []
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]
var Point = function (x, y) {
// ...
};
Point.prototype.toString = function () {
// ...
};
Object.keys(Point.prototype)
// ["toString"]
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]
// 报错
var point = Point(2, 3);
// 正确 使用new
var point = new Point(2, 3);
//不存在变量提示
new Foo(); // ReferenceError
class Foo {}
1.2 constructor 方法和普通方法
constructor() 方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor()方法,如果没有显式定义,一个空的constructor() 方法会被默认添加。
class Point {
}
// 等同于
class Point {
constructor() {}
}
class MyClass {
constructor() {
// ...
}
get prop() {
return 'getter';
}
set prop(value) {
console.log('setter: '+value);
}
}
let inst = new MyClass();
inst.prop = 'hello world';
// setter: hello world
inst.prop
// 'getter'
2.Class表达式
const MyChild = class Child {
toString() {
console.log(Child.name); //name属性总是返回紧跟在class关键字后面的类名。
}
};
//类的名字是MyChild而不是Child,Child只在Class内部代码可用
let mychild = new MyChild();
mychild.toString(); // Child
//如果函数内部用不到Child,也可以省略
const MyChild = class {
// ...
};
3.Class的静态方法
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static
关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
class Foo {
static className() {
console.log("flyWind");
}
}
Foo.className(); //flyWind 不能通过实例调用会报错
// 注意,如果静态方法包含this关键字,这个this指的是类,而不是实例。
class Foo {
static bar() {
this.baz();
}
static baz() {
console.log("hello");
}
baz() {
console.log("world");
}
}
Foo.bar(); // hello
// 1.静态方法bar调用了this.baz,这里的this指的是Foo类,而不是Foo的实例,等同于调用Foo.baz。
// 2.静态方法可以与非静态方法重名。
// 父类的静态方法,可以被子类继承。
class Foo {
static classMethod() {
return "hello";
}
}
class Bar extends Foo {}
Bar.classMethod(); // 'hello'
4.Class的继承
class Parent {
constructor(name, age) {
this.name = name;
this.age = age;
}
toString() {
console.log("年龄:" + this.age + "姓名:" + this.name);
}
}
class Child extends Parent {
constructor(name, age, height) {
super(name, age); //调用父类的constructor(构造方法),子类必须在 constructor 方法中调用 super 方法,使子类获得自己的 this 对象,否则新建实例时会报错。
this.height = height;
}
sayInfo() {
super.toString(); // 调用父类的toString()
console.log(`身高:${this.height}`);
}
}
var person = new Child("flyWind", 24, 175);
person.sayInfo(); //姓名:flyWind 年龄:24 身高:175
// 父类的静态方法,也会被子类继承。
class A {
static hello() {
console.log("hello world");
}
}
class B extends A {}
B.hello(); // hello world
// hello()是A类的静态方法,B继承A,也继承了A的静态方法。
5.super关键字
super这个关键字,既可以当作函数使用,也可以当作对象使用。
super 作为函数时
- super 作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次 super 函数。
- super 虽然代表了父类 A 的构造函数,但是返回的是子类 B 的实例,即 super 内部的 this 指的是 B
- super()只能用在子类的构造函数之中,用在其他地方就会报错。
class A {}
class B extends A {
constructor() {
super();
}
}
// 注意, super虽然代表了父类A的构造函数, 但是返回的是子类B的实例,
//即super内部的this指的是B,因此super() 在这里相当于A.prototype.constructor.call(this)。
super 作为对象时
- 在普通方法中指向父类的原型对象,在静态方法中指向父类
- super 指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过 super 调用的。
- 在子类普通方法中通过 super 调用父类的方法时,方法内部的this 指向当前的子类实例。
- super 作为对象,用在静态方法之中,这时super 将指向父类,在普通方法之中指向父类的原型对象。
class A {}
class B extends A {
constructor() {
super();
console.log(super); // 报错
}
}
class A {}
class B extends A {
constructor() {
super();
console.log(super.valueOf() instanceof B); // true
}
}
let b = new B();
//上面代码中,super.valueOf()表明super是一个对象,因此就不会报错。同时,由于super使得this指向B的实例,所以super.valueOf()返回的是一个B的实例。
6.ES6中的Class实现
ES6中Class的底层还是通过构造函数去创建的。有兴趣的可以使用Babel去转换代码(Babel是一个工具链,主要用于在当前和较旧的浏览器或环境中将ECMAScript 2015+代码转换为JavaScript的向后兼容版本-https://babeljs.io/repl)
转换前:
class Parent {
constructor(a){
this.filed1 = a;
}
filed2 = 2;
func1 = function(){}
}
经过babel转码之后:
//调用_classCallCheck方法判断当前函数调用前是否有new关键字,若构造函数前面没有new则构造函数的prototype不会出现在this的原型链上,返回false
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
var Parent = function Parent(a) {
_classCallCheck(this, Parent);
_defineProperty(this, "filed2", 2);
_defineProperty(this, "func1", function () {});
this.filed1 = a;
};
执行过程:
- 调用_classCallCheck方法判断当前函数调用前是否有new关键字,若构造函数前面没有new则构造函数的prototype不会出现在this的原型链上,返回false
- 将class内部的变量及函数赋值给this
- 执行constructor内部的逻辑
Map()
map和 object的区别
- 一个 Object 的键只能是字符串或者 Symbols,但一个 Map 的键可以是任意值
- Map 中的键值是有序的(FIFO 原则),而添加到对象中的键则不是
- Map 的键值对个数可以从 size 属性获取,而 Object 的键值对个数只能手动计算
Map中的key
// 1. key是字符串
let myMap = new Map();
let keyString = "string";
myMap.set(keyString, "亦~乘风");
// keyString === 'string'
myMap.get(keyString); // "亦~乘风"
myMap.get("string"); // "亦~乘风"
// 2.key是对象
let myMap = new Map();
let keyObj = {},
myMap.set(keyObj, "乘风");
myMap.get(keyObj); // "乘风"
myMap.get({}); // undefined, 因为 keyObj !== {}
// 3. key也可以是函数或者NaN
Map的迭代
// 1.使用 forEach
let myMap = new Map();
myMap.set(0, "yes");
myMap.set(1, "no");
// 0 = yes , 1 = no
myMap.forEach(function(value, key) {
console.log(key + " = " + value);
}, myMap)
// 2. 也可以使用 for...of
Map与Array的转换
letkvArray = [["key1", "value1"], ["key2", "value2"]];
// Map 构造函数可以将一个 二维 键值对数组转换成一个 Map 对象
let myMap = new Map(kvArray);
// 使用 Array.from 函数可以将一个 Map 对象转换成一个二维键值对数组
let outArray = Array.from(myMap);
在这说一些题外话比如关于map的面试题
问:请谈一下 Map和ForEach 的区别(问到map,必定问到此题)
答:
forEach()方法不会返回执行结果,而是undefined
map()方法会得到一个新的数组并返回
同样的一组数组,map()的执行速度优于 forEach()(map() 底层做了深度优化)