js高级

159 阅读15分钟
varletconst
函数级作用域块级作用域块级作用域
变量提升不存在变量提升不存在变量提升
值可更改值可更改值不可更改

1.es6的新增语法

1.let

ES6中新增的用于声明变量的关键字。

let声明的变量只在所处于的块级有效

if (true) { 
     let a = 10;
}
    console.log(a) // a is not defined
				//A没有定义
//注意:使用let关键字声明的变量才具有块级作用域,使用var声明的变量不具备块级作用域特性。

不存在变量提升

 console.log(a); // a is not defined 
 let a = 20;

暂时性死区

 var tmp = 123;
 if (true) { 
     tmp = 'abc';
     let tmp; 
 } 

2.const

作用:声明常量,常量就是值(内存地址)不能变化的量

具有块级作用域

 if (true) { 
     const a = 10;
 }
console.log(a) // a is not defined
				//A没有定义

声明常量时必须赋值

const PI; // Missing initializer in const declaration
		//在const声明中缺少初始化式

常量赋值后,值不能修改。

 const PI = 3.14;
 PI = 100; // Assignment to constant variable
		//对常数变量的赋值。
 const ary = [100, 200];
 ary[0] = 'a';
 ary[1] = 'b';
 console.log(ary); // ['a', 'b']; 
 ary = ['a', 'b']; // Assignment to constant variable.
				//对常数变量的赋值。

3.let、const、var 的区别

  1. 使用 var 声明的变量,其作用域为该语句所在的函数内,且存在变量提升现象。

  2. 使用 let 声明的变量,其作用域为该语句所在的代码块内,不存在变量提升。

  3. 使用 const 声明的是常量,在后面出现的代码中不能再修改该常量的值。

2.箭头函数

1.ES6中新增的定义函数的方式

() => {} 
const fn = () => {}

2.函数体中只有一句代码,且代码的执行结果就是返回值,可以省略大括号

 function sum(num1, num2) { 
     return num1 + num2; 
 }
 const sum = (num1, num2) => num1 + num2;

3.如果形参只有一个,可以省略小括号

 function fn (a) {
     return a;
 } 
 const fn = a => a;

4.箭头函数不绑定this关键字,箭头函数中的this,指向的是函数定义位置的上下文this。

 const obj = { name: '张三'} 
 function fn () { 
     console.log(this);
     return () => { 
         console.log(this)
     } 
 } 
 const resFn = fn.call(obj); 
 resFn();

5.函数的简写总结

 <button>潮吧点击</button>
    <script>
      //以前写函数
      function fun1(num1) {
        return num1 + 100;
      }
      //箭头函数的时候   函数=(参数)=>{执行业务}
      // const fun2 = (num1) => num1 +100

      //1.定义一个箭头函数 没有形参 没有返回值  2行实际业务
      const fun3 = () => {
        const num3 = 100 + 100;
        console.log(num3);
      };

      //2.只有一个参 没有返回值 业务只有一行代码
      // const fun4 = (num)=> console.log('潮吧');
      const fun4 = (num) => console.log("潮吧");

      //3.两个或多个参数(括号不能省略) 没有返回值 业务只有一行代码
      const fun5 = (a, b) => console.log(a + b);

      //4.没有形参 有返回值 业务两行代码\
      const fun6 = () => {
        let a = 100 + 100;
        return a;
      };
      //5.没有形参 有返回值 业务一行代码
      //   const fun7 = ()=> {
      //       return 100+100
      //   }
      //没有形参 有返回值 业务一行代码  等价上述写法
      const fun7 = () => 100 + 100;

      //实际应用
      const button = document.querySelector("button");
      button.addEventListener("click", () => {
        console.log("潮吧");
      });
            /* 
      大部分情况,对于普通函数,this 等于这个函数的调用者
        (例外 - bind、call、apply)
      大部分情况,对于箭头函数,this 等于window
        箭头函数和this 一起用的话,要慎用!! 
        例外(面向对象用法,)
       */
    </script>

3.常用数组方法

方法描述
every()检测数值元素的每个元素是否都符合条件。
filter()检测数值元素,并返回符合条件所有元素的数组。
find()返回符合传入测试(函数)条件的数组元素。
findIndex()返回符合传入测试(函数)条件的数组元素索引。
forEach()数组每个元素都执行一次回调函数。
includes()判断一个数组是否包含一个指定的值。
indexOf()搜索数组中的元素,并返回它所在的位置。
isArray()判断对象是否为数组。
join()把数组的所有元素放入一个字符串。
map()通过指定函数处理数组的每个元素,并返回处理后的数组。
reduce()将数组元素计算为一个值(从左到右)。
reverse()反转数组的元素顺序。
some()检测数组元素中是否有元素符合指定条件。
sort()对数组的元素进行排序。

1.数组-forEach

作用:遍历数组

    <script>
        //forEach
        // forEach()
      function forEach() {
        const arr = ["大", "潮", "吧"];
        //forEach() 数组每个元素都执行一次回调函数  = 类似以前的 for   forEach 高级函数(可以接收一个形参-函数)
        //for循环可以通过 break来打断   forEach不能通过break打断

        //分别打印

        arr.forEach(function (value, index) {
          console.log(`值是${value} 下标是${index}`);
        });
        arr.forEach((value, index) => {
          console.log(`值是${value} 下标是${index}`);
        });
      }
    </script>

4.面向对象

1. 面向过程编程 POP(Process-oriented programming)

面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次调用就可以了。

一种编程行业通用的写项目级的代码的思维,引导我们如何编写高质量的代码,万物皆对象 - 看待事物的角度,(属性:数据,行为:动作(方法))。代码特点是封装和继承

2.面向过程和面向对象的对比

1.面向过程

优点:性能比面向对象高,适合跟硬件联系很紧密的东西,例如单片机就采用的面向过程编程。

缺点:没有面向对象易维护、易复用、易扩展

3.面向对象

优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统 更加灵活、更加易于维护

缺点:性能比面向过程低

3.对象

现实生活中:万物皆对象,对象是一个具体的事物,看得见摸得着的实物。例如,一本书、一辆汽车、一个人可以是“对象”,一个数据库、一张网页、一个与远程服务器的连接也可以是“对象”。

在 JavaScript 中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等

对象是由属性和方法组成的:

属性:事物的特征,在对象中用属性来表示(常用名词)

方法:事物的行为,在对象中用方法来表示(常用动词)

4.类class

在 ES6 中新增加了类的概念,可以使用 class 关键字声明一个类,之后以这个类来实例化对象。

类抽象了对象的公共部分,它泛指某一大类(class) 对象特指某一个,通过类实例化一个具体的对象

语法:

class name{
    //class body
}

创建实列

let NewName =new name()
//注意: 类必须使用 new 实例化对象

5.类 constructor 构造函数

constructor() 方法是类的构造函数(默认方法),用于传递参数,返回实例对象,通过 new 命令生成对象实例时,自动调用该方法。如果没有显示定义, 类内部会自动给我们创建一个constructor()

语法:

class Person {
  constructor(name,age) {   // constructor 构造方法或者构造函数
      this.name = name;
      this.age = age;
    }
}  

创建实列

let ldh = new Person('刘德华', 18); 
console.log(ldh.name) 

6.类添加方法

语法:

class Person {
  constructor(name,age) {   // constructor 构造器或者构造函数
      this.name = name;
      this.age = age;
    }
   say() {
      console.log(this.name + '你好');
   }
} 

创建实列:

let ldh = new Person('刘德华', 18); 
ldh.say()  
//注意: 方法之间不能加逗号分隔,同时方法不需要添加 function 关键字。

7.类的继承

现实中的继承:子承父业,比如我们都继承了父亲的姓。

程序中的继承:子类可以继承父类的一些属性和方法

1.语法

class Father{   // 父类
} 
class  Son extends Father {  // 子类继承父类
}   

2. super 关键字

super关键字 用于访问和调用对象父类上的函数。可以调用父类的构造函数,也可以调用父类的普通函数。

语法

class Father {
     say() {
         return '我是爸爸';

     }
}
class Son extends Father { // 这样子类就继承了父类的属性和方法
     say() {
          // super.say()  super 调用父类的方法
          return super.say() + '的儿子';
     }
}
let damao = new Son();
console.log(damao.say()); 

8.注意点

1.在 ES6 中类没有变量提升,所以必须先定义类,才能通过类实例化对象.

