JavaScript高级(三)

80 阅读4分钟

JavaScript高级(三)

一、set

1.set对象 是es6 才推出
2.解释:永远不会有重复元素的对象,可以理解为不重复的数组
3.set是一个对象,不是一个数组
4.实现 使用set转成数组的时候,需要考虑 (set对象转-数组 数组 - 转成对象)

①数组 - 转成对象 new Set(beforeArr)

②set对象转-数组 const arr=[...set];

5.关键代码

①关键代码

 //  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);


1650821500943.png

②关键代码

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

1650821522698.png

6.案例-set去重

①关键代码
function render() {
  const html = arr.map((value) => `<li>${value}</li>`).join("");
  ul.innerHTML = html;
}


input.addEventListener("keydown", function (event) {
        if (event.key === "Enter") {

          // 数组转成set对象
          let set = new Set(arr);

          // 给set添加数据的时候 使用 add方法
          set.add(this.value);

          // set对象转-数组 
          arr = [...set];

          //   arr.push(this.value);
          render();
        }
      });

②完整代码
<!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") {
          // 去重处理
          let set = new Set(arr);
          set.add(this.value);
          arr = [...set];
          //   arr.push(this.value);
          render();
        }
      });

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

二、this指向

1.func1(); // this = window 定义在全局的函数 理解为window的一个属性

2.window.func1(); // this = window 定义在全局的函数 理解为window的一个属性

      // const obj = {
      //   name: "悟空",
      //   say() {
      //     console.log(this);
      //   },
      // };
      // obj.say(); // this = ?   // obj

      function Person() {
        this.name = "悟空";
      }
      Person.prototype.say = function () {
        console.log(this);
      };
      const p1 = new Person();
      p1.say(); // this = p1
      console.log(p1);

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

(1)bind call apply 都可以修改this的指向

①call

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

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


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

②apply

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

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


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

③bind

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

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


// bind 方式来修改this指向
// bind 不会直接调用skill函数 而是 返回一个新的函数
// 需要我们主动的调用新的函数- 调用skill()
const func = obj.skill.bind(person);
func();
(2)bind call apply 都可以修改this的指向-总结

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)传递参数的时候不同写法上 (下一节课再来讲解)

4.bind-call-apply传递参数

(1)传递参数1

const obj = {
        name: "老王",
        skill(a, b) {
          console.log(this.name + " " + a + " " + b);
        },
      };

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

      // obj.skill.call(person, 1, 2); // 传参
      // obj.skill.apply(person, [1, 2]); // 数组
      const func = obj.skill.bind(person);
      func(1, 2); //

(2)传递参数2

// const max = Math.max(1, 2, 3, 4);
// console.log(max);
// const arr = [1, 2, 3, 4];
// 借用 Math.max方法 目的不是修改this指向
// 而是 计算数组最大值


const arr = [1, 2, 3, 4];
console.log(Math.max(...arr));

(3)传递参数3

①obj.skill.call(person,参数1,参数2)

②obj.skill.apply(person,[参数1,参数2])

③const func = obj.skill.bind(person);

func(参数1,参数2)

5.箭头函数-this指向(在js中,顶级的对象 就是window)

(1)在js中,顶级的对象 就是window1
// 定义一个函数 形式是箭头函数的
// const func = () => {
//   console.log(this);
// };
(2)在js中,顶级的对象 就是window2
// const obj = {
//   name: '悟空',
//   say: () => {
//     console.log(this);
//   },
// };
obj.say(); // window
(3)在js中,顶级的对象 就是window3
// const obj1 = {
//   name: '悟空1',
//   obj2: {
//     name: '悟空2',
//     say: () => {
//       console.log(this);
//     },
//   },
// };
// obj1.obj2.say();
(4)在js中,顶级的对象 就是window4
<!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>06-箭头函数-this指向.html</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
    </style>
  </head>
  <body>
    <button>点击我 我就改变颜色</button>
    <script>
      const button = document.querySelector("button");
      // button.addEventListener('click', function () {
      //   console.log(this); // this = button
      // });
      button.addEventListener("click", () => {
        console.log(this); // this = 谁
      });

      /* 
      大部分情况,对于普通函数,this 等于这个函数的调用者
        (例外 - bind、call、apply)
      大部分情况,对于箭头函数,this 等于window
        箭头函数和this 一起用的话,要慎用!! 
        例外(面向对象用法,)
       */
    </script>
  </body>
