ECMAScript 6

2024-01-10 12:34:11

ECMAScript 6

ES6 全称ECMAScript 6 也叫ECMAScript2015

ES6的主要技术

1、变量、常量

2、取值赋值、解构

3、运算符的扩展、展开运算符、指数运算符

4、字符串的扩展

5、数组的扩展

6、函数的扩展

7、Set单值集合

8、Map键值对集合

9、对象的扩展

10、生成器和迭代器与迭代器接口

11、反射Reflect

12、代理Proxy

13、Promise异步处理、async与await(ES7)

14、ES6的模块化开发ESModule

15、CommonJS模块化

let定义变量

之前我们在ES5里面,我们定义变量使用的是var关键字,这个关键字有几个特点

1、var 没有数据类型

2、var 是有一个建立阶段

3、var 是没有作用域的,它要通过函数才能形成作用域

var a = 1;  //没有数据类型的,所以任何类型的数据都可以赋值

//var没有作用域
{
    var a = "哈哈"
}
console.log(a);

//var有一个建立阶段
console.log(c);
var c = 123;

var定义的变量是不够严谨的

ES6推出了let用于定义变量

1、let定义的变量也没有数据类型

2、let没有建立阶段,必须先定义再使用

3、let定义的变量是有作用域的,以大括号作为作用域

暂时性死区

let定义变量是有作用域的,并且不能再定义之前调用

let a = "hello";
{
    console.log(a);  //这里是没有问题的,因为内部的可以调用外部的变量
}

let b = "world";
{
    console.log(b);   //这里就形成了一个暂时性死区,b变量调不出来
    let b = "hello";
}

const常量

常量里面的数据是不能改变的,比如,Math.PI

const关键字声明常量,它具备上面的let的所有特点,同时不能改变值

const a = "hahaha";
a = "hehehehe";    //报错
const声明的常量锁栈不锁堆

首先,我们先要知道内存中的数据结构分为 栈、堆、表、链

如果const声明的常量里面保存的是一个引用数据,那么根据锁栈不锁堆的原则,我们是可以改变数据中的属性值的,但是无法覆盖之前的整个数据

const常量定义的时候一定记得初始化,不然没有任何意义

let与const的闭包特性

var btn = document.querySelectorAll("button");
for(var i = 0; i < btn.length;i++){
    btn[i].addEventListener("click",function(){
        console.log(i);
    })
}

ES5闭包解决

for(var i = 0; i < btn.length;i++){
    btn[i].addEventListener("click",(function(j){
        var j = i;
        return function(){
            console.log(j)
        }
    })(i))
}

ES6闭包解决

var btn = document.querySelectorAll("button");
for(let i = 0; i < btn.length;i++){
    btn[i].addEventListener("click",function(){
        console.log(i);
    })
}

解构

一种特殊的取值赋值的方式

解构取值:

1、数组解构取值

var arr = ["张三","李四"];
// arr[0];
// arr[1];

let [a,b] = arr;
console.log(a);
console.log(b);

对于复杂的数组可以实现深度解构

var arr = ["张三","李四",["王五","赵六"]];
// arr[0];
// arr[1];

let [a,b,[c,d]] = arr;
console.log(a);
console.log(b);
console.log(c);
console.log(d);

解构实现变量置换

let a = 10;
let b = 20;
[a,b] = [b,a];
console.log(a,b);
对象的解构取值
var userInfo = {
    userName:"张三",
    age:19,
    sex:"男"
}

let {userName,age,sex} = userInfo;
解构赋值
let userName = "张三";
let age = 18;
let userInfo = {
    userName,
    age
}

展开运算符

在ES6里面,新增了展开运算符,所有实现了Iterable接口的都可以使用展开运算符,目前来讲在系统中ES里面实现了这个接口的数据如下

1、数组

2、NodeList

3、HTMLCollection

4、Set

5、Map

6、arguments

7、字符串

展开运算符使用 … 来完成运算

var arr = [1,2,3,4,5,6,7];
console.log(...arr);

//展开运算获取最多的数字
var max1 = Math.max(...arr);
console.log(max1);

//数组的深拷贝
var a = 1;
var b;
b = a;
var newArr = [...arr];

//实现数组的合并
var arr1 = ["A","b","c","d"];
var arr2 = [...arr,...arr1];

let obj = {
    userName:"张三",
    age:18
}
let obj1 = {
    hobby:"sleep"
}