2.类里面的共有属性和方法一定要加this使用.

3.类里面的this指向问题.

4.constructor 里面的this指向实例对象, 方法里面的this 指向这个方法的调用者

对象在内存中的示意图

1.png

5.创建对象的几种方式

在典型的 OOP 的语言中(如 Java),都存在类的概念,类就是对象的模板,对象就是类的实例,但在 ES6之前, JS 中并没用引入类的概念。 ES6, 全称 ECMAScript 6.0 ,2015.06 发版。但是目前浏览器的 JavaScript 是 ES5 版本,大多数高版本的浏览器也支持 ES6,不过只实现了 ES6 的部分特性和功能。 在 ES6之前 ,对象不是基于类创建的,而是用一种称为构建函数的特殊函数来定义对象和它们的特征。

1.字面量

  • 简单粗暴
  • 不适合创建多个同样类型的对象的场景
const obj ={ name:"悟空",height:100,age:5000};

2.工厂函数

  1. 容易理解
  2. 失去血缘关系,无法简单分辨对象的特征
  3. 后期无法实现继承

2.png

    function createPerson(name, age, height) {
      return {
        name: name,
        age: age,
        height: height
      }
    }

    function createStudent(name, age, height, grade) {
      return {
        name1: name,
        age1: age,
        height1: height,
        grade: grade
      }
    }

    // 这个不是工厂模式
    function createStudent2(name, age, height, grade) {
      this.name = name;
      this.age = age;
      this.height = height;
      this.grade = grade;
    }


    const obj1 = createPerson("八戒", 18, 500);
    const obj2 = createStudent("悟能", 83, 231, 3);
    const obj3 = new createStudent2("悟能", 83, 231, 3);

    console.log(obj1);
    console.log(obj2);
    console.log(obj3);

3.构造函数

  1. 可以方便的创建对象
  2. 拥有血缘关系
  3. 还有后续更多的优势
    // 1 声明函数 
    function createStudent(name, age) {
      // 2 通过 this 赋值
      this.name = name;
      this.age = age;
    }

    // 3 通过 new 来创建对象
    const obj = new createStudent("悟能", 83);

    console.log(obj);

小结: 笔试题比较常见

​ 构造函数的工作原理:

  1. 开辟空间
  2. 将新的创建的对象对象构造函数中的this
  3. 为对象赋值
  4. 将创建好的对象的地址返回

4.构造函数的弊端

同一个 say 方法占据了两份内存

    function createStudent(name, age) {
      this.name = name;
      this.age = age;
      this.say = function () {
        console.log(this.name);
      }
    }

    const obj = new createStudent("悟能", 83);
    const obj1 = new createStudent("悟能1", 84);

    console.log(obj.say === obj1.say); // false  不是同一say方法 浪费了内存

图示

3.png

提取同一个 say 方法

  1. 解决了浪费内存的弊端
  2. 但是造成了 污染全局变量 的问题
    // 提前将say 声明好
    function say() {  // 污染全局变量
      console.log(this.name);
    }
    function createStudent(name, age) {
      this.name = name;
      this.age = age;
      this.say = say
    }

    const obj = new createStudent("悟能", 83);
    const obj1 = new createStudent("悟能1", 84);

    console.log(obj.say === obj1.say); // true

图示

4.png

原型模式

在构造函数的原型上 存放函数

  1. 解决了同一个 say 浪费 内存的问题
  2. 解决了污染全局变量的问题
    function createStudent(name, age) {
      this.name = name;
      this.age = age;
    }
    // 将刚才的全局函数say 直接挂载到 构造函数的原型上 即可
    // prototype 是个对象 每一个构造函数都会内置有的. 我们称之为原型
    createStudent.prototype.say = function () {
      console.log(this.name);
    }

    const obj = new createStudent("悟能", 83);
    const obj1 = new createStudent("悟能1", 84);

    console.log(obj.say === obj1.say); // true

解释

  • 原型的单词是 prototype, 原型的这个名字是行业内共同认可的名字。
  • 原型本质是一个对象,理解为 JavaScript 自动帮我们添加的
  • 原型是 JavaScript 自动帮我们在定义构造函数的时候添加的
  • 所有构造函数的实例,共享一个原型
  • 原型上一般是挂载函数

5.png

6.原型链继承

1.call()

调用这个函数, 并且修改函数运行时的 this 指向

fun.call(thisArg, arg1, arg2, ...)
         //thisArg :当前调用函数 this 的指向对象
         //arg1,arg2:传递的其他参数

2.借用构造函数继承父类型属性

核心原理: 通过 call() 把父类型的 this 指向子类型的 this ,这样就可以实现子类型继承父类型的属性。

 // 父类
    function Person(name, age, sex) {
      this.name = name;
      this.age = age;
      this.sex = sex;
    }
    // 子类
    function Student(name, age, sex, score) {
      Person.call(this, name, age, sex);  // 此时父类的 this 指向子类的 this,同时调用这个函数
      this.score = score;
    }
    var s1 = new Student('zs', 18, '男', 100);
    console.dir(s1);

3.借用原型对象继承父类型方法

一般情况下,对象的方法都在构造函数的原型对象中设置,通过构造函数无法继承父类方法。
核心原理:

1.将子类所共享的方法提取出来,让子类的 prototype 原型对象 = new 父类()

2.本质:子类原型对象等于是实例化父类,因为父类实例化之后另外开辟空间,就不会影响原来父类原型对象

3.将子类的 constructor 从新指向子类的构造函数

利用代码的能力实现 面向对象的特性 封装继承

4.类的本质

  1. class本质还是function.
  2. 类的所有方法都定义在类的prototype属性上
  3. 类创建的实例,里面也有__proto__ 指向类的prototype原型对象 4.所以ES6的类它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。 5.所以ES6的类其实就是语法糖.
  4. 语法糖:语法糖就是一种便捷写法. 简单理解, 有两种方法可以实现同样的功能, 但是一种写法更加清晰、方便,那么这个方法就是语法糖

初体验

  1. 子类strudent 继承了父类 Person的属性
    // 父类
    function Person(name, height) {
      this.name = name;
      this.height = height;
    }

    Person.prototype.say = function () {
      console.log(this.name);
      console.log(this.height);
    }

    // 子类
    function Student(grade, name, height) {
      // 借用了父类的构造函数,完成对自己的赋值
      Person.call(this, name, height)
      this.grade = grade;
    }

    // 赋值了父类原型上的所有的 属性和方法
    Student.prototype = Person.prototype;
    // 修改之类的指向
    Student.prototype.constructor = Student;

    // 创建子类的实例
    const stu = new Student("一年", "周星星", 170);
    stu.say();

需求

  1. 有一个负责创建元素的构造函数 A
  2. 有一个负责创建图片的构造函数 B
  3. 构造函数 B 可以使用 构造函数 A 的原型上的所有的功能 实现继承

效果

7.png

代码

    // 1 负责创建元素  
    function Element(nodeName, text) {
      const node = document.createElement(nodeName);
      node.classList.add("element")
      node.innerText = text;
      this.node = node;
    }

    // 2 原型上挂载 共用的方法
    Element.prototype.appendTo = function (selector) {
      const parent = document.querySelector(selector);
      parent.appendChild(this.node);
    }

    // 3 创建一个实例
    const div = new Element("div", "做人开心就好");
    // 4 追加到父元素上
    div.appendTo("section");

    // 5 一个新的构造函数 用来创建图片
    function ElementImg(src) {
      // 6 借用了 1 中的构造函数,并且把参数传递了进去
      Element.call(this, "img", "");
      // 7 图片设置路径
      this.node.src = src;
    }

    // 8 继承了 父亲的构造函数上的原型上的所有 函数
    ElementImg.prototype=Element.prototype;			// 修改来原型,也就修改了构造函数
    // 9 重新将 constructor 的指向改回来
    ElementImg.prototype.constructor=ElementImg;

    // 10 创建一个图片实例:注意,实例化代码10不能在8和9之前
    const img = new ElementImg("images/01.png");
    
    // 11 创建到父元素上
    img.appendTo("section");

7.es6 class(重点)

es6的class 的出现 基本上可以替代了es5的构造函数和原型,使之代码结构上更加简洁。

1.关键字

  1. class
  2. 属性
  3. 方法
  4. 继承 extends
  5. 构造函数 constructor
  6. 方法重写 override:子类方法覆盖父类,super.父类方法()
  7. 父类的构造函数 super :子类有构造方法且使用this前,必须使用super()