</html>
(5)总结

① 大部分情况,对于普通函数,this 等于这个函数的调用者(例外 - bind、call、apply)

②大部分情况,对于箭头函数,this 等于window;箭头函数和this 一起用的话,要慎用!!

​ 例外(面向对象用法,)

三、es5和es6

1.es6的class 的出现 基本上可以替代了es5的构造函数和原型,使之代码结构上更加简洁。
2.es5和es6面向对象区别

(1)es5面向对象

<script>
      // es5 写一个 构造函数 首字母是大写
      // 当我们new 一个 对象的时候 Person 代码会被执行
      function Person(name) {
        // console.log("开始创建对象啦");
        this.name = name;
      }

      // 定义行为 引出 原型
      Person.prototype.say = function () {
        console.log("say方法被调用啦 " + this.name);
      };

      // new 一个对象
      const p1 = new Person("悟空1");
      const p2 = new Person("悟空2");
      // console.log(p1);

      console.log(p1.say === p2.say);
    </script>

(2)es6面向对象

<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("唱歌");
        }
      }

      // es6 继承写法 稍后会讲解
    </script>
3.es5和es6继承

(1)es5继承

 <script>
      function Person(name) {
        this.name = name;
      }
      Person.prototype.say = function () {
        console.log("say方法我调用啦 " + this.name);
      };
      Person.prototype.fly = function () {
        console.log("父亲 起飞");
      };

      function Student(name, color) {
        Person.call(this, name); // 继承属性
        this.color = color;
      }
      Student.prototype.say = Person.prototype.say; // 继承方法
      Student.prototype.fly = Person.prototype.fly; // 继承方法

      const s1 = new Student("学生", "red");
      console.log(s1);
      s1.say();
    </script>

(2)es6继承

    <script>
      function Person(name) {
        this.name = name;
      }
      Person.prototype.say = function () {
        console.log("say方法我调用啦 " + this.name);
      };
      Person.prototype.fly = function () {
        console.log("父亲 起飞");
      };

      function Student(name, color) {
        Person.call(this, name); // 继承属性
        this.color = color;
      }
      Student.prototype.say = Person.prototype.say; // 继承方法
      Student.prototype.fly = Person.prototype.fly; // 继承方法

      const s1 = new Student("学生", "red");
      console.log(s1);
      s1.say();
    </script>
4.es6-案例
(1)使用es6的class 实现以下功能

①父类 Element

class Element {}

②属性 this.dom

this.dom = dom;

③方法 append

append(parentSelector) {}
(2)定义两个子类 都要继承父亲的属性和方法

①ElementDouble

class ElementDouble extends Element {}

②ElementSingle

class ElementSingle extends Element {}

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

    <script>
      //  父亲
      class Element {
        constructor(tagName) {
          const dom = document.createElement(tagName);
          this.dom = dom;
        }
        append(parentSelector) {
          document.querySelector(parentSelector).appendChild(this.dom);
        }
      }
      // 继承父亲
      class ElementDouble extends Element {
        constructor(tagName, content) {
          super(tagName);
          this.dom.innerText = content;
        }
      }
      class ElementSingle extends Element {
        constructor(tagName, src) {
          super(tagName);
          this.dom.src = src;
        }
      }

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

      const imgModel = new ElementSingle("img", "./images/01.jpg");
      imgModel.append("div");
    </script>
5.es6-class和箭头函数
 <script>
      // es6 属性的定义 写法有两种
      // 1 直接在构造函数内  constructor  this.name=name
      // 2 可以写在 大括号内

      // 3 方法 三种写法

      class Person {
        // color = 'yellow';
        // height = 180;
        // weight = 200;
        constructor(name) {
          this.name = name;
          // this.color = 'yellow';
          // this.height = 180;
          // this.weight = 200;
        }
        // 写法一
        say() {
          console.log("say 方法被调用了 " + this.name);
        }
        // 写法二
        // say = function () {
        //   console.log("say 方法被调用了 " + this.name);
        // };
        // 写法三
        // say = () => {
        //   // 箭头函数中的this , 绝大部分指向  window
        //   // 例外 用在 es6的class 充当 方法  this 指向 p1 实例
        //   console.log("say 方法被调用了 " + this.name);
        // };
      }
      const p1 = new Person("悟空");
      p1.say();
      // console.log(p1);
    </script>
