ES6笔记(kerwin老师)

01【let和const】

1.let

1.1块级作用域

什么是块级作用域?

  • 声明的变量只在代码块{}中有效

  • 外部无法访问到这个代码块中的变量。

与var区别:

  • var是全局作用域

经典案例:点击每一个li输出索引值

<body>
<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>
<script>
    let lis = document.querySelectorAll("ul li");
    for(var i=0;i<lis.length;i++){
        lis[i].addEventListener('click',function (){
            console.log(i);  //都是3
        })
    }
</script>
</body>

此代码的执行思路是,先执行for循环,执行完后i为3,因为点击事件属于异步操作,所以for循环执行后再执行点击事件,又由于var声明的变量是全局变量,相当于整个执行过程中的i只有一个,此时点击任何一个li输出的i都为3。

此时改用let声明:

let lis = document.querySelectorAll("ul li");
for(let i=0;i<lis.length;i++){
    lis[i].addEventListener('click',function (){
        console.log(i);
    })
}

由于let声明的变量只在代码块中执行,所以以上代码中,每执行一次for循环,就会产生一个新的变量i,而每一个变量i都没有被释放掉,相当于此时会产生3个i,并且for循环相当于一个父作用域,点击事件相当于一个子作用域,每点击一次,程序就会去寻找父级中的i并输出来。

因此用let解决此类问题就更加方便。

1.2不允许重复声明

  • var 可以重复声明,后声明的会覆盖先声明的。

var a =1;
var a =2;
console.log(a);  //2
  • let不允许重复声明

let a =1;
let a =2;
console.log(a);  //报错Uncaught SyntaxError: Identifier 'a' has already been declared

1.3没有变量提升

  • var有变量提升

console.log(num); //undefined
var num =10;
  • let 没有变量提升

let a =10;
function test(){
    console.log(a); //报错Uncaught ReferenceError: Cannot access 'a' before initialization
    let a =10;
}
test();

1.4不与顶层对象挂钩

let a = 10;
console.log(window.a); //undefined
  • 而var定义的变量是全局变量,可通过window来查找

var a = 10;
console.log(window.a); //10

2.const(与let相似)

1.1常量必须初始化,并且不允许更改

1.2不能重复定义

1.3块级作用域

1.4没有变量提升

1.5不与顶层对象挂钩

1.6用const修饰的变量一定不能被改吗?

const obj = {
    name:"张三",
    age:"18"
}
obj = "fgjs" //这样会报错,因为常量不允许被修改

obj.name = "李四";
console.log(obj); //此时obj中的name被修改了,因为对象是复杂数据类型,存的是地址

//如果想让对象不被更改,可使用Object.freeze()方法
const obj = Object.freeze({
    name:"张三",
    age:"18"
})
obj.name = "王五";
console.log(obj); //不报错,但此时obj里的name还是‘张三’

02【解构赋值】

从数组和对象中提取值,对变量进行赋值,这被称为解构。

let [a, b, c] = [1, 2, 3];

上面代码表示,可以从数组中提取值,按照对应位置,对变量赋值。本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。

如果解构不成功,变量的值就等于undefined。

let [x, y] = ['a'];
x // "a"
y // undefined
  • 案例1:快速交换两个变量的值,不借助第三个变量

let x = 1;
let y = 2;
[y,x]=[x,y];
console.log(x,y);

1.默认值的基本用法**

const [a, b] = [];
console.log(a, b);    // undefined undefined

// ---------------------------------------
const [a = 1, b = 2] = [];
console.log(a, b);    // 1 2

当后端返回的值是空的,又不希望这个变量是undefined,就可以给它一个默认值来解决这个问题

let [x=1]=[];
console.log(x); //1

2.数组解构赋值

3.对象解构赋值

对象的解构和数组基本类似,对象解构的变量是在 {}中定义的。

var obj = { 
    name: 'imooc', age: 7 
};
var { name, age } = obj;  // name: imooc, age: 7
3.1重命名属性

在对象解构出来的变量不是我们想要的变量命名,这时我们需要对它进行重命名。

var {a:x = 8, b:y = 3} = {a: 2};
console.log(x); // 2
console.log(y); // 3
3.2获取对象中嵌套的值
 let code = "AAAA"
        let res = {
            code:200,
            data:{
                list:["aaa","bbb","ccc"]
            }
        }

        let {data:{list:[x,y,z]},code:co,err="没有错误"} = res;

        console.log(x,co,err)

4.函数传参时解构赋值

function getData(){
    let res = {
        code:100,
        data:{
            list:["a","b"]
        }
    }
    test(res)
}
function test({code,data:{list:[x]}}){
    console.log(code,x); //100 'a'
}
getData();

5.对于字符串的解构赋值

let a = "sdaffa";
let [x,y] = a;
console.log(x,y);

let {length} = a;
console.log(length);
//如果名字冲突,也可给他重命名
// let {length:len} = a;
// console.log(length);

03【模板字符串】

解决代码换行的美观性和方便性