2.完整代码体验

    class Person {
      // 构造方法
      constructor(name) {
        // 属性
        this.name = name;
      }
      // 方法
      say() {
        console.log(this.name);
      }
    }
	// 继承
    class Student extends Person{
      constructor(name,height){
        // console.log(this);			// 语法错误:必须先调用super()才能使用this
        super(name);
        this.height=height;
      }
    }

    const s1=new Student("八神",180);
    s1.say();							// 八神

	class Saler extends Person{
        constructor(name,age){
            super(name);
            this.age = age;
        }
        // 覆盖(重写)
        say(){
            // 访问父类方法
            super.say();				// 马云
            console.log(this.age);
        }
    }
	const s2 = new Saler('马云',50);
	s2.say();							// 50

3.Tab栏切换

1.HTML+CSS样式

<!DOCTYPE html>
<html>

<head lang="en">
    <meta charset="UTF-8">
    <title>tab栏切换</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        ul {
            list-style: none;
        }

        .box {
            width: 400px;
            height: 300px;
            border: 1px solid #ccc;
            margin: 100px auto;
        }

        .hd {
            height: 45px;
        }

        .hd span {
            display: inline-block;
            /*将行内元素转换成行内块元素,宽高才起作用*/
            width: 90px;
            background-color: pink;
            line-height: 45px;
            text-align: center;
            cursor: pointer;
        }

        .hd span.current {
            /*交集选择器,标签指定式选择器*/
            background-color: purple;
            /*紫色*/
        }

        .bd li {
            height: 255px;
            background-color: purple;
            display: none;
            /*设置隐藏*/
        }

        .bd li.current {
            display: block;
            /*显示*/
        }
    </style>
    <script src="js/TabExchange.js"></script>
</head>

<body>
    <div class="box" id="box">
        <div class="hd">
            <span class="current">体育</span>
            <span>娱乐</span>
            <span>新闻</span>
            <span>综合</span>
        </div>
        <div class="bd">
            <ul id="list">
                <li class="current">我是体育模块</li>
                <li>我的娱乐模块</li>
                <li id="li3">我是新闻模块</li>
                <li>我是综合模块</li>
            </ul>
        </div>
    </div>

    <script>
        let tab = new TabExchange('box', 'current');
        console.dir(tab);
    </script>

</body>

</html>

2.JS 引入样式

class TabExchange {

    // 构造函数
    constructor(id, className) {
        // 获取id对应的元素:规范,属于最外层
        this.id = document.querySelector(`#${id}`)
        // 规定内部有两层解构:div>span
        this.hd = this.id.querySelectorAll('div>span')
        // 规范内部有两层解构:div>ul>li
        this.bd = this.id.querySelectorAll('div>ul>li')
        // 保存当前要处理的样式类
        this.style = className;

        // 调用自己的tabClick 方法处理点击事件
        this.tabClick();
    }


    // 绑定点击事件
    tabClick() {
        // 因为接下来要绑定事件,事件里面也会出现this,所以先定义变量that来保存当前TabExchange对象的this
        let that = this

        // 循环遍历,绑定事件,并进行事件处理
        this.hd.forEach(function (item, key) {
            // item就是span,key就是span的下标
            item.addEventListener('click', function () {

                // 排他:清空hd全部样式
                that.hd.forEach(function (item1) {
                    item1.classList.remove(that.style)
                })

                // 清空bd的所有样式
                that.bd.forEach(function (item2) {
                    item2.classList.remove(that.style)
                })

                // 给当前事件元素添加样式
                that.hd[key].classList.add(that.style)
                that.bd[key].classList.add(that.style)
            })
        })
    }

}

4.元素创建继承

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<style>
    .element {
        width: 200px;
        height: 200px;
        border-radius: 50%;
        border: 10px solid #00ffff;

        text-align: center;
        font-size: 30px;
        line-height: 200px;

        overflow: hidden;
        float: left;
    }
</style>

<body>
    <script>
        // 创建一个类:可以创建普通元素,并且可以实现将元素挂到指定父元素下
        class CreateElement {
            // 构造函数,传入标签名,文本内容,和元素默认类名
            constructor(element, text, className = 'element') {
                // 1.创建元素
                const node = document.createElement(element)
                // 2.添加类名
                node.classList.add(className)
                // 3.添加文本内容(双标签有效,单标签无效)
                node.innerText = text

                // 4.将创建的元素节点存入到属性node
                this.node = node
            }

            // 将当前创建的元素 添加到指定父元素下
            appendTo(selector) {
                // 获取父元素
                const parent = document.querySelector(selector)
                // 添加子元素
                parent.appendChild(this.node)
            }
        }

        // 直接创建一个普通元素
        const div = new CreateElement('div', '做人开心就好')
        div.appendTo('body')

        // 图片标签不适用于普通元素创建(有属性src),但是想使用默认的样式和创建过程
        // 创建子类 继承 父类
        class CreateImg extends CreateElement {
            // 自己的构造函数:只需要传入图片
            constructor(src, className = 'element') {
                // 父类构造函数
                super('img', '', className)

                // 给创建的元素增加src属性
                this.node.src = src
            }
        }

        // 改变了创建的内容,但是可以直接应用父类继承的方法
        const img = new CreateImg('images/02.jpg')
        img.appendTo('body')

    </script>
</body>

</html>

8.函数参数默认值

定义函数的同时,可以给形参一个默认值

    // 定义函数的同时,可以给形参一个默认值
    function show(msg = "大家一起快活呀") {
      console.log(msg);
    }

    show();// 打印 大家一起快活呀
    show("搞笑不");// 打印 搞笑不

9.对象简写

在定义对象的时候,如果属性名和变量名一直,那么可以实现简写

    const name = "悟空";
    const skill = "72变";
    const say = function () { }
    const obj = {
      name, skill, say
    }
    console.log(obj);// {name:"悟空",skill:"72变",say:function(){}}

对象的方法也可以简写

    const obj = {
      say() {
        console.log(this);
      }
    }

10.解构与剩余参数

1.剩余参数

1.剩余参数语法允许我们将一个不定数量的参数表示为一个数组

 function sum (first, ...args) {
     console.log(first); // 10
     console.log(args); // [20, 30] 
 }
 sum(10, 20, 30)

2.剩余参数和解构配合使用

 let students = ['wangwu', 'zhangsan', 'lisi'];
 let [s1, ...s2] = students; 
 console.log(s1);  // 'wangwu' 
 console.log(s2);  // ['zhangsan', 'lisi']

提供更加方便获取数组中元素或者对象中属性的写法

2.解构赋值

ES6中允许从数组中提取值,按照对应位置,对变量赋值。对象也可以实现解构

按照一定模式,从数组中或对象中提取值,将提取出来的值赋值给另外的变量

获取数组中的元素

1.数组解构

      const [a, b, c, d] = [1, 2, 3, 4];
      console.log(a, b, c, d);// 1,2,3,4

2.对象解构

 let person = { name: 'zhangsan', age: 20 }; 
 let { name, age } = person;
 console.log(name); // 'zhangsan' 
 console.log(age); // 20

 let person = { name: 'zhangsan', age: 20 }; 
let {name: myName, age: myAge} = person; // myName myAge 属于别名
 console.log(myName); // 'zhangsan' 
 console.log(myAge); // 20


 <body>
    <div data-index="100" data-num="200"></div>
    <script>
      //   dataset = { index: 100  num:'200' };
      const dataset = document.querySelector("div").dataset;
      const { index, num } = document.querySelector("div").dataset;
      console.log(dataset);
      //   const {index}=dataset;
      //   const {num}=dataset;
      console.log(index);
      console.log(num);

      const arr = ["彭炎", "肖耀", "高航", "潮吧"];
      // 希望 声明四个变量 a b c d
      // 太low!! 低级  
      //   const a = arr[0];
      //   const b = arr[1];
      //   const c = arr[2];
      //   const d = arr[3];

      //数组解构  优雅写法  高端
      const [a, b, c, d] = arr; // const [a,b,c,d] = ["彭炎", "肖耀", "高航", "潮吧"];
      console.log(a, b, c, d);

      let data = { username: "彭炎", age: "18", height: "180" };

      //low 低端写法 不优雅
      //   const username = data.username
      //   const age = data.age
      //   const height = data.height

      //解构对象 优雅写法 高端
      const { username, age, height } = data;
      console.log(username, age, height);

      //解构+默认值
      // const arr = [1,100];
      // const [a,b ] = arr; a = 1 b=undefined
      // const [a, b = 2] = arr;
      // console.log(a, b); // a =1  b = 2

      // b = 2 默认值 (你没有,就使用我,你有,使用你的)
      // const [a, b = 2] = arr;
      // console.log(a,b);
      // const obj = {
      //     username: 100,
      //     height: 500,
      // };
      // const { username, height = 200 } = obj;
      // console.log(username, height);

      /* 
      小结
      1 解构 对象 和 数组上
        对象 
        const  { username,height } = {username:"悟空",height:200}
        数组
        const [a,b]=[100,200];

     2 解构 + 默认值
       如果 右边的对象中没有height 属性 那么 height变量 =  1000 
       const  { username,height=1000 } = {username:"悟空",height:200} 
        
      //  c 在右边找不到对应的数据 c 就使用默认值 300 
        const [a,b,c=300]=[100,200];
       */
    </script>
  </body>

