自学内容网 自学内容网

ES6 -- 2015

学习视频

1. let和const

1.1 let

  1. 变量必须先声明再使用
  2. 同一变量不能重复声明
  3. 变量有块级作用域

1.2 const

  1. 声明常量,常量不能改变
  2. 常量必须有初始值,不能先声明再赋值

2. 解构

1 数组解构

保持左右的结构一样,安装顺序一一对应

  • 完全解构
let [a,b] = [1,2]
console.log(a,b) // 1 2
  • 部分解构
let [a] = [1,2]
console.log(a) // 1 
  • 忽略方式解构
let [ ,b] = [1,2]
console.log(b) //  2
  • 嵌套结构
let [,,[a]] = [1,2,[3]]
console.log(a) // 3
  • 剩余运算符解构
let [a,...b] = [1,2,3]
console.log(a,b) // 1 [2,3]
  • 解构默认值
let [a,b=2] = [1]
console.log(a,b) // 1 2
  • 应用:两数交换
let a = 10
let b = 20
[b, a] = [a, b]
console.log(a, b) // 20, 10

2. 对象解构

按属性名对应

  1. 完全解构
let user = {name:"zhangsan",age:18}
let {age, name} = user
console.log(name, age) // zhangsan 18
  1. 部分解构
let user = {name:"zhangsan",age:18}
let {name} = user
console.log(name) // zhangsan
  1. 解构后重命名
let user = {name:"zhangsan",age:18}
let {name:userName,age:userAge} = user
console.log(userName, userAge)// zhangsan 18
  1. 剩余运算符解构
let user = {name:"zhangsan",age:18,address:'银河系'}
let {name,...rest} = user
console.log(name, rest)// zhangsan {age: 18, address: '银河系'}
  1. 默认值
let user = {name:"zhangsan",age:18}
let {name, age, address='银河系'} = user
console.log(name, age, address)// zhangsan 18 银河系 

3. 字符串解构

  • 数组解构字符串
let str = 'qwert'
let [a,b,c,d] = str
console.log(a,b,c,d) // q w e r
  • 字符串属性解构
let str = 'qwert'
let {length} = str
console.log(length)// 5

3. 对字符串扩展

1. 模板字符串

  1. 解决了字符串换行