let name ="xiaomei";
// let oli = "<li>\
//    <b>"+name+"</b>\
//    </li>"

let oli =`<li>
   <b>${name}</b>
   </li>`
console.log(oli);

案例:

let arr =["tiechui", "kerwin" ,"gangdan"];
let newlist = arr.map(function(item,index){
    return `<li>
        <b>${item}</b>
    </li>`

    // //${} 括号里不仅可放变量,还可放表达式,函数等
    // return `<li class="${index===0?'active':' '}">
    //     <b>${item}</b>
    // </li>`
})
console. log(newlist);
let oul = document . querySelector("ul" );
oul.innerHTML = newlist. join("");

04【字符串扩展】

1.include函数

判断字符串中是否存在指定字符

let a = "dsgh";
console.log(a.includes("g")) //true
console.log(a.startsWith("d")) //判断以字母开头 true
console.log(a.endsWith("gh"))  //判断以字母结尾 true

console.log(a.includes("g",1)) //判断索引从1后开始查找 false
console.log(a.startsWith("s",1)) //true
console.log(a.endsWith("g",3))  //true

2.repeat函数

repeat()方法返回一个新字符串,表示将原字符串重复n次。

let myname = "kerwin"

console.log(myname.repeat(3)) //kerwinkerwinkerwin

console.log(myname.repeat(0)) //"" 
console.log(myname.repeat(3.5)) //kerwinkerwinkerwin

console.log(myname.repeat("3"))//kerwinkerwinkerwin

05【数值扩展】

1.二进制和八进制表示法

let count1 = 100
let count2 = 0x100
let count3 = 0o100
let count4 = 0b100

2.isFinite与isNaN方法

减少全局性方法,使得语言逐步模块化

let num1 = Number.isFinite(100) //true
let num2 = Number.isFinite(100/0) //false
let num3 = Number.isFinite(Infinity) // false
let num4 = Number.isFinite("100") //false
let num1 = Number.isNaN(100) // false
let num2 = Number.isNaN(NaN) //true
let num3 = Number.isNaN("kerwin") //false
let num4 = Number.isNaN("100") // false

它们与传统的全局方法isFinite()和isNaN()的区别在于,传统方法先调用Number()将非数值的值转为数值,再进行判断,而这两个新方法只对数值有效,Number.isFinite()对于非数值一律返回false, Number.isNaN()只有对于NaN才返回true,非NaN一律返回false。

3.isInteger方法

用来判断一个数值是否为整数。

let num1 = Number.isInteger(100) // true
let num2 = Number.isInteger(100.0) //true
let num3 = Number.isInteger("kerwin") //false
let num4 = Number.isInteger("100") // false

4.极小常量Number.EPSILON

它表示1与大于1的最小浮点数之间的差。2.220446049250313e-16

function isEqual(a,b){
        return Math.abs(a-b)<Number.EPSILON
}

console.log(isEqual(0.1+0.2,0.3)) //true
console.log(0.1+0.2===0.3) //false

5.Math.trunc

将小数部分抹掉,返回一个整数

console.log(Math.trunc(1.2)) //1
console.log(Math.trunc(1.8))// 1
console.log(Math.trunc(-1.8)) //-1
console.log(Math.trunc(-1.2))//-1

6.Math.sign

Math. sign方法用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值。

Math.sign(-100) // -1
Math.sign(100) // +1
Math.sign(0) // +0
Math.sign(-0) // -0
Math.sign("kerwin") // NaN

06【数组扩展】

1.扩展运算符

(1)浅复制

let arr = [1,2,3];
let arr2 = [...arr];
arr2.pop(); //删除数组最后一个,不会影响arr
console.log(arr,arr2);

(2)快速合并

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

(3)和解构赋值一起使用

let arr = [1,2,3];
let arr2 = [1,2,3,4,5,6];
let [a,b,...c] = arr2;
console.log(a,b,c);

2.Array.from

将类数组对象转换为真正数组

function test(){
        console.log(Array.from(arguments))
}

test(1,2,3)

let oli = document.querySelectorAll("li")
console.log(Array.from(oli))

3.Array.of

将一组值转化为数组,即新建数组

let arr =Array(1,2); //此时输出数组[1,2]
let arr1 = Array(2); //只传一个时会认为是数组的长度,输出的是empty
let arr2 = Array.of(2); //将值转换为数组
console.log(arr,arr1,arr2);

4.find方法

1)该方法主要应用于查找第一个符合条件的数组元素

2)它的参数是一个回调函数。在回调函数中可以写你要查找元素的条件,当条件成立为true时,返回该元素,如果没有符合条件的元素,返回值为undefined

// find findIndex()
let arr = [11,12,13,14,15]

let res = arr.find(function(item){
  return item>13
})  //14
let res = arr.findIndex(function(item){
  return item>13
})  //3

//findLast findLastIndex() ES2022
let res = arr.findLast(function(item){
  return item>13
}) //15
let res = arr.findLastIndex(function(item){
  return item>13
})  //4