元素交互顺序

    let a = 1111;
    let b = 2222;
    [b, a] = [a, b];
    console.log(a, b);// 2222 1111

获取对象中的属性(重点)

      const obj = {
        name: "悟空",
        skill: "72变",
        say() { }
      }
      const { name, skill,say } = obj;
      console.log(name, skill,say);// 悟空 72变 function(){}

11.拓展运算符 || 剩余运算符

1.Array 的扩展方法

1.扩展运算符(展开语法

扩展运算符可以应用于合并数组

// 方法一 
 let ary1 = [1, 2, 3];
 let ary2 = [3, 4, 5];
 let ary3 = [...ary1, ...ary2];
 // 方法二 
 ary1.push(...ary2);

将类数组或可遍历对象转换为真正的数组

  <body>
    <div>666</div>
    <div>666</div>
    <div>666</div>
    <div>666</div>
    <div>666</div>
    <script>
      let oDivs = document.querySelectorAll("div");
      oDivs = [...oDivs];
      console.log(oDivs);// [div, div, div, div, div]
    </script>
  </body>

2.构造函数方法:Array.from()

将类数组或可遍历对象转换为真正的数组

let arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
}; 
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']

方法还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组。

let arrayLike = { 
     "0": 1,
     "1": 2,
     "length": 2
 }
 let newAry = Array.from(arrayLike, item => item *2)
 console.log(newAry);// [2, 4]

3.实例方法:find()

用于找出第一个符合条件的数组成员,如果没有找到返回undefined

 let ary = [{
     id: 1,
     name: '张三'
 }, { 
     id: 2,
     name: '李四'
 }]; 
 let target = ary.find((item, index) => item.id == 2);
console.log(target);//{id: 2, name: '李四'}
let target2 = ary.find((item, index) => item.id == 3);
 console.log(target2);//undefined

4.实例方法:findIndex()

用于找出第一个符合条件的数组成员的位置,如果没有找到返回-1

let ary = [1, 5, 10, 15];
let index = ary.findIndex((value, index) => value > 9); 
console.log(index); // 2
let index2 = ary.findIndex((value, index2) => value > 20);
console.log(index2); // -1

5.实例方法:includes()

表示某个数组是否包含给定的值,返回布尔值。

[1, 2, 3].includes(2) // true 
[1, 2, 3].includes(4) // false

2.String 的扩展方法

1.模板字符串

1.ES6新增的创建字符串的方式,使用反引号定义

 let name = `zhangsan`;
console.log(name); //zhangsan

2.模板字符串中可以解析变量

let name = '张三'; 
 let sayHello = `hello,my name is ${name}`; // hello, my name is zhangsan

3.模板字符串中可以换行

 let result = { 
     name: 'zhangsan', 
     age: 20, 
     sex: '男' 
 } 
 let html = ` <div>
     <span>${result.name}</span>
     <span>${result.age}</span>
     <span>${result.sex}</span>
 </div> `;

4.在模板字符串中可以调用函数

 const sayHello = function () { 
    return '哈哈哈哈 追不到我吧 我就是这么强大';
 }; 
 let greet = `${sayHello()} 哈哈哈哈`; 
console.log(greet); // 哈哈哈哈 追不到我吧 我就是这么强大 哈哈哈哈

2.实例方法:startsWith() 和 endsWith()

startsWith():表示参数字符串是否在原字符串的头部,返回布尔值 endsWith():表示参数字符串是否在原字符串的尾部,返回布尔值

 let str = 'Hello world!';
 str.startsWith('Hello') // true 
 str.endsWith('!')       // true

3.实例方法:repeat()

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

'x'.repeat(3)      // "xxx" 
'hello'.repeat(2)  // "hellohello"

函数内获取

    function show(a, ...all) {		// 只能放最后
      console.log(a);
      console.log(all);
    }


    show(1);// 1 []
    show(1, 2, 3);// 1 [2,3]

数组内获取

    const [a, ...rest] = [1, 2, 3, 4, 5];
    console.log(a); // 1
    console.log(rest);// [2, 3, 4, 5]

对象内获取

    const obj={
      name:"悟空",
      skill:"72变",
      say(){}
    }

    const {name,...others}=obj;
    console.log(name); // 悟空
    console.log(others); // {skill: "72变", say: ƒ}

12.Set

1.set数据结构

ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

Set本身是一个构造函数,用来生成 Set 数据结构。

const set = new Set()

Set函数可以接受一个数组作为参数,用来初始化。

const set = new Set([1, 2, 3, 4, 4]);
 console.log(set); // 1,2,3,4

永远不会有重复元素的对象

可以理解为不重复的数组

    const set = new Set([1, 5, 3, 4]);
    set.add(5);
    set.add(5);
    console.log(set);

2.set的实例方法

add(value)添加某个值,返回 Set 结构本身
delete(value)删除某个值,返回一个布尔值,表示删除是否成功
has(value)返回一个布尔值,表示该值是否为 Set 的成员
clear()清除所有成员,没有返回值
 const s = new Set();
 s.add(1).add(2).add(3); // 向 set 结构中添加值 
 s.delete(2)             // 删除 set 结构中的2值 
 s.has(1)                // 表示 set 结构中是否有1这个值 返回布尔值 
 s.clear()               // 清除 set 结构中的所有值

3.遍历

Set 结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值。

set.forEach(value => console.log(value))

4.Set对象转为数组

    const set = new Set([1, 5, 3, 4]);
    set.add(5);
    set.add(5);
    console.log(set);

    const arr = [...set];// 将set对象转数组
    console.log(arr);

5.set数组去重

 <script>
      /* 
            1. set对象 是es6 才推出
            2.注意的功能 去重处理
                set.add(1)
            3.set是一个对象,不是一个数组
            4.实现 使用set转成数组的时候 需要考虑 (set 对象转-数组)
                数组 -转成对象  new Set(beforeArr)
                set对象转-数组  const arr = [...set]
        */
      //  0 有时候 先设置一个初始数组 里面已经存在一些数据
      // 在创建set的对象的时候,把数组传给它
      const beforeArr = ["a", "b", 3, 4];

      //  1 set对象 需要被new 出来
      const set = new Set(beforeArr); // 数组转成set对象

      // 2 给set添加数据的时候 使用 add方法
      set.add(1);
      set.add(2);
      set.add(2); // 发现有重复数据了,自己内部过滤
      set.add(2); // 发现有重复数据了,自己内部过滤
      set.add(2); // 发现有重复数据了,自己内部过滤
      set.add(2); // 发现有重复数据了,自己内部过滤

      // 3 打印set 看一下
      console.log(set);

      // 4 set 转成 数组(map、forEach。。。)
      const arr = [...set];
      console.log(arr);
    </script>

6.set去重案列

<body>
    <input type="text" />
    <ul></ul>
    <script>
      const input = document.querySelector("input");
      const ul = document.querySelector("ul");
      let arr = ["a", "b"];
      render();
      input.addEventListener("keydown", function (event) {
        if (event.key === "Enter") {
          const set = new Set(arr);
          set.add(this.value);
          arr = [...set];
          console.log(arr);
          render();
        }
      });

      function render() {
        const html = arr.map((value) => `<li>${value}</li>`).join("");
        ul.innerHTML = html;
      }
    </script>
  </body>

13.函数的四种调用模式