//Object.assgin()
//实现对象的合并
let obj2 = {
    ...obj,
    ...obj1
}

字符串扩展

模板字符串,模板字符串自带格式,你格式什么样子打印出来就是什么样子

var str = `亲爱的张三:
                你好!
                恭喜你,已中本公司大奖,价值9999元的大礼包直接抗回家!
                你只需要缴纳1999元的保证金就可以领取大奖
            电话:1234566789
            联系人:李四

                                                    XXX公司
                                                XXXX年XX月XX日`
console.log(str);

模板字符串另外一个特点可以快速实现字符串拼接

let nickName = "张三";
let tel = 110;
let connectPerson = "李四"
let company = "画大饼公司"
var str = `亲爱的${nickName}:
                你好!
                恭喜你,已中本公司大奖,价值9999元的大礼包直接抗回家!
                你只需要缴纳1999元的保证金就可以领取大奖
            电话:${tel}
            联系人:${connectPerson}

                                                    ${company}公司
                                                XXXX年XX月XX日`
console.log(str);

模板字符串内部可以使用 ${ } 在大括号里面可以写任意的JS代码

function abc(){
    console.log("haha")
}
abc``   //可以替代函数调用时使用的()
字符串扩展方法

1、include() 判断当前字符串中是否包含某一个字符串,以前可以使用indexOf方法

2、repeat() 生成一个重复字符串

3、trimStart() / trimEnd() 去除开始与结束的空格

数组的扩展方法

1、Array.of()

声明数组的方法,它可以解决一个问题 new Array() 所存在的问题

var arr = new Array(2);   //代表这个数组的长度是2
var arr1 = Array.of(2);

2、Array.from() 把类数组转成数组

var lis = document.querySelectorAll("li");
let arr = Array.from(lis);

3、fill() 填充数组的方法

参数1:需要填充的数据

参数2:需要从索引几开始填充

参数3:填充到索引几

4、flat(steps)拍平一个多维数组

以前我们需要通过递归+遍历,现在有这个方法就简单多了

参数steps:表示拍平到第几个维度,也可以传入Infinity,不管几维都给拍平

5、find() 在数组中查找一个元素,如果找到返回找到的第一个元素,如果找不到返回undefined

let list = [
    {
        age:18
        userName:"张三",
        
    },
    {
        userName:"李四",
        age:20
    },
    {
        userName:"王五",
        age:21
    }
]
let obj = list.find(function(item,index,_arr){
    //返回后面可以写一个匹配条件
    return item.userName == "张三"
})

6、findIndex() 查找某一个元素的索引,这个方法的用法和上面find一样

for…of遍历

of的遍历与in的遍历是相同的,但是for…in是一个有序遍历(有索引的时候依靠索引遍历),而for…of是一个无序遍历(遍历的时候不需要索引)

for(var i of list){
    console.log(i);
}

注意:

for…of是基于属性值来遍历,所以并不是所有的对象都可以用它来遍历,如果要使用for…of来遍历,则这个对象必须要实现Iterable接口

Set单值集合

Set是ES6中新增的一种数据结构,它是一个单值集合,没有属性名(没有索引,没有key),并且是一个不重合的集合

Set创建
let s1 = new Set();
let s2 = new Set(["a","b","c","d"]);
let s3 = new Set(["a","b","a","c","d"])
Set集合方法

1、add() 给集合添加元素

s3.add("haha").add("hehe")

2、delete() 向Set集合中删除一个元素,返回布尔值

3、has() 判断set集合中有没有这个元素,返回布尔值

4、size属性,表示当前set集合的元素个数

5、clear() 清空set集合

6、values() 它是一个生成器方法,返回当前set集合中的迭代器

7、keys()和values方法一样,但是set没有键,所以返回的依然是values的结果

let arr1 = [1,2,3,4,5,5,6]
let s1 = new Set();
let s2 = new Set(["a","b","c","d"]);
let s3 = new Set(["a","b","a","c","d"])
let s4 = new Set(arr1)  

for(let i of s3){
    console.log(i);
}

//将set集合的数据转换成数组的数据,来进行调用,同时还可以达到去重的操作
let arr = [...s1];

Map键值对集合

map是键值对形式的一个储存,它结合set的特点(储存速度快),也解决了set集合取值慢的特性

map集合当中的键不允许重复的,但是值可以