5.fill方法

使用自己想要的参数替换原数组内容,但是会改变原来的数组

let arr1 = new Array(3).fill("kerwin")
let arr2 = ['a', 'b', 'c'].fill("kerwin", 1, 2)
console.log(arr1)//['kerwin', 'kerwin', 'kerwin']
console.log(arr2)// ['a', 'kerwin', 'c']

6.flat与flatMap方法

按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回

简而言之就是扁平化数组,将二维变为一维

	let arr = [1,2,3,[4,5,6]]
    let arr1 = arr.flat();
    console.log(arr,arr1);

    let obj = [{
        name: "A",
        list: ["鞍山", "安庆", "安阳"]
    },
        {
            name: "B",
            list: ["北京", "保定", "包头"]
        }
    ]
    let res = obj.flatMap(function (item){
        return item.list;
    })
    console.log(res);

07【对象扩展】

1.对象简写

let name ="moduleA"
let obj = {
    name, //name:name
    test1(){ }, //test1:function(){}
    test2(){ }, //test2:function(){}
}

2.对象属性表达式

l0et name ="moduleA"
let obj = {
    [name]:a, //moduleA:a
    [name+"test1"](){ }, //moduleAtest1(){}
    [name+"test2"](){ }  //moduleAtest2(){}
}

3.扩展运算符... es2018

let obj1 ={
    name:"a",
    age:"18"
}
let obj2 = {
    sex:"女",
    age:"20" 
}
console.log({...obj1,...obj2}); //obj2的age会覆盖obj1的age {name:"a",sex:"女",age:"20"}

4.Object.assign

Object.assign(target, object1,object2)的第一个参数是目标对象,后面可以跟一个或多个源对象作为参数。

target:参数合并后存放的对象

object1:参数1

object2:参数2

let obj1 = {
    name: "kerwin"
};

let obj2 = {
    name:"tiechui"
};
let obj3 = {
    age:100
};

console.log(Object.assign(obj1, obj2, obj3)); // {name: 'tiechui', age: 100}
console.log(obj1); //{name: 'tiechui', age: 100}
console.log(obj2); //{name: 'tiechui'}
console.log(obj3); //{age: 100}
//最终结果会影响obj1,若不想影响obj1,定义一个空的obj
//obj = {}
//console.log(Object.assign(obj,obj1, obj2, obj3));

5.Object.is

判断两个值是否是相同的值

主要是解决NaN===NaN为false的判断

console.log(NaN==NaN) //false
console.log(NaN===NaN) //false
console.log(+0===-0) //true

console.log(Object.is(NaN,NaN)) //true
console.log(Object.is(+0,-0)) //false

08【函数的扩展】

1.参数默认值

function ajax(url,method="get",async=true){
    console.log(url,method,async);
}
ajax("/aaa"); ///aaa get true
ajax("/aaa","post"); ///aaa post true

2.rest剩余参数

function test(...data){
        console.log(data); // [1, 2, 3, 4, 5, 6]
    }
    test(1,2,3,4,5,6);

    //name属性
    console.log(test.name); //test

3.箭头函数

  • 箭头函数是 ES6 里面一个简写函数的语法方式

  • 重点: 箭头函数只能简写函数表达式,不能简写声明式函数

function fn() {} // 不能简写
const fun = function () {} // 可以简写
const obj = {
  fn: function () {} // 可以简写
}
  • 语法: (函数的行参) => { 函数体内要执行的代码 }

let fn = function () { }
// 可以使用箭头函数写成
let fn = () => { } 

return语句的箭头函数(return前没有其他逻辑代码):

let fn = function () {
	return "11"
}
// 可以使用箭头函数写成
let fn = () => "11"

如果return的是一个对象,需要注意:

let test2 = function(){
    return {
        name:"a",
        age:18
    }
}
//箭头函数写成
let test2 = ()=>({
     name:"a",
     age:18
}) //括号必须加

如果只有一个参数,可以省略掉参数括号,两个参数则必须要括号

let newlist = arr.map(function(item){ 
    return `<li>
       		 <b>${item}</b>
       	   </li>`
})
// 可以使用箭头函数写成
let newlist = arr.map((item)=>`<li> <b>${item}</b></li>`)
let newlist = arr.map(item=>`<li> <b>${item}</b></li>`)

4.无法访问arguments,无法new

let test3 = ()=>{
   console.log(arguments)
 }

test3(1,2,3,4) //Uncaught ReferenceError: arguments is not defined
new test3() // test3 is not a constructor

5.箭头函数没有this

箭头函数内部没有 this,箭头函数的 this 指向父作用域

<body>
    模糊搜索:
    <input type="text" id="mysearch">
    <script>
        let osearch = document.querySelector("#mysearch")
        osearch.oninput = function(){
            // console.log(this.value)
            // let _this = this
            setTimeout(()=>{
                console.log(this.value)
                console.log(`发送${this.value}到后端,获取列表数据`)
            },1000)
        }
    </script>   