1.函数的定义方式

  1. 函数声明方式 function 关键字 (命名函数)

  2. 函数表达式 (匿名函数)

  3. new Function()

    let fun = new Function('参数1','参数2'..., '函数体')
    //Function 里面参数都必须是字符串格式
    //第三种方式执行效率低,也不方便书写,因此较少使用
    //所有函数都是 Function 的实例(对象)
    //函数也属于对象
    

函数的定义方式.png

2. 函数的调用方式

  1. 普通函数
  2. 对象的方法
  3. 构造函数
  4. 绑定事件函数
  5. 定时器函数
  6. 立即执行函数

根据函数内部this的指向不同,可以将函数的调用模式分成4种

1.函数调用模式

2.方法调用模式

3.构造函数调用模式

4.上下文调用模式(借用方法模式)

1.函数调用模式

如果一个函数不是一个对象的属性时,就是被当做一个函数来进行调用的。此时this指向了window

function fn(){
  console.log(this);// 指向window 
}
fn();

2.方法调用模式

当一个函数被保存为对象的一个属性时,我们称之为一个方法。当一个方法被调用时,this被绑定到当前对象

const obj = {
  sayHi:function(){
    console.log(this);//在方法调用模式中,this指向调用当前方法的对象。
  }
}
obj.sayHi();

3.构造函数调用模式

如果函数是通过new关键字进行调用的,此时this被绑定到创建出来的新对象上。

function Person(){
  console.log(this);
}
Person();//this指向什么?
var p = new Person();//this指向什么?

4.方法借用模式

也叫上下文模式,分为 apply 与 call

1.call

1.call() 方法调用一个对象。简单理解为调用函数的方式,但是它可以改变函数的 this 指向。

fun.call(thisArg, arg1, arg2, ...)
 //thisArg:在 fun 函数运行时指定的 this 值
 //arg1,arg2:传递的其他参数
 //返回值就是函数的返回值,因为它就是调用函数
 //因此当我们想改变 this 指向,同时想调用这个函数的时候,可以使用 call,比如继承

call方法可以调用一个函数,并且可以指定这个函数的this

    const RichWumon = {
      name: "富婆",
      say: function () {
        console.log(this.name, " 我要重金求子");
      }
    }
    const obj = {
      name: "屌丝"
    }
    RichWumon.say();			// 富婆
    RichWumon.say.call(obj);	// 屌丝

2.call应用

  1. 将伪数组转成数组
    let divs = document.querySelectorAll('div');
    // let divs = document.body.children;
    console.log(divs);

    function change(nodelist) {
        console.log(Object.prototype.toString.call(nodelist));
        return Array.prototype.slice.call(nodelist);

    }

3.apply

1.apply() 方法调用一个函数。简单理解为调用函数的方式,但是它可以改变函数的 this 指向。

fun.apply(thisArg, [argsArray])
 //thisArg:在fun函数运行时指定的 this 值
 //argsArray:传递的值,必须包含在数组里面
 //返回值就是函数的返回值,因为它就是调用函数
 //因此 apply 主要跟数组有关系,比如使用 Math.max() 求数组的最大值

2.就是apply()方法接受的是一个包含多个参数的数组。而call()方法接受的是若干个参数的列表

可以利用apply 将 刚才的call 的代码修改一下

    const RichWumon = {
      name: "富婆",
      say: function () {
        console.log(this.name, " 我要重金求子");
      }
    }

    const obj = {
      name: "屌丝"
    }

    RichWumon.say();			// 富婆
    RichWumon.say.apply(obj);	// 屌丝

4.apply应用

1.简化log方法

	// 简化log方法
    function log() {
        // 不需要改变this
        console.log.apply(console, arguments);
    }

5.bind方法

1.bind() 方法不会调用函数。但是能改变函数内部this 指向

fun.bind(thisArg, arg1, arg2, ...)
 //thisArg:在 fun 函数运行时指定的 this 值
 //arg1,arg2:传递的其他参数
 //返回由指定的 this 值和初始化参数改造的原函数拷贝
 //因此当我们只是想改变 this 指向,并且不想调用这个函数的时候,可以使用 bind

**bind()**方法创建一个新的函数, 可以绑定新的函数的this指向

var name = '张三';
function Fn(){
    this.age = 1;
    
    console.log(this.name + this.age);
}
Fn();			// 张三 1
// 返回值:新的函数
// 参数:新函数的this指向,当绑定了新函数的this指向后,无论使用何种调用模式,this都不会改变。
let obj = {
    name:'小强',
}
const newFn = Fn.bind(obj);
newFn();		// 小强 1

6. call apply bind 总结

1.相同点:

都可以改变函数内部的this指向.

2.区别点:

1.call 和 apply 会调用函数, 并且改变函数内部this指向.

2.call 和 apply 传递的参数不一样, call 传递参数 aru1, aru2..形式 apply 必须数组形式[arg]

3.bind 不会调用函数, 可以改变函数内部this指向.

3.主要应用场景:

1.call 经常做继承

2.apply 经常跟数组有关系. 比如借助于数学对象实现数组最大值最小值

3.bind 不调用函数,但是还想改变this指向. 比如改变定时器内部的this指向

7.修改this指向

  <script>
      /* 
      1 bind call apply 都可以修改this的指向  

      2 分别演示 修改this指向
        call  obj.skill.call(person)
        
        apply obj.skill.apply(person)
        
        bind  
        const func = obj.skill.bind(person);
        func();
       */
      const obj = {
        name: '老王',
        skill() {
          console.log(this.name + '  翻墙');
        },
      };
      const person = {
        name: '大郎',
      };
      // call 方式来修改this的指向
      // obj.skill.call(person);// 大郎 借用老王的方法

      // apply 方式来修改this的指向
      // obj.skill.apply(person); // 大郎 借用老王的方法

      // bind 方式来修改this指向
      // bind 不会直接调用skill函数 而是 返回一个新的函数
      // 需要我们主动的调用新的函数- 调用skill()
      const func = obj.skill.bind(person);
      func();
    </script>

8.传递参数

  <script>
      const obj = {
        name: '老王',
        skill(a, b) {
          console.log(this.name + ' ' + a + ' ' + b);
        },
      };
      const person = {
        name: '大郎',
      };
      //call
      // obj.skill.call(person,1,2);// 传参

      //apply
      // obj.skill.apply(person, [1, 2]); // 数组

      // 在早些时候 我们是这样来计算 数组最大值和最小值
      //太low了 不优雅
      // const max = Math.max(1, 2, 3, 4);
      // console.log(max);
      // const arr=[1,2,3,4];
      // 借用 Math.max方法 目的不是修改this指向
      // 而是 计算数组最大值
      // const max = Math.max.apply(null,arr);
      // 现在用这种写法  优雅
      // const arr=[1,2,3,4];
      // console.log(Math.max(...arr));

      //bind
      const func=obj.skill.bind(person);
      func(1,2);
      /* 
      1 bind call  apply 都可以实现修改this指向
      2 代码写法上有区别
        1 obj.skill.call(person);
        2 obj.skill.apply(person);
        3 const func = obj.skill.bind(person);
          func();
      3 传递参数
        1 obj.skill.call(person,参数1,参数2)
        2 obj.skill.apply(person,[参数1,参数2])
        3 const func = obj.skill.bind(person);
          func(参数1,参数2)
      
       */
    </script>

3.this指向

调用方式this指向
普通函数调用window
构造函数调用实例对象原型对象里面的方法也指向实例对象
对象方法调用该方法所属对象
事件绑定方法绑定事件对象
定时器函数window
立即执行函数window

这些 this 的指向,是当我们调用函数的时候确定的。 调用方式的不同决定了this 的指向不同 一般指向我们的调用者.

14.综合案列

0.箭头函数

  <button>潮吧点击</button>
    <script>
      //以前写函数
      function fun1(num1) {
        return num1 + 100;
      }
      //箭头函数的时候   函数=(参数)=>{执行业务}
      // const fun2 = (num1) => num1 +100

      //1.定义一个箭头函数 没有形参 没有返回值  2行实际业务
      const fun3 = () => {
        const num3 = 100 + 100;
        console.log(num3);
      };

      //2.只有一个参 没有返回值 业务只有一行代码
      // const fun4 = (num)=> console.log('潮吧');
      const fun4 = (num) => console.log("潮吧");

      //3.两个或多个参数(括号不能省略) 没有返回值 业务只有一行代码
      const fun5 = (a, b) => console.log(a + b);

      //4.没有形参 有返回值 业务两行代码\
      const fun6 = () => {
        let a = 100 + 100;
        return a;
      };
      //5.没有形参 有返回值 业务一行代码
      //   const fun7 = ()=> {
      //       return 100+100
      //   }
      //没有形参 有返回值 业务一行代码  等价上述写法
      const fun7 = () => 100 + 100;

      //实际应用
      const button = document.querySelector("button");
      button.addEventListener("click", () => {
        console.log("潮吧");
      });
    </script>