let m1 = new Map([
    ["a","张三"],
    ["b","李四"],
    ["c","王五"],
    [3,{
        userName:"哈哈"
    }],
    [4,function(){
        console.log("hello")
    }]
])

1、键不能重复,值可以

2、键与值可以是任意数据类型

Map方法

1、size属性

2、set(key,value)向当前map集合新增元素

3、get(key)通过一个键获取map集合当中的一个值

4、delete() 向Map集合中删除一个元素,返回布尔值

5、clear() 清空Map集合

6、values() 它是一个生成器方法,返回当前Map集合中的迭代器

7、keys()生成器方法,返回当前map集合的key-value的集合

Map遍历
for(let i [k,v] of m1){
	console.log(k,v)
}

函数扩展

之前在ES5中我们学习了哪些函数

  • 递归函数
  • 回调函数
  • 闭包函数
  • 匿名函数
  • 构造函数
  • 普通函数
  • 立即执行函数

以上是我们在ES5中接触和学习到函数形式,但是它们本质上来讲没有区别,之所以有那么多种叫法完全出于调用方式的不同

无构造函数的函数

官方叫法成员函数,属性函数

在之前的ES5当中,所有通过function定义的东西都叫函数,而所以通过function声明的函数都具备构造函数的特点,也就是可以被new调用

let obj = {
    name:"haha",
    sayHello:function(){
        console.log("hello")
    }
}

obj.sayHello();       //普通调用
new obj.sayHello();   //构造函数调用

在上面的代码中,我们可以看到sayHello方法本是obj对象的,它就应该通过obj.sayHello() 来调用,而不应该用new来调用

上面这种情况就会产生一些不必要的歧义,ES6就改变了这种函数的定义方式,直接抛弃了关键字function

let obj = {
    name:"haha",
    sayHello(){
        console.log("hello")
    }
}

obj.sayHello();       //普通调用
new obj.sayHello();   //报错

箭头函数

抛弃了function关键字来声明的一个函数声明方式

//无参箭头函数
const sayHello1 = () => {
    console.log("hello");
}

//有一个参数的箭头函数
const sayHello2 = userName => {
    console.log(userName);
}

//有多个参数的箭头函数
const sayHello2 = (userName,age) => {
    console.log(userName,age);
}

//箭头函数的返回值
const a = () => {
    return "hello"
}

//如果只有一句话要执行,可以省略大括号
const sayHello2 = (userName,age) => console.log(userName,age);


//省略return关键字返回数据
const b = (x,y) => x + y

省略return,举例:

let newArr = arr.filter(item => item % 2 == 0)
箭头函数的注意事项:

1、箭头函数不具备构造函数的特点,不能用new调用

2、箭头函数是没有arguments

3、箭头函数内部没有this,如果写了this它绑定的是外边的this

ES6的函数参数

函数参数的默认值

之前我们声明函数可以放置参数

function sayHello(userName,age){
	age = age || 18;
    console.log(age);
}

在上面的代码种,我们可以看到,如果要设置一个参数的默认值,我们需要使用逻辑运算在函数内部进行一个计算才行,但是ES6可以直接设置参数的默认值

function sayHello(userName,age=18){
	console.log(age);
}

多个参数也行

const sayHello = (userName="张三",age=18) => {
	console.log(age);
}

函数的剩余参数

在ES6里面,我们的箭头函数没有arguments,但是我们向实现类似于实参列表的功能怎么办?

const sayHello = (userName,hobby) => {
    console.log(`大家好,我叫${userName}`);
    console.log(`我的爱好是${hobby}`)
}
sayHello("张三","看书","睡觉")

代码分析:

上面的代码我们实现了打印用户爱好的功能,但是如果有多个爱好怎么办?

这里使用剩余参数

const sayHello = (userName,...hobby) => {
    console.log(`大家好,我叫${userName}`);
    console.log(`我的爱好是${hobby.join("-")}`)
}
sayHello("张三","看书","睡觉","打游戏","吃饭")

简单来说,剩余参数就是利用展开运算符在参数中声明了一个没有长度的数组

class关键字

在上面的函数扩展当中,无论是新增的成员函数还是箭头函数,都不能使用构造函数的new去调用了,那么如果我们要在ES6的新语法中定义一个构造函数怎么办?ES6推出了class关键字