</body>

由于箭头函数没有this,他里面的this指向父作用域也就是指向了osearch的this,这就解决了异步操作时this的指向问题。

09【symbol】

ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它属于 JavaScript 语言的原生数据类型之一,其他数据类型是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。

1.基础知识

let s1 = Symbol() //生成了一个symbol类型数据
  • 不能进行运算

console.log(s1>"aaaaa")
  • 显示调用toString()

console.log(s1.toString() + "aaaa")
  • 隐式转换boolean

if (s1) {
  console.log("执行")
}

2.和对象属性表达式一起使用

let keys = {
    name: Symbol("name"),
    age: Symbol("age"),
    location: Symbol("location"),
    test: Symbol("test")
}
let obj = {
    [keys.name]: "kerwin",
    [keys.age]: 100,
    [keys.location]: "dalian",
    [keys.test]() {
        console.log("test")
    }
}

console.log(obj[keys.name])
// obj[keys.test]()

obj.name = "aaaaaaa"
console.log(obj)

防止别人修改自己的代码属性,symbol可接受传参,目的是为了将每个属性进行区分

3.不能进行for in遍历问题

for(let i in obj){
    console.log(i)
 } //只能打印出加的普通属性
console.log(Object.getOwnPropertySymbols(obj))//打印symbol属性,不能打印普通属性

既要打印出普通属性又要打印出symbol属性用Reflect.ownKeys()方法:

 Reflect.ownKeys(obj).forEach(item=>{
     console.log(item,obj[item])
 })

4.作为常量

统一代码的一致性

   const VIDEO = Symbol();
        const AUDIO = Symbol();
        const IMAGE = Symbol();
        

        function play(type){
            switch(type){
                case VIDEO:
                console.log("视频播放")
                break;
                case AUDIO:
                console.log("音频播放")
                break;
                case IMAGE:
                console.log("图片播放")
                break;
            }
        }

        play(IMAGE)

10【Iterator迭代器】

Iterator 的作用有三个:

一是为各种数据结构,提供一个统一的、简便的访问接口;

二是使得数据结构的成员能够按某种次序排列;

三是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of循环

let arr = ["kerwin", "tiechui", "gangdaner"]

for(let i of arr){
    console.log(i)
}

Iterator 的遍历过程是这样的。