1.forEach

    <script>
        //forEach
        // forEach()
      function forEach() {
        const arr = ["大", "潮", "吧"];
        //forEach() 数组每个元素都执行一次回调函数  = 类似以前的 for   forEach 高级函数(可以接收一个形参-函数)
        //for循环可以通过 break来打断   forEach不能通过break打断

        //分别打印

        arr.forEach(function (value, index) {
          console.log(`值是${value} 下标是${index}`);
        });
        arr.forEach((value, index) => {
          console.log(`值是${value} 下标是${index}`);
        });
      }
    </script>

2.map

    <script>
      //map
      map();
      function map() {
        // map 根据原来的数组 来返回新的数组
        // 也会循环数组 在循环的回调函数中可以返回新的数据 组装成新的数组
        const arr = ["a", "b", "c"];
        // 可以返回 ["我的字母是a","我的字母是b","我的字母是c"]

        const newArr = arr.map((value) => `我的字母是` + value);
        console.log(newArr); //['我的字母是a', '我的字母是b', '我的字母是c']

        const list = [10, 11, 12];
        const newList = list.map((value) => value + 1);
        console.log(newList); //[11, 12, 13]

        const objectArr = [{ name: "悟空" }, { name: "潮吧" }];
        const newObjectArr = objectArr.map((value) => {
          value.color = "red";
          return value;
        });
        console.log(newObjectArr); //[{name:'悟空',color:'red'},{name:'潮吧',color:'red'}]

        const texts = ["刘德华", "郭德纲", "林志颖"];
        // // 返回  [<div>刘德华</div>,<div>郭德纲</div>,<div>林志颖</div>]
        const newTexts = texts.map((value) => `<div>${value}</div>`);
        // console.log(newTexts)
        // // [<div>刘德华</div>,<div>郭德纲</div>,<div>林志颖</div>]
        // // 把它转成字符串
        const html = newTexts.join(""); // <div>刘德华</div><div>郭德纲</div><div>林志颖</div>
        // console.log(html);
        document.body.innerHTML = html;

        // 后面再说
        // const renderHTML=value=>`<div>${value}</div>`
        // document.body.innerHTML=["刘德华","郭德纲","林志颖"].map(renderHTML).join("");
      }
    </script>

3.map捐赠管理

<!-- 
  1 快速实现静态布局
  2 使用提供好的数组数据 来遍历 渲染页面
 -->
<!DOCTYPE html>

<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0,maximum-scale=1,minimum-scale=1,user-scalable=no"
    />
    <title>01-捐赠管理.html</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
      h3 {
        margin: 50px;
        text-align: center;
      }
      table {
        width: 800px;
        margin: 0 auto;
        border-collapse: collapse;
        text-align: center;
      }
      thead {
        background-color: #337ab7;
        color: #fff;
      }
      td,
      th {
        padding: 10px 50px;
        /* 设置文字不换行 */
        white-space: nowrap;
      }
    </style>
  </head>
  <body>
    <h3>捐赠管理</h3>
    <table border="1">
      <thead>
        <tr>
          <th>序号</th>
          <th>捐赠人</th>
          <th>收捐单位</th>
          <th>金额</th>
          <th>收捐日期</th>
          <th>操作</th>
        </tr>
      </thead>
      <tbody></tbody>
    </table>

    <script>
      // 获取tbody
      const tbody = document.querySelector("tbody");
      // 定义数据
      const arr = [
        // id:数据编号  person:捐赠人姓名   unit:捐赠单位名称  money:捐赠金额  date:捐赠日期
        {
          id: 1,
          person: "刘德化",
          unit: "壹基金",
          money: 1000,
          date: "2021-10-5",
        },
        {
          id: 2,
          person: "周杰伦",
          unit: "自然之友",
          money: 1000,
          date: "2021-01-15",
        },
        {
          id: 3,
          person: "李连杰",
          unit: "嫣然基金",
          money: 1000,
          date: "2021-06-7",
        },
      ];

      // 根据数组来渲染页面
      const newArr = arr.map(
        (value) =>
          `
         <tr>
            <td>${value.id}</td>
            <td>${value.person}</td>
            <td>${value.unit}</td>
            <td>${value.money}</td>
            <td>${value.date}</td>
            <td>
     <a href="#" class="del">删</a>
     <a href="#" class="update">改</a>
             </td>
        </tr>
         `
      );
      const html = newArr.join("");
      tbody.innerHTML = html;
    </script>
  </body>
</html>

4.every

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0,maximum-scale=1,minimum-scale=1,user-scalable=no"
    />
    <title>数组常见方法every</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
    </style>
  </head>
  <body>
    <script>
      every();
      function every() {
        // 会返回 true或者false
        // 如果数组中每一个元素都复合条件,every返回true

        // const arr = [1, 6, 3, 4];
        // // 判断数组中每一个元素,是不是都小于 5 如果是  返回true

        // // every当中的函数,要求每一个都return 了true 最后 every的返回值才是true
        // const result = arr.every((value) => {
        //   if (value < 5) {
        //     return true;
        //   } else {
        //     return false;
        //   }
        // });
        // console.log(result);

        // 有一个数组,存放每一个人 关于肺炎的检查结果
        const arr = [true, true, true, true, true]; // true就表示安全,false 中招
        // 我想要判断一下  这个人群安不安全  (只要有一个为false,不安全) 要求每一个都为true才安全

        // const result = arr.every((value) => {
        //   if (value === true) {
        //     return true;
        //   } else {
        //     return false;
        //   }
        // });
        // console.log(result);

        // 
      }

      //every 如果是空数组  调用every 直接返回true
      let arr = []
      const arr1 = arr.every(value => console.log(66666))
      console.log(arr1);



      /* 
      every
      与&&逻辑与意思相近
      会返回 true 或者false
      要求数组中每一个元素都符号条件every得到true
      如果是空数组  调用every 直接返回true
      */
    </script>
  </body>
</html>

5.every全选全不选

<!DOCTYPE html>

<html>
  <head lang="en">
    <meta charset="UTF-8" />
    <title>08-全选和不全选-every</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }

      table {
        border-collapse: collapse;
        border-spacing: 0;
        border: 1px solid #c0c0c0;
        width: 500px;
        margin: 100px auto;
        text-align: center;
      }

      th {
        background-color: #09c;
        font: bold 16px "微软雅黑";
        color: #fff;
        height: 24px;
      }

      td {
        border: 1px solid #d0d0d0;
        color: #404060;
        padding: 10px;
      }

      .allCheck {
        width: 80px;
      }
    </style>
  </head>

  <body>
    <table>
      <tr>
        <th class="allCheck">
          <input type="checkbox" name="" id="checkAll" />
          <span class="all">全选</span>
        </th>
        <th>商品</th>
        <th>商家</th>
        <th>价格</th>
      </tr>
      <tr>
        <td>
          <input type="checkbox" name="check" class="ck" />
        </td>
        <td>小米手机</td>
        <td>小米</td>
        <td>¥1999</td>
      </tr>
      <tr>
        <td>
          <input type="checkbox" name="check" class="ck" />
        </td>
        <td>小米净水器</td>
        <td>小米</td>
        <td>¥4999</td>
      </tr>
      <tr>
        <td>
          <input type="checkbox" name="check" class="ck" />
        </td>
        <td>小米电视</td>
        <td>小米</td>
        <td>¥5999</td>
      </tr>
    </table>
  </body>
  <script>
    let checkAll = document.querySelector("#checkAll");

    let checkboxList = document.querySelectorAll(".ck"); // checkboxList 现在是一个伪数组
    checkboxList = [...checkboxList]; // OK

    // 商品全选点击 功能
    checkAll.addEventListener("click", function () {
      for (let index = 0; index < checkboxList.length; index++) {
        checkboxList[index].checked = checkAll.checked;
      }
    });

    // // 给每一个商品绑定点击事件
    for (let index = 0; index < checkboxList.length; index++) {
      checkboxList[index].addEventListener("click", function () {
        // 判断是否达到了全选 条件
        // 判断每一个小小的复选框的选中状态 如果都是true,那么就全选

        //复杂写法
        // let checked = checkboxList.every((value) => {
        //   if (value.checked === true) {
        //     return true;
        //   } else {
        //     return false;
        //   }
        // });
        //简写
        let checked = checkboxList.every((value)=>value.checked)
        // 设置全选按钮即可
        checkAll.checked = checked;
      });
    }
  </script>