class Student{
    //constructor是在new调用的一瞬间,自动执行
    constructor(userName,age,sex){
        this.userName = userName;
        this.age = age;
        this.sex = sex;
    }
    sayHello(){
        console.log(this.userName)
    }
}
let stu1 = new Student("张三",20,"男");
let stu2 = new Student("李四",18,"女")
class里面的访问器属性

在ES6中访问器属性的功能得到的了扩展

class Person {
    constructor(firstName,lastName){
        this.firstName = firstName;
        this.lastName = lastName;
    }
    userName(){
        return this.firstName + this.lastName;
    }
}

let p1 = new Person("张","三");
console.log(p1.userName())

上面的代码,有个问题,我们的userName表现出来的还是一个方法,不是一个属性

class Person {
    constructor(firstName,lastName){
        this.firstName = firstName;
        this.lastName = lastName;
    }
    get userName(){
        return this.firstName + this.lastName;
    }
    set userName(v){
        this.firstName = v[0];
        this.lastName = v.slice(1)
    }
}

let p1 = new Person("张","三");

分析:

get / set 共同修改了访问器属性,get在取值的时候自动调用,set在赋值的时候自动调用,set的参数就赋的值

let obj = {
	firstName:"张",
	lastName:"三",
	get userName(){
		return this.firstName + this.lastName;
	}
    set userName(v){
        this.firstName = v[0];
        this.lastName = v.slice(1)
    }
}
class里面的关键static
class Student{
    //constructor是在new调用的一瞬间,自动执行
    constructor(userName,age,sex){
        this.userName = userName;
        this.age = age;
        this.sex = sex;
    }
    static sayHello(){
        console.log(this.userName)
    }
}

代码分析:

对于class里面的普通方法,我们需要先new一个对象出来,然后通过这个对象来调用里面的方法

对于class里面的静态方法,我们可以直接使用构造函数名来调用

静态的方法不能使用非静态的属性和方法

extends关键字

这个是通过extends关键字来直接实现class的继承

class Person{
    sayHello(){
        console.log("我是一个人")
    }
}
class Student extends Person {
    study(){
        console.log("我在学习")
    }
}
let s1 = new Student();

分析:

上面的代码中,我们让Student继承自Person,同时两个构造函数内部都没有参数,所以可以把构造器省略掉

class Person{
    constructor(userName,age){
        this.userName = userName;
        this.age = age;
    }
    sayHello(){
        console.log("我是一个人")
    }
}
class Student extends Person {
    constructor(userName,age,sex){
        super(userName,age);
        this.sex = sex;
    }
    study(){
        console.log("我在学习")
    }
}
let s1 = new Student("张三",20,"男");

代码分析:

如果要构造函数接收参数,则构造器一定要手写,写的时候注意,super一定要先写出来,this要在super后面

静态继承

之前我们说过,class里面除了普通属性和方法之外,还有static静态属性和方法,静态的是否可以被继承?

class Person{
    constructor(userName,age){
        this.userName = userName;
        this.age = age;
    }
    static hobby = "睡觉";
    static sayHello(){
        console.log("我是一个人")
    }
}
class Student extends Person {
    constructor(userName,age,sex){
        super(userName,age);
        this.sex = sex;
    }
    study(){
        console.log("我在学习")
    }
}
let s1 = new Student("张三",20,"男");

代码分析:

1、静态方法和属性是可以被继承的

2、静态与非静态之间还是隔离的,就算被继承了也是隔离的

3、静态方法可以调用静态属性

方法的重载override
class Person{
    constructor(userName,age){
        this.userName = userName;
        this.age = age;
    }
    static hobby = "睡觉";
    static sayHello(){
        console.log("我是一个人")
    }
    abc(){
        console.log("Person")
    }
}
class Student extends Person {
    constructor(userName,age,sex){
        super(userName,age);
        this.sex = sex;
    }
    study(){
        console.log("我在学习")
    }
    abc(){
        console.log("Student");
    }
    parentAbc(){
        super.abc();
    }
}
let s1 = new Student("张三",20,"男");

代码分析:

1、当父级类的方法满足不了子类的要求的时候,我们可以考虑重写方法

2、重写只需要在子类当中建立一个同名的方法即可

3、在重写子方法的时候,如果要调用父级的同名方法可以使用super指向父级调用

文章来源:https://blog.csdn.net/qq_64558518/article/details/135498350
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。