6.es6-三种定义方法的写法-最优
<script>
      class Person {
        // 性能最好 推荐方式!!
        say1() {
          console.log("say1");
        }
        say2 = function () {
          console.log("say2");
        };
        say3 = () => {
          console.log("say3");
        };
      }
      const p1 = new Person();
      const p2 = new Person();
      p1.say1();
      p1.say2();
      p1.say3();
      console.log(p1.say1 === p2.say1); // true p1 和 p2 say1方法是同一个-节省内存
      console.log(p1.say2 === p2.say2); // false  p1 和 p2 say2方法不是同一个-性能不好
      console.log(p1.say3 === p2.say3); // false  p1 和 p2 say3方法不是同一个-性能不好
</script>


7.es5-原型链
    <script>
      // 1 创建数组的方式 有两种
      // const arr = ['a', 'b', 'c']; // 字面量 常用-直接和简单的写法

      // 2 数组 也是可以被new
      // const arr = new Array('a', 'b', 'c');
      // console.log(arr);

      function Person() {}
      const p1 = new Person();
      // 在 Person的原型上添加一个方法
      Person.prototype.show = function () {
        console.log("自己添加的方法");
      };
      // p1.show();

      // 给js内置的Array数据 原型也添加一个方法试试
      Array.prototype.show = function () {
        console.log("自定义的数组方法");
      };

      const arr = new Array("a", "b", "c");
      // const arr = ['a', 'b', 'c'];
      // arr.show();

      // 利用原型对象的方式,在任意的构造函数上添加想要的行为
      // 任意的构造函数 包括 自己定义的构造函数
      // 也包括 js中 -内置就有的构造函数 Array

      // arr.forEach
      // arr.push()
      // arr.map()
      // arr.filter

      // 对象的创建也分两种清空
      // const ojb={};// 字面量 常用 直接 简单
      const obj = new Object(); // 利用构造函数的方式来创建对象
      Object.prototype.show = function () {
        console.log("对象 自己的show方法");
      };
      obj.show();
    </script>
    <script>
      /* 
      原型链
      一个对象 它可以通过 prototype 来 找到被它继承父亲的方法
      如果一个对象从底层触发 可以通过 prototype 一层一层往上找到 继承的关系 =  原型链 

      // 作用
      1 如果我需要给 某个数据 (字符串、数组、对象) 统一添加一个方法 ,可以直接在原型上添加 
      2 初学者 不要乱在原型上定义方法 - 影响巨大  
      
       */
      // const obj = {
      //   username: '悟空',
      //   say() {
      //     console.log('这个是say方法');
      //   },
      // };

      // //  万物皆对象
      // Object.prototype.show = function () {
      //   console.log('这个是原型上的show方法');
      // };

      // // 你看要 dom对象  大哥大
      // console.dir(document.querySelector("div"));

      // const arr = [];
      // console.log(arr);
      // arr.show();

      // console.log();
      // Math.show()

      // const str="123";
      // console.dir(str.show);
      // str.show();

      //  console.log(obj);// 直接看到定义在自己身上的属性和方法 看不见定义在 原型对象上的属性和方法
      //  console.log([]);
      // const arr=[];
      // arr.push
      // console.log(arr);
      // obj.say();
      // obj.show();
      // const arr1=[];
      // const arr2=[];
      // const arr3=[];

      // js 真垃圾 !!

      // DNA 存在于你整个家族 不只是父亲和儿子

      Array.prototype.forEach = function () {
        console.log("老子罢工了");
      };
      Array.prototype.map = function () {
        console.log("老子罢工了");
      };
      const arr = [1, 2, 3];
      arr.forEach(); // forEach 循环 到了你这个项目 这个知识用不了
      // 对于新入门小小员工来说  毁灭性的打击
      // 10多年代码 用不上 心态。。。。

      // arr.map()// 以前我学都是啥 想不开   你要有责任!!
    </script>