console.log("字符串
随意换行") // 报错

console.log(`字符串\
可以\
随意换行`)//字符串可以随意换行
  1. 更容易的插入变量
let user = {name:"zhangsan",age:18}
console.log(`我叫${user.name},今年${user.age}`)//我叫zhangsan,今年18岁

2. 字符串新增的方法

  1. 是否包含字符串,includes() 返回值:boolean
// 是否包含字符串 wo
console.log(str.includes('wo')) // true
console.log(str.includes(`oo`)) // false
// 索引0位置开始是否有hel字符串
console.log(str.includes('hel', 0)) // true
  1. 是否从某个字符串开始, startsWith(),返回值:boolean
// he 是否在字符串的开头,也就是索引0开始
console.log(str.startsWith(`he`));// true
// el 是否从索引1开始
console.log(str.startsWith(`el`,1))// true
  1. 是否以某个字符串结尾 endsWith(),返回值:boolean
// 字符串是否以 ld 结尾
console.log(str.endsWith(`ld`));// true
// 截取索引从0到4的字符串,是否以 o 结尾
console.log(str.endsWith(`o`,5));// true
  1. 返回重复几次字符串,repeat(),返回值:string
console.log(str.repeat(3));// hello worldhello worldhello world

4. 对象的扩展

1. 简化对象中属性名和方法名的写法

// 简化对象中属性名和函数名的写法
let str = `一个字符串`

// 未简写的形式
let obj = {
    str: str,
    fun: function(){
        return '一个函数'
    }
}
console.log(obj.str,obj.fun())//一个字符串 一个函数

// 简写的形式
let obj2 = {
    str,// 属性名与引入的对象的名字相同,可以使用简写形式
    fun(){
        return '又一个函数'
    }
}
console.log(obj2.str,obj2.fun()) // 一个字符串 又一个函数
  1. 对象中的属性名使用[]表达式
  • 属性名使用[]拼接字符串
let lastName = 'last';
let firstName = 'first';
let obj = {
    ['my' + 'name']:'zhangsan',
    [lastName + 'name']:'lisi',
    [`${firstName}name`]:'wangwu'
}
console.log(obj);//{myname: 'zhangsan', lastname: 'lisi', firstname: 'wangwu'}
  • 对象中的属性名可以是动态的
let arrName = ['zhangsan','lisi','wangwu']
let obj = {}

for(let item of arrName){
    obj[item+"Name"]=item
}
console.log(obj)    //{zhangsanName: 'zhangsan', lisiName: 'lisi', wangwuName: 'wangwu'}

2. 对象扩展的方法

  1. Object.assign对象合并

Object.assign(target: {}, source: any)返回值是合并后的对象

  • 第一种情形
let user = {}
let user1 = {name:'zhangsan'}
let user2 = {age:10}

console.log(user === user1) // false
user = Object.assign(user1,user2)   
console.log(user1 === user) // true
console.log(user);  // {name: 'zhangsan', age: 10}
console.log(user1)  // {name: 'zhangsan', age: 10}
console.log(user2)  // {age: 10}
  • 第二种情形
let user = {}
let user1 = {name:'zhangsan'}
let user2 = {age:10}

Object.assign(user,user1,user2) 
console.log(user)   // {name: 'zhangsan', age: 10}
console.log(user1)  // {name: 'zhangsan'}
console.log(user2)  // {age: 10}
  • 第三种情形:如有相同的属性,则覆盖属性值
let user1 = {name:'zhangsan',age:33}
let user2 = {age:10}

Object.assign(user1,user2) 
console.log(user1)  // {name: 'zhangsan', age: 10}
console.log(user2)  // {age: 10}
  • 应用:初始化对象
let user1 = {name:'',age:0}
let user2 = {...user1}

user1.name = 'zhangsan'
user1.age = 10
console.log(user1)  // {name: 'zhangsan', age: 10}
Object.assign(user1,user2) 
console.log(user1)  // {name: '', age: 0}
  1. 对象是否相等Object.is

=== 作用相同
相比较的是两个对象,地址相同返回true,不同返回false

let user1 = {name:'',age:0}
let user2 = {name:'',age:0}


console.log(Object.is(user1,user2)); // false
user1 = user2;
console.log(Object.is(user1,user2)); // true

5. 数组的扩展

1. 扩展运算符...

let arr1 = ['a','b','c']
let arr2 = ['1','2','3']

let arr = ['A',...arr1,...arr2]
console.log(arr)// ['A', 'a', 'b', 'c', '1', '2', '3']

2. 查找 find()

let arr = ['A', 'a', 'b', 'c', '1', '2', '3']
// 返回符合条件的第一个元素
let findResult = arr.find(function(item){
    return item > 'b'
})
console.log(findResult);    // c

3. 查找 findIndex()

let arr = ['A', 'a', 'b', 'c', '1', '2', '3']
// 返回符合条件的第一个元素下标
let findResult = arr.findIndex(function(item){
    return item > 'b'
})
console.log(findResult);    // 3

4. 将一组值转换为数组

let arr = Array.of(1,2,3)
console.log(arr);   // [1,2,3]

5. 将对象内的值转化为数组

通常使用的对象是无法通过Array.form转换为数组的

  • 应用场景:查找class下的所有dom对象,将其转化为数组
var parent = document.querySelectorAll('#myClass');
let children = parent[0].children;
console.log(children);
// 将children转换为真正的数组
let arr = Array.from(children)
console.log(arr);

在这里插入图片描述

6. include 方法

ES7 – 2016 新增

  • 数组中是否包含某个元素,返回值为boolean
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
arr.includes(5); // true

6. 函数的扩展

1. 不同函数中的this

{
    // 普通函数
    function fn() {
        console.log(this)   // windows
    }
    fn()
}
{
    // 构造函数
    function Person(name) {
        this.name = name;
        console.log(this)   // Person {name: 'zs'}
    }
    let zs = new Person('zs')
    console.log(zs) // Person {name: 'zs'}
}

{
    // 函数作为属性值
    let obj = {
        name: 'obj',
        fn() {
            console.log(this)
        }
    }
    obj.fn()    // {name: 'obj', fn: ƒ}
}

2. 更改this的指向

  1. call(this: Function, thisArg: any, ...argArray: any[])

只在执行call方法这一句代码时改变this的指向(并不是彻底改变函数中this的指向)

// 函数作为属性值
  let obj = {
      name: 'obj',
      fn() {
          console.log(`name:${this.name},age:${this.age}`) 
      }
  }
  // fn函数的this指向obj,所以没有age的属性值
  obj.fn()    // name:obj,age:undefined
  
  let obj1 = { age: 18 }
  // 将fn函数的this指向obj1,所以没有name的属性值
  obj.fn.call(obj1) //name:undefined,age:18
  • call携带参数
// 函数作为属性值
let obj = {
    name: 'obj',
    fn(param) {
        console.log(param)
    }
}
// fn函数的this指向obj
obj.fn("this指向obj")    // this指向obj

let obj1 = { age: 18 }
// 将fn函数的this指向obj1
obj.fn.call(obj1,'this指向obj1') // this指向obj1
  1. apply(this: Function, thisArg: any, argArray?: any)
  • 与call方法唯一不同的是传递参数时使用 []
// 构造函数
function Person() {
    console.log(this) 
}

let obj =  { name: 'obj'}
Person.apply(obj)   // {name: 'obj'}
let zs1 = new Person() // Person {}
// 构造函数
function Person(...argArray) {
    this.age= 10
    console.log(`参数:`,argArray) 
    console.log('this指向====>',this)
}

let obj =  { name: 'obj' }
Person.apply(obj,['arg1','arg2'])   
/*
结果:
    参数:['arg1', 'arg2']
    this指向====> {name: 'obj', age: 10}
*/
let zs1 = new Person('XXX') // Person {}
/*
结果:
    参数: ['XXX']
    this指向====> Person {age: 10}
*/
  1. bind
  • bind方法返回fn函数更换this后的fn函数
  • 对上面这句话的解释:fn函数还是fn函数,只不过fn函数中的this指向了obj对象
// 普通函数
function fn() {
    console.log(this)   // {name: 'obj'}
}

let obj = { name: 'obj' }
let newFn = fn.bind(obj)
newFn()

3. 函数中参数设置默认值

  • 如果有多个参数,使用默认值的参数要放在参数的最后面
// 默认值参数
function fun (a, b=1000) {
    console.log(a, b)
}

7. 箭头函数

1. 箭头函数中的this,指向window

let fun = ()=>{
    console.log(this)   // window
}
fun()

2. 返回值是对象,使用({......})

let fun = ()=>({name:'zhangsan'})
console.log(fun()) 

8. 二进制和八进制

let num1 = 0b101;   // 0b或者0B 二进制
let num2 = 0o123;   // 0o或者0O 八进制
let num3 = 0xff;    // 0x或者0X 十六进制
console.log(num1, num2, num3);// 5 83 255

9. symbol

10. Class

1. 基本结构

class Person {
    // 构造器(类似构造函数)
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    // 方法
    sayHi(){
        console.log(`Hi, I am ${this.name}`);
    }
}

2. 继承

// 继承
class Student extends Person {
    constructor(name, age, grade) {
        // 调用父类的构造函数
        super(name, age);   // 调用父类的构造函数,不许写在第一行
        this.grade = grade;
    }
    study() {
        console.log(`${this.name} is studying`);
    }
}

3. get和set属性

class Person {
    pname// 定义属性,可以不通过构造器定义属性

    // 对外使用 name 属性,类中的属性实际是pname
    set name(value) {
        console.log('set name 对name的属性值进行校验等操作')
        this.pname = value;
    }
    get name() {
        console.log('get name 对name的属性值进行操作')
        return this.pname;
    }
}

const person = new Person();
person.name = 'John'// set name 对name的属性值进行校验等操作
console.log(person.name);// get name 对name的属性值进行操作  John

4. 类的静态属性和方法

class Person {
    // static: 定义静态属性和方法的关键字
    static type 
    static sayHello() {
        console.log(this);// 指向Person类
    }
    walk() {
        console.log(this);// 指向实例化对象
    }
}
// 静态属性和方法的使用
Person.type = 'person';
console.log(Person.type);   // person
Person.sayHello();  // 静态方法中的 this 指向类本身
/*结果:
class Person {
    static type 
    static sayHello() {
        console.log(this);
    }
    walk() {
        console.log(this);
    }
}
*/
const person = new Person()
person.walk();  // walk中的this指向的是person实例

11. Set

1. Set集合的增删改查

  • Set,是一种数据结构,和数组类似,但是Set集合里面的元素不能重复
  1. 创建Set集合
// new关键字,创建Set集合
const set1 = new Set()
// 把数组转化为Set
const set2 = new Set([1, 2, 3])
  1. 将数组转换为Set集合
// 把数组转化为Set
const set2 = new Set([1, 2, 3])
const arr = [1, 2, 3, 4, 5, 1, 2]
const set3 = new Set(arr)// {1, 2, 3, 4, 5}
  1. 获取set集合中元素的数量
// 获取Set集合的长度 .size
let size = set3.size // size是Set集合的属性,不是方法
  1. 添加元素
// 添加元素 add()
const set4 = set3.add(4)
  1. 删除元素
// 删除元素 delete()
const set5 = set3.delete(2)
  1. 判断集合中是否有某个元素
// 判断元素是否存在 has()
const isExist = set3.has(2) // 返回值是布尔值
  1. 清空集合
// 清空集合 clear()
set3.clear()

2. 遍历Set

set的数据结构和数组相似,但是它没有index
set是以键值对的形式储存的,但是set的键和值是相等的

  • 通过遍历数组和Set集合比较两个的数据结构

遍历数组

// 数组的遍历
const arr = ['a', 'b', 'c', 'd']
arr.forEach((value,index,array) => {
  console.log(`value===>${value},index===>${index},array===>`,array);
})`在这里插入代码片`
// 结果
value===>a,index===>0,array===> (4) ['a', 'b', 'c', 'd']
value===>b,index===>1,array===> (4) ['a', 'b', 'c', 'd']
value===>c,index===>2,array===> (4) ['a', 'b', 'c', 'd']
value===>d,index===>3,array===> (4) ['a', 'b', 'c', 'd']

遍历Set集合

const list = new Set(['a', 'b', 'c', 'd'])
list.forEach((value,index,set) => {
    console.log(`value===>${value},index===>${index},set===>`,set);
})
// 结果
value===>a,index===>a,set===> Set(4) {'a', 'b', 'c', 'd'}
value===>b,index===>b,set===> Set(4) {'a', 'b', 'c', 'd'}
value===>c,index===>c,set===> Set(4) {'a', 'b', 'c', 'd'}
value===>d,index===>d,set===> Set(4) {'a', 'b', 'c', 'd'}

结论: set集合中没有索引,是以键值对的结构存储元素,并且键与值是一样的

  1. for... of
const list = new Set(['a', 'b', 'c', 'd'])
for (let item of list) {
    console.log(item);// abcd
}
  1. 将set结构转换为数组结构后遍历
  • Array.from
const list = new Set(['a', 'b', 'c', 'd'])
Array.from(list).forEach((value,index,set) => {
    console.log(`value===>${value},index===>${index},set===>`,set);
})
// 结果
value===>a,index===>0,array===> (4) ['a', 'b', 'c', 'd']
value===>b,index===>1,array===> (4) ['a', 'b', 'c', 'd']
value===>c,index===>2,array===> (4) ['a', 'b', 'c', 'd']
value===>d,index===>3,array===> (4) ['a', 'b', 'c', 'd']
  • […set]
let list = new Set(['a', 'b', 'c', 'd']);

[...list].forEach((value,index,array) => {
    console.log(`value===>${value},index===>${index},array===>`,array);
})

12 Map

  • Map:数据结构,以键值对的形式储存,但是键在集合中唯一

1. Map集合的增删改查

  1. 创建Map
const map = new Map();
map.set("IN", "India");
map.set("FR", "France");
console.log(map)
// {'IN' => 'India', 'FR' => 'France'}


const map1 = new Map([["IN","India"],["FR","France"]])
  1. 获取集合中某个键的值
let v = map.get("IN")
console.log(v)// India
  1. 删除元素
map.delete("IN")// 删除键值为'IN'的元素
console.log(map)
  1. 清空集合
map.clear()
console.log(map) //Map(0) {size: 0}
  1. 集合中元素的数量
let size = map.size
  1. 修改键的值
const map = new Map();
map.set("IN", "India");
map.set("IN","XXXXX")
console.log(map)// {'IN' => 'XXXXX'}

2. 遍历Map

  • 获取Map所有的键
const map = new Map();
map.set("IN", "India");
map.set("FR", "France");
map.set("USA", "United States")

const keys = map.keys();
console.log(keys);// {'IN', 'FR', 'USA'}
  • 获取Map所有的值
const values = map.values();
console.log(values); // {'India', 'France', 'United States'}
  • for...of
for(let [key,value] of map){
    console.log("key==> ",key,"value===> ",value);
}
/*
key==>  IN value===>  India
key==>  FR value===>  France
key==>  USA value===>  United States
*/
for(let item of map){
    console.log(item);
}
/*
['IN', 'India']
['FR', 'France']
['USA', 'United States']
*/
  • forEach
map.forEach((value,key)=>{
    console.log("key==>",key,"value===>",value);
})
/*
key==>  IN value===>  India
key==>  FR value===>  France
key==>  USA value===>  United States
*/

13. Iterator 迭代器

迭代器:

  1. 实现Symbol.iterator方法的数据解构都可以通过for…of遍历数据
  2. 迭代器必须实现next(),它返回一个对象,该对象有两个属性:value 和 done。value 是当前元素的值,done 是布尔值,表示是否已经到达了序列的末尾。
  • 原生实现迭代器的数据类型

    1. Array(数组)
    2. String(字符串)
    3. Map(映射表)
    4. Set(集合)
    5. NodeList 和 HTMLCollection: 查询器返回的结果
    6. Generator 函数: Generator 函数本身就是一个迭代器工厂
    7. TypedArray(类型化数组)
    8. Arguments 对象(传递给函数的参数)
  • 在对象中使用迭代器

let obj = {
    arr:["苹果","香蕉","梨"],
    // 创建迭代器
    [Symbol.iterator](){
        let index = 0;
        return {
            next(){
            if(index < obj.arr.length){
                return {value:obj.arr[index++],done:false}
            }else{
                return {value:undefined,done:true}
            }
        }
        }
    },
}
// 对象中实现迭代器后可以使用for..of
for(let item of obj){
    console.log(item)
}
// 迭代器中的next()方法
let it = obj[Symbol.iterator]();
console.log(it.next()); // {value: '苹果', done: false}
console.log(it.next()); // {value: '香蕉', done: false}
console.log(it.next()); // {value: '梨', done: false}
console.log(it.next()); // {value: undefined, done: true}

14. Promise

1. 基本使用

let promise = new Promise((resolve, reject) => {
    // resolve("成功")
    reject("失败")
});
promise.then(value => {
    console.log(value)  // 成功 
},err => {
    console.log(err)    // 失败 
});
  • new Promise中参数是一个函数,函数中有两个参数,第一个函数执行,对应then方法的第一个函数执行;第二个函数执行,则then方法的第二个函数执行
  • then里面有两个参数,参数数据类型是函数
function resolve(value){}
function reject(reason){}

function executor(resolve, reject){
    // 在resolve和reject执行前,promise为待定状态(pending): 初始状态,既没有被兑现,也没有被拒绝。
    resolve("成功")     // 如果执行resolve函数,promise为成功状态(fulfilled): 意味着操作成功完成。
    // reject("失败")   // 如果执行reject函数,promise为失败状态(rejected): 意味着操作失败
}
let promise = new Promise(executor)

function thenResolve(value){    // promise状态变为成功状态执行该函数
    // value 是 resolve函数的参数
    console.log(value)
}
function thenReject(reason){    // promise状态变为失败状态执行该函数
    // reason 是 reject函数的参数
    console.log(reason)
}
promise.then(thenResolve, thenResolve)

2. Promise的链式调用

//  链式调用
let ajax = (url) => {
    return new Promise((resolve) => {
        console.log('请求地址:' + url)
        setTimeout(resolve,2000)
    })
}

ajax('A_url').then(res=>{
    console.log('A请求返回的结果')
    return ajax("B_url")
}).then(res=>{
    console.log('B请求返回的结果')
    return ajax('C_url')
}).then(res=>{
    console.log('C地址返回的结果')
})
/*
    请求地址:A_url
    A请求返回的结果
    请求地址:B_url
    B请求返回的结果
    请求地址:C_url
    C地址返回的结果
*/

3. Promise.all

  • Promise.all( [ promise1, promise2, promise3 ] )
  • 将多个promise绑定到一起,直到所有的promise都变为fulfilled(成功)状态时,一起返回结果
let p1 = new Promise((resolve,  reject)=>{
    resolve("P1")
})
let p2 = new Promise((resolve,  reject)=>{
    resolve("P2")
    // reject("P2")
})
let p3 = new Promise((resolve,  reject)=>{
    resolve("P3")
    // reject("P3")
})
Promise.all([p1,p2,p3]).then(res=>{
    console.log(res)// ['P1', 'P2', 'P3']
},value=>{
    console.log(value)
})
  • 如果有多个promise返回失败状态,则Promise.all中只会接收最先变为失败状态的promise

4. Promise.race

  • 数组中promise,谁的状态(成功或失败)先改变,返回谁的结果
let p1 = new Promise((resolve,  reject)=>{
    setTimeout(()=>{
        reject("P1")
    },2000)
})
let p2 = new Promise((resolve,  reject)=>{
    setTimeout(()=>{
        resolve("P2")
    // reject("P2")
    },1000)

})
let p3 = new Promise((resolve,  reject)=>{
    setTimeout(resolve,3000,"P3")
})
Promise.race([p1,p2,p3]).then(res=>{
    console.log(res)// P2
},value=>{
    console.log(value)
})

5. catch

作用:当promise状态变为fulfilledthen执行成功的回调,但是在回调中产生的异常没有处理方式

  • catch可以捕获所有的异常,无论是promise还是then里面产生的
  • 如果没有错误的回调,catch也能接收rejected状态
let promise = new Promise((resolve, reject) => {
    // throw new Error("错误")
    // resolve("成功")
    reject("失败")
});
promise.then(value => {
    throw new Error("错误")
    console.log(value)  // 成功 
})
.catch(err => {
    console.log("catch:",err)    // 失败
})

6. finally

15. Module 模块的导出和导入

1. 基本使用

  1. 导入和导出
    • 导出的变量不能重新赋值
export let var1 = 1;
export let obj1 = {
  a: 1,
}
export let fun1 = ()=>{};

import {var1, obj1, fun1} from './module.js'
let var2 = 2;
let obj2 = {
  b: 2,
}
let fun2 = ()=>{};
export {var2, obj2, fun2};

import {var2, obj2, fun2} from './module.js';
  1. 使用别名 as
  • 在export中使用别名
let var2 = 2;
let obj2 = {
  b: 2,
}
let fun2 = ()=>{};

export {
    var2 as var3,
    obj2 as obj3,
    fun2 as fun3,
}

import {var3, obj3, fun3} from './module.js'
  • 在import中使用别名
let var2 = 2;
let obj2 = {
  b: 2,
};
let fun2 = () => {};
export { var2, obj2, fun2 };

import {var2 as var3, obj2 as obj3, fun2 as fun3} from './module.js'

2. 整体导入 * as

export let var1 = 1;
export let obj1 = {
  a: 1,
};
export let fun1 = () => {};

import * as myName from './module.js'
// 使用
console.log(myName.var1)    // 1
console.log(myName.obj1)    // {a: 1}
console.log(myName.fun1)    // () => {}

3. 默认导出 default,匿名导入

  • 在一个文件中只能有一个default,但是不影响普通的导出方式
  • 导入时名字不需要加 {}
let var2 = 2;
let obj2 = {
  b: 2,
};
let fun2 = () => {};

export default {
  var2,
  obj2,
  fun2,
};

import myName from './module.js'
console.log(myName.var2)    // 2
console.log(myName.obj2)    // {a: 2}
console.log(myName.fun2)    // () => {}
  • 默认导出匿名函数
export default function () {
  console.log("hello world");
};

import myName from './module.js'
myName()    // hello world
  • 默认导出匿名对象
export default {
  name: "zhangsan",
  age: 18,
  talk(){
    console.log("hello world");
  }
};

import myName from './module.js'
myName.age = 20
myName.talk()   // hello world
console.log(myName) // {name: 'zhangsan', age: 20, talk: ƒ}

4. 在html中使用Module

  • type="module"
    <script type="module">
        import {var1} from './module.js'
        console.log(var1)
    </script>

原文地址:https://blog.csdn.net/YUELEI118/article/details/142317200

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!