</html>
<!-- 
  两个知识
  1 伪数组转真正的数组  let newArr=[...伪数组] ;
  2 every 要求数组的每一个元素都符号要求, every才返回true 
    使用场景, 商品全选 => 每一个小商品都选中, 全选才勾选!! 

  4 如果 数组 有every方法, list.every 看是不是undefined 
    不是的话 表示数组有 every方法 list.every() 调用
    是  表示数组没有 every方法,不用list.every() 
  
 -->

6.some

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0,maximum-scale=1,minimum-scale=1,user-scalable=no"
    />
    <title>数组常见方法some</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
    </style>
  </head>
  <body>
    <script>
      some();
      function some() {
        // 检测数组,其中只要有一个元素符合条件,some返回true (every要求每一个都符合)
        const arr = [1, 3, 4, 6, 2];
        // 这个数组里面有没有元素大于6的
        const result = arr.some((value) => value > 6);
        console.log(result);

        /* 
        every 和 some 一起记
        every 要求全部都符合
        some 最少有一个符合即可 
         */
      }
    </script>
  </body>
</html>

7.filter

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
    </style>
  </head>
  <body>
    <script>
      filter()
      function filter() {
          //过滤  过滤出满足条件的数据 =>新数组
          const arr = [1,2,3,4,5,6,7,8,9,10]
          //返回奇数
          const newArr = arr.filter((value)=>{
              if (value%2 !==0) {
                  return true
              }else{
                  return false
              }
          })
          //返回偶数
          const newArr2 = arr.filter((value) => value%2===0)
          console.log(newArr);
          console.log(newArr2);
      }
    </script>
  </body>
</html>

8.待办filter

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0,maximum-scale=1,minimum-scale=1,user-scalable=no"
    />
    <title>11-待办列表.html</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
      body {
        background-color: #ccc;
      }
      ul {
        list-style: none;
      }
      li {
        padding: 20px;
        text-align: left;
        font-size: 30px;
        border-bottom: 1px dashed #ccc;
        display: flex;
        justify-content: space-between;
        align-items: center;
      }
      li input {
        margin-right: 10px;
      }
      li button {
        display: none;
        padding: 5px;
      }
      li:hover button {
        display: inline-block;
        cursor: pointer;
      }
      .chk:checked + span {
        text-decoration: line-through;
      }

      h1 {
        margin-bottom: 10px;
      }

      .box {
        background-color: #fff;
        width: 60vw;
        padding: 20px 20px 0;
        margin: 50px auto;
      }
      .box .tool input {
        width: 100%;
        height: 50px;
        text-indent: 20px;
        font-size: 20px;
        font-style: italic;
        color: #666;
        font-weight: 700;
      }
      section {
        height: 50px;
        display: flex;
        justify-content: space-between;
        align-items: center;
      }
      a {
        text-decoration-color: #666;
        color: inherit;
      }
    </style>
  </head>
  <body>
    <div id="app" data-v-app="">
      <div class="box">
        <h1>待办列表</h1>
        <div class="tool">
          <input autofocus="" type="text" placeholder="请输入代办事项" />
        </div>
        <ul></ul>
        <section>
          <span>1 未完成</span><a href="#">清理 <b>0</b> 已完成</a>
        </section>
      </div>
    </div>
    <script>
      const input = document.querySelector('.tool input');
      const ul = document.querySelector('ul');
      const unFinishSpan = document.querySelector('section span');
      const finishA = document.querySelector('a b');
      const a = document.querySelector('a');
      let arr = [];
      const strArr = localStorage.getItem('todo');
      if (strArr) {
        arr = JSON.parse(strArr);
      }

      render();

      input.addEventListener('keydown', function (event) {
        if (event.key === 'Enter') {
          // input.value
          arr.push({
            text: input.value,
            checked: false,
          });
          render();
          input.value = '';
        }
      });

      ul.addEventListener('click', function (event) {
        const index = event.target.dataset.index;

        if (event.target.className === 'chk') {
          arr[index].checked = !arr[index].checked;
          render();
        } else if (event.target.nodeName === 'BUTTON') {
          arr.splice(index, 1);
          render();
        }
      });

      a.addEventListener('click', function () {
        let newArr = [];
        for (let index = 0; index < arr.length; index++) {
          if (!arr[index].checked) {
            newArr.push(arr[index]);
          }
        }

        arr = newArr;
        render();
      });

      // 函数负责把数组数据渲染在页面上
      function render() {
        let html = ``;
        for (let index = 0; index < arr.length; index++) {
          const renderChecked = arr[index].checked ? 'checked' : '';
          html += `
           <li>
             <div><input data-index="${index}" type="checkbox" class="chk"  ${renderChecked}  /><span class="false">${arr[index].text}</span></div>
             <button data-index="${index}">X</button>
           </li>
           `;
        }
        ul.innerHTML = html;

        localStorage.setItem('todo', JSON.stringify(arr));

        statistics();
      }
      function statistics() {
            // 未完成
        unFinishSpan.innerText = arr.filter(value=>!value.checked).length + ' 未完成';
        // 已经完成
        finishA.innerText = arr.filter(value=>value.checked).length;
      }
    </script>
  </body>
</html>

9.call继承

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0,maximum-scale=1,minimum-scale=1,user-scalable=no"
    />
    <title>12-继承的案例</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
    </style>
  </head>
  <body>
    <script>
      /* 
      封装 代码 实现以下的功能
      1 父亲   Element
        1 属性 dom   this.dom
        2 行为 实例.append(父元素标签的选择器)
      2 儿子1  ElementDouble  div
        1 继承父亲 Element  
          属性 dom
          行为 append
      3 儿子2  ElementSingle  img 
          属性 dom
          行为 append

      4 当执行以下代码时 出现对应效果
        1 const divModel = new ElementDouble("div","div内的文字")
        2 divModel.append("body") ;  body标签可以出现 一个 div 

        1 const imgModel = new ElementSingle("img","图片地址")
        2 imgModel.append("body");  body标签可以出现 一个图片 
       */

      //  父亲
      function Element(tagName) {
        const dom = document.createElement(tagName);
        this.dom = dom;
      }
      //  父亲
      Element.prototype.append = function (parentSelector) {
        document.querySelector(parentSelector).appendChild(this.dom);
      };

      // 儿子1
      function ElementDouble(tagName, content) {
        Element.call(this, tagName); // 继承 - 父亲的属性
        this.dom.innerText = content;
      }
      // 去继承父亲的行为
      ElementDouble.prototype.append = Element.prototype.append;

      // 儿子2
      function ElementSingle(tagName, src) {
        Element.call(this, tagName);
        this.dom.src = src;
      }
      ElementSingle.prototype.append = Element.prototype.append;

      const divModel = new ElementDouble('div', '这个是div');
      divModel.append('body');

      const imgModel = new ElementSingle('img', './images/1.jpg');
      imgModel.append('div');

      // 如果代码重复实现了 很有可能就是我们要封装的时候
      // 以前的封装 仅仅是封装一个小小的函数而已
      // 现在的封装, 面相对象的思维来封装
      // 封装 属性  父亲
      // 封装 方法  父亲

      // 两个儿子的代码 有重复部分
      // 创建标签重复 this.dom=dom  append方法也重复

      // 儿子1 有要什么功能
      // 先复用父亲的属性和方法

    </script>
  </body>
</html>

10.对象简写

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
    </style>
  </head>
  <body>
    <script>
      // 简写 如果变量的名字和属性的名字 一致的话,对象可以简写
      // const obj = {
      //   // 属性名 和 属性值
      //   username: 123,
      // };
      let username = "彭炎";
      let age = 18;
      let color = "green";

      let obj = {
        username,
        age,
        color,
      };
      console.log(obj);

      const say = function () {};

      function show() {}

      const person = {
        show: function () {
          console.log(show);
        }, //常规写法

        //es6 关于方法简写
        say() {
          console.log(say);
        }, // es6 关于 方法简写
      };
      person.show();
      person.say();
      //很常用
      /* 
      小结:
      1 变量名如果和你的对象的属性名一致 可以简写
         let username='悟空'
         const obj = { username }

      2 对象的方法 简写
        const obj ={
          say(){  // 简写的方法

          }
        }
       */
    </script>
  </body>