(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。

(2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。

(3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。

(4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。

let i = arr[Symbol.iterator]()
console.log(i.next())
console.log(i.next())
console.log(i.next())
console.log(i.next())
ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)。Symbol.iterator属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。

也就是说只要添加了Symbol.iterator属性,就可以进行for of遍历。

原生默认具备 Iterator 接口的数据结构如下。

  • Array

  • Set

  • Map

  • String

  • arguments 对象

  • NodeList 对象

如果满足iterator迭代器接口的,就可以用扩展运算符(...)。

案例:

封装了一个obj2,当里面的属性不可访问时,遍历里面的属性

这时需要写一个迭代器,可遍历list属性

let obj2 = {
            code: 200,
            name: "obj2",
            list: ["kerwin", "tiechui", "gangdan"],
            //迭代器
            [Symbol.iterator]() {
                let index = 0
                // console.log(this.list)
                return {
                    next: () => {
                        // console.log(this)
                        return {
                            value: this.list[index++],
                            done: index === (this.list.length + 1) ? true : false
                        }
                    }
                }
            }
        }

        for (let i of obj2) {
            console.log(i)
        }

        // let iter = obj2[Symbol.iterator]()

        // console.log(iter)

        // console.log(iter.next())
        // console.log(iter.next())
        // console.log(iter.next())
        // console.log(iter.next())
        //immutable.js

11【Set结构】

它类似于数组,但成员的值都是唯一的,没有重复的值。

存储值不一样的结构时首选Set

1. 初识Set

  • 数组去重:

let s1 = new Set([1, 2, 3, 2, 3])
console.log(s1) //{1,2,3}
console.log([...s1]) //[1,2,3]
console.log(Array.from(s1))  //[1,2,3]

2.实例的属性和方法

  • size:返回Set实例的成员总数(set数组的长度相当于length)。

  • Set.prototype.add(value):添加某个value。

  • Set.prototype.delete(value):删除某个value,返回一个布尔值,表示删除是否成功。

  • Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。

  • Set.prototype.clear():清除所有成员,没有返回值。

3 遍历

  • Set.prototype.keys():返回键名的遍历器

  • Set.prototype.values():返回键值的遍历器

set结构是没有索引的,set里面的键名和键值都是一样的,所以key()和values()遍历出来的值一样

let s2 = new Set([1, 2, 3])
for(let i of s2){
    console.log(i)  //1  2  3
}

for(let i of s2.keys()){
     console.log(i) //1  2  3
}
for(let i of s2.values()){
     console.log(i) //1  2  3  
}
  • Set.prototype.entries():返回键值对的遍历器

let s2 = new Set([1, 2, 3])
for(let i of s2.entries()){
     console.log(i) //[1,1] [2,2] [3,3]
}

//for of 搭配entries遍历数组
let arr = ["aa","bb","cc"]
for(let i of arr.entries()){
     console.log(i) //[0, 'aa']  [1, 'bb']  [2, 'cc']
} 

//for of 搭配entries以及解构赋值遍历数组
for(let [index,item] of arr.entries()){
     console.log(index,item) //0 'aa'  1 'bb'    2 'cc'
} 
  • Set.prototype.forEach():遍历每个成员

s2.forEach((item,index)=>{
    console.log(item,index) //1 1   2 2   3 3
})

4 复杂数据结构去重案例

let list = [1,2,2,"kerwin","kerwin",[1,2],[3,4],[1,2],{name:"kerwin"},{age:100},{name:"kerwin"},undefined,undefined,NaN,NaN]
        
        function uni(arr){
            let res = new Set();
            return arr.filter((item)=>{
                //判断 has trun false
                // 没有 return true
                let id = JSON.stringify(item);
                if(res.has(id)){
                    return false;
                }else{
                    res.add(id);
                    return true;
                }
            })
        }  

console.log(uni(list));

JSON.stringify()方法介绍:

JSON.stringify 方法是将一个 JavaScript 对象或值转换为 JSON 字符串,默认该方法其实有三个参数:第一个参数是必选,后面两个是可选参数非必选。第一个参数传入的是要转换的对象;第二个是一个 replacer 函数,比如指定的 replacer 是数组,则可选择性地仅处理包含数组指定的属性;第三个参数用来控制结果字符串里面的间距,后面两个参数整体用得比较少。

//该方法的语法为:
JSON.stringify(value[, replacer [, space]])

具体参照:https://blog.csdn.net/CRMEB/article/details/118904748

12【Map结构】

类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。

存取键值对首选Map

1.初识Map

let m1 = new Map()
m1.set("name","kerwin")
m1.set({a:1},"大连")

console.log(m1)

let m2= new Map([
    ["name","kerwin"],
    [{a:1},"大连"]
])
console.log(m2)

2 实例的属性和方法

  • size:返回 Map 结构的成员总数(length)。

  • Map.prototype.set(key,value):添加key对应得value,返回 Map 结构本身。

  • Map.prototype.get(key):获取key对应的value

  • Map.prototype.delete(key):删除某个键(键名+键值)

  • Map.prototype.has(key):某个键是否在当前 Map 对象之中。

  • Map.prototype.clear():清除所有成员,没有返回值。

3 遍历

  • Map.prototype.keys():返回键名的遍历器。

  • Map.prototype.values():返回键值的遍历器。

  • Map.prototype.entries():返回所有成员的遍历器。

  • Map.prototype.forEach():遍历 Map 的所有成员。

13【Proxy代理】

Proxy如其名, 它的作用是在对象和和对象的属性值之间设置一个代理,获取该对象的值或者设置该对象的值, 以及实例化等等多种操作, 都会被拦截住, 经过这一层我们可以统一处理,我们可以认为它就是“代理器”

1.拦截数据什么时候被修改

<div id="box"></div>
<script>
let obj = {
   data:"11",
}
obj.data = "22";

//怎样拦截到data什么时候被修改了?

let obj = {}

    Object.defineProperty(obj,"data",{
        get(){
            console.log("get")
            return box.innerHTML
        },
        set(value){
            console.log("set",value)
            //设置dom
            box.innerHTML = value
        }
    })

    console.log(obj)

//通过 Object.defineProperty()可以把obj的某一个属性进行一个get和set的拦截,只要访问这个属性,get方法就会调用,只要修改这个属性set方法就会调用
</script>

Object.defineProperty()拦截缺点:

  • 每次只能拦截一个属性

  • 只能拦截对象

2.proxy代理操作

通过操作proxy来控制操作的内容

优点:

  • 可拦截多个属性

  • 可拦截多种数据类型

 <div id="box"></div>
    <script>
        let obj = {}
        let proxy = new Proxy(obj,{
            //get()访问属性
            get(target,key){
                console.log("get",target[key])
                return target[key]  //get必须有return值
            },
			
            //set()修改属性
            set(target,key,value{
                 console.log("set",target,key,value)
                if(key==="data"){
                    box.innerHTML = value
                }
                target[key] = value
            },
            
            //has()判断代理的对象中有没有某个属性,不常用
            has(){
                return false
            }
        })

3.this问题

let s = new Set()
        let proxy = new Proxy(s,{
            get(target,key){
                //判断如果式方法,修正this指向
                let value = target[key]
                if(value instanceof Function){
                    //call apply bind
                    return value.bind(target)
                }
                return value
            },
            set(target,key,value){
                target[key] = value
                console.log("set")
            }
        })

call apply bind 改变this指向问题请参照:https://blog.csdn.net/m0_67880202/article/details/128719416?spm=1001.2014.3001.5502

14【Reflect对象】

Reflect 可以用于获取目标对象的行为,它与 Object 类似,但是更易读,为操作对象提供了一种更优雅的方式。它的方法与 Proxy 是对应的。

Reflect 用于改变默认行为

1.代替Object的某些方法

const obj = {
};
Reflect.defineProperty(obj, 'name', {
    value: 'kerwin',
    writable: false,
    configurable:false
});

2.修改某些Object方法返回结果

// 老写法
try {
  Object.defineProperty(target, property, attributes);
  // success
} catch (e) {
  // fail
}

// 新写法
if (Reflect.defineProperty(target, property, attributes)) {
  // success
} else {
  // fail
}
//Reflect好处:在处理异常错误的时候,不用try catch ,可直接通过判断true false ,保证代码不被打断

3.命令式变为函数行为

const obj = {
    name:"kerwin"
};
//老写法
console.log("name" in obj) //true
//新写法
console.log(Reflect.has(obj, 'name')) //true

//老写法
delete obj.name
//新写法
Reflect.deleteProperty(obj, "name")

4.配合Proxy

let obj = {
    name:"kerwin"
}
//set
Reflect.set(obj,"age",100)
//get
console.log(Reflect.get(obj,"name"))
let s = new Set()

let proxy = new Proxy(s, {
    get(target, key) {
        //判断如果式方法,修正this指向
        //target[key]
        let value = Reflect.get(target,key)
        if (value instanceof Function) {
            //call apply bind
            return value.bind(target)
        }
        return value
    },
    //用reflect修改本身的默认行为
    set(target, key, value) {
        Reflect.set(...arguments)
    }
})
//代理数组
let arr  = [1,2,3]

let proxy = new Proxy(arr,{
    get(target,key){
        console.log("get",key)
        return Reflect.get(...arguments)
    },
    set(target,key,value){
        console.log("set",key,value)
        return Reflect.set(...arguments)
    }
})

15【Promise】

Promise 是异步编程的一种解决方案,比传统的解决方案回调函数, 更合理和更强大。ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象 。

  • 指定回调函数方式更灵活易懂。

  • 解决异步 回调地狱 的问题。

1.回调地狱

  • 当一个回调函数嵌套一个回调函数的时候

  • 就会出现一个嵌套结构

  • 当嵌套的多了就会出现回调地狱的情况

  • 比如我们发送三个 ajax 请求

  • 第一个正常发送

  • 第二个请求需要第一个请求的结果中的某一个值作为参数

  • 第三个请求需要第二个请求的结果中的某一个值作为参数

ajax({
  url: '我是第一个请求',
  success (res) {
    // 现在发送第二个请求
    ajax({
      url: '我是第二个请求',
      data: { a: res.a, b: res.b },
      success (res2) {
        // 进行第三个请求
        ajax({
          url: '我是第三个请求',
          data: { a: res2.a, b: res2.b },
  				success (res3) { 
            console.log(res3) 
          }
        })
      }
    })
  }
})

回调地狱,其实就是回调函数嵌套过多导致的

当代码成为这个结构以后,已经没有维护的可能了

2.Promise使用

  • 语法:

new Promise(function (resolve, reject) {
  // resolve 表示成功的回调
  // reject 表示失败的回调
}).then(function (res) {
  // 成功的函数
}).catch(function (err) {
  // 失败的函数
})
let p = new Promise(function(resolve,reject){})

//then接收两个回调
p.then(function(){
    //成功
},function(){
    //失败
})

//then接收一个回调
p.then(function(){
    //成功
}).catch(function(){
    //失败
})

3.Promise 对象的状态

Promise 对象通过自身的状态,来控制异步操作。Promise 实例具有三种状态。

异步操作未完成(pending)
异步操作成功(fulfilled)
异步操作失败(rejected)

这三种的状态的变化途径只有两种。

从“未完成”到“成功”
从“未完成”到“失败”

一旦状态发生变化,就凝固了,不会再有新的状态变化。这也是 Promise 这个名字的由来,它的英语意思是“承诺”,一旦承诺成效,就不得再改变了。这也意味着,Promise 实例的状态变化只可能发生一次。

因此,Promise 的最终结果只有两种。

异步操作成功,Promise 实例传回一个值(value),状态变为fulfilled。
异步操作失败,Promise 实例抛出一个错误(error),状态变为rejected。

4.封装简单ajax验证promise

function ajax(url){
            return new Promise((resolve,reject)=>{
                let xhr = new XMLHttpRequest()
                xhr.open("get",url,true)
                xhr.send()
                xhr.onreadystatechange = function(){
                    if(xhr.readyState===4){
                        if(xhr.status>=200&&xhr.status<300){
                            resolve(JSON.parse(xhr.responseText))
                        }else{
                            reject(xhr.responseText)
                        }
                    }
                }
            })
        }
        
        ajax("1.json").then(res=>{
            console.log(res)
        }).catch(err=>{
            console.log(err)
        })

5.回调地域解决方案

let p = new Promise(function(resolve,reject){})

//then接收一个回调
`p.then(function(){
    //p.then()返回的是一个promise对象,所以后面可再接then()
    //如果return 非promise类型,pending-fulfilled
    //如果return promise类型,根据这个新的promise对象的结果,决定pending-fulfilled pending-rejected
})`.then(function(){
    
})

6.promise对象方法

Promise 是一个对象,也是一个构造函数。

1.Promise.resolve

将现有对象转为 Promise 对象

Promise.resolve('kerwin')
// 等价于
new Promise(resolve => resolve('kerwin'))

2.Promise.reject

Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。

const p = Promise.reject('error');
// 等同于
const p = new Promise((resolve, reject) => reject('error'))

3.promise.all(es6)

Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

const p = Promise.all([p1, p2, p3]);

p的状态由p1,p2,p3 决定,分成两种情况。

(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。

(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

4.Promise.race(es6)

Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

const p = Promise.race([p1, p2, p3]);

上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

5.Promise.allSettled

Promise.allSettled()方法,用来确定一组异步操作是否都结束了(不管成功或失败)。所以,它的名字叫做”Settled“,包含了”fulfilled“和”rejected“两种情况。

const promises = [ ajax('/200接口'), ajax('/401接口') ];

Promise.allSettled(promises).then(results=>{
    // 过滤出成功的请求
    results.filter(item =>item.status === 'fulfilled');
    过滤出失败的请求
    results.filter(item=> item.status === 'rejected');
})

6.Promise.any

只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。

Promise.any()跟Promise.race()方法很像,只有一点不同,就是Promise.any()不会因为某个 Promise 变成rejected状态而结束,必须等到所有参数 Promise 变成rejected状态才会结束。

16【Generator函数】

Generator 函数是 ES6 提供的一种异步编程解决方案
Generator 函数是一个状态机,封装了多个内部状态。
执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。

1.基本语法

function *gen(){
    console.log(1)
    yield;//'产出' 遇到yield暂停
    console.log(2)
    yield;
    console.log(3)
}

let g = gen()
g.next()
g.next()
g.next()

yield(产出)表达式是暂停执行的标记,而next方法可以恢复执行。

function* gen() {
      // console.log(11)
      yield "aaa"//产出
      // console.log(22)
      yield "bbb"
      // console.log(33)
      return "ccc"
}

let g = gen()
let res1 = g.next()
console.log(res1) //{value: 'aaa', done: false}
let res2 = g.next()
console.log(res2) //{value: 'bbb', done: false}
let res3 = g.next()
console.log(res3) //{value: 'ccc', done: true}

//可用for of 遍历产出值
// for(let i of g){
//     console.log(i)
// }

2.next()传参

为何第一个参数没有作用,详解见代码

function* gen() {
     //②遇到yield代码就暂停了,此时还未进行赋值操作
     let res1 = yield "aaa"
     console.log("gen内部", res1)  //输出为:gen内部 传入-22222
     let res2 = yield "bbb"
     console.log("gen内部", res2)  //输出为:gen内部 传入-33333
}

     let g = gen()

     //①代码执行g.next("传入-11111")
     let res1 = g.next("传入-11111")
   
	 //③当再一次执行next进行赋值操作,将next中的参数 "传入-22222" 赋值给 res1,所以传入的第一个参数 "传入-11111" 没有作用
     let res2 = g.next("传入-22222")
     let res3 = g.next("传入-33333")

3.异步

function ajax(url) {
            return new Promise((resolve, reject) => {
                let xhr = new XMLHttpRequest()
                xhr.open("get", url, true)
                xhr.send()
                xhr.onreadystatechange = function () {
                    if (xhr.readyState === 4) {
                        if (xhr.status >= 200 && xhr.status < 300) {
                            resolve(JSON.parse(xhr.responseText))
                        } else {
                            reject(xhr.responseText)
                        }
                    }
                }
            })
        }

手动版本

function *gen(){
    let res1 = yield ajax("1.json")
    console.log(res1)
    let res2 = yield ajax("2.json")
    console.log(res2)
}

let g = gen()   

//g.next().value是一个promise对象
g.next().value.then(data=>{
    //将data赋值给 res1
    g.next(data).value.then(res=>{
        //将data赋值给 res2
        g.next(res)
    })
}) 

自动版本

function *gen(){
    let res1 = yield ajax("1.json")
    console.log(res1)
    let res2 = yield ajax("2.json")
    console.log(res2)
}

//自动版本(递归),前提是yield后必须接的是promise对象
function AutoRun(gen) {
            let g = gen();
            function next(data) {
                let res = g.next(data);
                if (res.done) return
                res.value.then(function (data) {
                    next(data);
                });
            }
            next();
        }
AutoRun(gen)

17【Class语法】

1.类的写法

class Person {
    constructor(name,age){
        this.name = name;
        this.age = age;
    }
    say(){
        console.log(this.name,this.age)
    }
}
let obj = new Person("kerwin",100)
console.log(obj)

2.结合属性表达式

let s = Symbol('say')
        class Person {
            constructor(name,age){
                this.name = name
                this.age =age
            }

            [s](){
                console.log(this.name,this.age)
            }
        }
        let obj = new Person("kerwin",100)

        console.log(obj.__proto__===Person.prototype)

        obj[s]()

3.getter与setter

操作dom

<body>
    <ul id="list"> </ul>
    <script>
        class Person {
            constructor(name, age, id) {
                this.name = name;
                this.age = age;
                this.ele = document.querySelector(`#${id}`)
            }

            get html() {
                return this.ele.innerHTML
            }
            set html(data) {
                this.ele.innerHTML = data.map(item => `<li>${item}</li>`).join("")
            }
        }
        let obj = new Person("kerwin", 100, "list")
    </script>
</body>

4.静态属性和静态方法

class Person {
    static name = "Person这个类"
    constructor(name,age){
        this.name = name;
        this.age = age;
    }
    say(){
        console.log(this.name,this.age)
    }
	//es6静态属性和静态方法关键字 static
    static eat(){
        console.log("eat")
    }
}
let obj = new Person("kerwin",100)
//es6里静态属性和静态方法相当于
//Person.eat = function(){console.log("eat")}

//静态属性和静态方法只能通过类名来访问,不能通过实例对象访问
console.log(Person.name)
Person.eat()

5.继承

ES6 规定,子类必须在constructor()方法中调用super(),否则就会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,添加子类自己的实例属性和方法。如果不调用super()方法,子类就得不到自己的this对象。

class Person {
            static myname = "person类的名字"
            static mymethod = function () {
                console.log("mythod")
            }
            constructor(name, age) {
                this.name = name
                this.age = age
            }
            say() {
                console.log(this.name, this.age)
            }
        }
		
		//继承关键字 extends
        class Student extends Person {
            static myname = "Student类的名字"
            static mymethod = function () {
                console.log("Student- mythod", this.myname)
            }
            constructor(name, age, score) {
                super(name, age)//调用了父类中的构造函数
                this.score = score
            }
            say() {
                super.say()
                console.log(this.score)
            }
            getScore() {
                console.log(this.score)
            }
        }

        let obj = new Student("kerwin", 100, 150)

将类、继承用再页面渲染上 :

<body>
    <div class="box1">
        <h1></h1>
        <ul></ul>
    </div>
    <div class="box2">
        <h1></h1>
        <img src="" alt="" style="width:100px;">
        <ul></ul>
    </div>
    <script>
        var data1 = {
            title: "体育",
            list: ["体育-1", "体育-2", "体育-3"]
        }
        var data2 = {
            title: "综艺",
            url: "https://pic.maizuo.com/usr/movie/5011ee407fb407d47e333a3935ec33d1.jpg?x-oss-process=image/quality,Q_70",
            list: ["综艺-1", "综艺-2", "综艺3"]
        }

        class CreatBox{
            constructor(select,data){
                this.ele = document.querySelector(select)
                this.title = data.title
                this.list = data.list
                this.render()
            }
            render(){
                let oh1 = this.ele.querySelector("h1")
                let oul = this.ele.querySelector("ul")

                oh1.innerHTML = this.title
                oul.innerHTML = this.list.map(item=>
                    `<li>${item}</li>`
                ).join("")
            }
        }
        new CreatBox(".box1",data1)

        class CreateImgBox extends CreatBox{
            constructor(select,data){
                super(select,data)
                this.imgUrl = data.url

                this.render()
            }
            render(){
                // console.log("111111",this.imgUrl)
                super.render()
                let oimg = this.ele.querySelector("img")
                oimg.src=  this.imgUrl
            }
        }

        new CreateImgBox(".box2",data2)
    </script>
</body>

18【Module语法】

JavaScript 现在有两种模块。一种是 ES6 模块,简称 ESM;另一种是 CommonJS 模块,简称 CJS。
CommonJS 模块是 Node.js 专用的,与 ES6 模块不兼容。语法上面,两者最明显的差异是,CommonJS 模块使用require()和module.exports,ES6 模块使用import和export。

ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。

使用模块化好处:

  • 异步加载

  • 私密不漏

  • 重名不怕

  • 依赖不乱

写法1:

//导出A1
export default A1

//导入1.js里的A1(导入时的名字可自己命名),from后接相对路径
import A1 from "./1.js"

写法2:

两种导出方式,三种导入方式

//①导出A1,A2
export {A1,A2}
//②可直接在需要导出的函数前加export
export function A1(){
    console.log("A1")
}

//① 导入A1,A2 ,此方法导入导出名字必须一致
import {A1,A2} from "./1.js"

//② as重命名,将A1重命名为a1
import {A1 as a1,A2 as a2} from "./1.js"
//③ *as将1.js里需要导出的全部导入
import * as obj from "./1.js"

混合使用:

//两种导出方式可混合使用
export {A1}
export default A2
//导入时根据各自的特点进行导入
import A2,{A1} from "./1.js"
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值