</html>

11.解构

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
    </style>
  </head>
  <body>
    <div data-index="100" data-num="200"></div>
    <script>
      //   dataset = { index: 100  num:'200' };
      const dataset = document.querySelector("div").dataset;
      const { index, num } = document.querySelector("div").dataset;
      console.log(dataset);
      //   const {index}=dataset;
      //   const {num}=dataset;
      console.log(index);
      console.log(num);

      const arr = ["彭炎", "肖耀", "高航", "潮吧"];
      // 希望 声明四个变量 a b c d
      // 下次我看到谁这么写代码的, 手都打肿!!!
      // 太low!! 低级  我自己 无地自容  想不开你
      //   const a = arr[0];
      //   const b = arr[1];
      //   const c = arr[2];
      //   const d = arr[3];

      //数组解构  优雅写法  高端
      const [a, b, c, d] = arr; // const [a,b,c,d] = ["彭炎", "肖耀", "高航", "潮吧"];
      console.log(a, b, c, d);

      let data = { username: "彭炎", age: "18", height: "180" };

      //low 低端写法 不优雅
      //   const username = data.username
      //   const age = data.age
      //   const height = data.height

      //解构对象 优雅写法 高端
      const { username, age, height } = data;
      console.log(username, age, height);

      //解构+默认值
      // const arr = [1,100];
      // const [a,b ] = arr; a = 1 b=undefined
      // const [a, b = 2] = arr;
      // console.log(a, b); // a =1  b = 2

      // b = 2 默认值 (你没有,就使用我,你有,使用你的)
      // const [a, b = 2] = arr;
      // console.log(a,b);
      // const obj = {
      //     username: 100,
      //     height: 500,
      // };
      // const { username, height = 200 } = obj;
      // console.log(username, height);

      /* 
      小结
      1 解构 对象 和 数组上
        对象 
        const  { username,height } = {username:"悟空",height:200}
        数组
        const [a,b]=[100,200];

     2 解构 + 默认值
       如果 右边的对象中没有height 属性 那么 height变量 =  1000 
       const  { username,height=1000 } = {username:"悟空",height:200} 
        
      //  c 在右边找不到对应的数据 c 就使用默认值 300 
        const [a,b,c=300]=[100,200];
       */
    </script>
  </body>
</html>

12.set数组去重

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0,maximum-scale=1,minimum-scale=1,user-scalable=no"
    />
    <title>02-set去重案例</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
    </style>
  </head>
  <body>
    <input type="text" />
    <ul></ul>
    <script>
      const input = document.querySelector("input");
      const ul = document.querySelector("ul");
      let arr = ["a", "b"];
      render();
      input.addEventListener("keydown", function (event) {
        if (event.key === "Enter") {
          const set = new Set(arr);
          set.add(this.value);
          arr = [...set];
          console.log(arr);
          render();
        }
      });

      function render() {
        const html = arr.map((value) => `<li>${value}</li>`).join("");
        ul.innerHTML = html;
      }
    </script>
  </body>
</html>

13.bind - call-apply修改this指向

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0,maximum-scale=1,minimum-scale=1,user-scalable=no"
    />
    <title>04-bind-call-apply修改this指向.html</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
    </style>
  </head>
  <body>
    <script>
      /* 
      1 bind call apply 都可以修改this的指向  

      2 分别演示 修改this指向
        call  obj.skill.call(person)
        apply obj.skill.apply(person)
        bind  
         const func = obj.skill.bind(person);
         func(); 

      3 传递参数的时候不同写法上 (下一节课再来讲解)

       */

      const obj = {
        name: '老王',
        skill() {
          console.log(this.name + '  翻墙');
        },
      };

      const person = {
        name: '大郎',
      };

      // call 方式来修改this的指向
      // obj.skill.call(person);// 大郎 借用老王的方法

      // apply 方式来修改this的指向
      // obj.skill.apply(person); // 大郎 借用老王的方法

      // bind 方式来修改this指向
      // bind 不会直接调用skill函数 而是 返回一个新的函数
      // 需要我们主动的调用新的函数- 调用skill()
      const func = obj.skill.bind(person);
      func();
    </script>
  </body>
</html>

14.bind - call-apply传递参数

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0,maximum-scale=1,minimum-scale=1,user-scalable=no"
    />
    <title>04-bind-call-apply修改this指向.html</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
    </style>
  </head>
  <body>
    <script>
      const obj = {
        name: '老王',
        skill(a, b) {
          console.log(this.name + ' ' + a + ' ' + b);
        },
      };
      const person = {
        name: '大郎',
      };
      //call
      // obj.skill.call(person,1,2);// 传参
      //person
      // obj.skill.apply(person, [1, 2]); // 数组

      // 在早些时候 我们是这样来计算 数组最大值和最小值
      //太low了 不优雅
      // const max = Math.max(1, 2, 3, 4);
      // console.log(max);
      // const arr=[1,2,3,4];
      // 借用 Math.max方法 目的不是修改this指向
      // 而是 计算数组最大值
      // const max = Math.max.apply(null,arr);
      // 现在用这种写法  优雅
      // const arr=[1,2,3,4];
      // console.log(Math.max(...arr));

      //bind
      const func=obj.skill.bind(person);
      func(1,2);


      /* 
      1 bind call  apply 都可以实现修改this指向
      2 代码写法上有区别
        1 obj.skill.call(person);
        2 obj.skill.apply(person);
        3 const func = obj.skill.bind(person);
          func();
      3 传递参数
        1 obj.skill.call(person,参数1,参数2)
        2 obj.skill.apply(person,[参数1,参数2])
        3 const func = obj.skill.bind(person);
          func(参数1,参数2)
      
       */
    </script>
  </body>
</html>

15.es6面向对象

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0,maximum-scale=1,minimum-scale=1,user-scalable=no"
    />
    <title>08-es6的面向对象.html</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
      body {
        background-color: aqua;
      }
    </style>
  </head>
  <body>
    <script>
      // es6 面向对象 引出新的语法 类 class
      class Person {
        // 方法 constructor 在es6 构造函数
        // constructor 会在 new Person的时候触发
        constructor(name) {
          // console.log("开始创建对象啦");
          this.name = name;
        }
        // 直接写行为
        say() {
          console.log('say方法被调用啦 ' + this.name);
        }
      }
      // 一样new一个对象
      const p1 = new Person('悟空1');
      const p2 = new Person('悟空2');
      // console.log(p1);
      // p1.say();
      console.log(p1.say === p2.say);
      // 属性
      // 行为

      class SuperMan {
        constructor() {
          // 对象有什么属性都写在 这里
          this.name = '超人';
          this.color = 'blue';
        }
        // 行为
        say() {
          console.log('说话');
        }
        fly() {
          console.log('会飞');
        }
        sing() {
          console.log('唱歌');
        }
      }
    </script>
  </body>
</html>

16.继承案列

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0,maximum-scale=1,minimum-scale=1,user-scalable=no"
    />
    <title>12-继承的案例</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
    </style>
  </head>
  <body>
    <script>
      /* 
    使用es6的class 实现以下功能

    1 父类 Element 
      1 属性 this.dom
      2 方法 append
    
    2 定义两个子类 都要继承父亲的属性和方法
      1 ElementDouble
      2 ElementSingle 

    3 然后 通过编写代码 把以下程序运行起来
      
     */

      class Element {
        constructor(name) {
          const dom = document.createElement(name);
          this.dom = dom;
        }
        append(parentSelector) {
          document.querySelector(parentSelector).appendChild(this.dom);
        }
      }
      class ElementDouble extends Element {
        constructor(name, text) {
          super(name);
          this.dom.innerText = text;
        }
      }
      class ElementSingle extends Element {
        constructor(name, src) {
          super(name);
          this.dom.src = src;
        }
      }

      const divModel = new ElementDouble("div", "这个是div");
      divModel.append("body");

      const imgModel = new ElementSingle("img", "./images/1.jpg");
      imgModel.append("div");
    </script>
  </body>
</html>