JavaScript高级(二)

114 阅读9分钟

一、案例-面向对象

1.需求-标签内插入图片

(1)const imgModel =new MyImg("图片地址") (不要直接插入到body标签上)

(2) imgModel.append("父元素的选择器"); 希望可以在父元素里面插入一个img

(3)根据给到的需求

①先拆分出 代码的结构 要定义构造函数吗 原型上要写什么方法

②根据给到的代码具体需求 结合 底层js+webAPI的知识 来实现 封装的功能

③在以前更多的是学习如何直接写代码问题 ,现在也是在写代码 - 直接解决问题,通过封装代码(提高的能力)->解决问题

  <body>
    <ul>
      <li>1</li>
      <li>2</li>
      <li>3</li>
      <li>4</li>
      <li>5</li>
    </ul>
    <script>
      function MyImg(src) {
        const img = document.createElement("img");
        img.src = src;
        this.dom = img;
      }

      MyImg.prototype.append = function (parentSelector) {
        // 通过 this.dom  获取到 构造函数中创建好的图片img对象
        // 希望可以在父元素里面插入一个img  底层的webAPI代码是什么
        // document.body 父元素
        // 父元素.appendChild(子元素);  // img 创建的元素
        document.querySelector(parentSelector).appendChild(this.dom);
      };

      // 获取指定的li标签
      // const li=document.querySelector("li:nth-child(3)");

      // 先创建一个图片标签
      const imgModel = new MyImg("./images/b_01.jpg");
      // 指定位置来插入元素
      imgModel.append("li:nth-child(3)"); // 图片插入到指定的父元素中
    </script>
  </body>

二、原型-继承

1.原型-继承
<script>
      // 爸爸 person  name属性、age属性、height、属性
      function Person(name, age, height) {
        this.username = name;
        this.age = age;
        this.height = height;
      }

      // 学生  student name属性、age属性、height属性、color颜色
      function Student(name, age, height, color) {
        this.name = name;
        this.age = age;
        this.height = height;
        this.color = color;
      }

      const p1 = new Person("奥特之父", 5000, 200);
      const s1 = new Student("迪迦", 2000, 100);

      // 我想 让 两个实例 形成父子关系     没有做到继承
      // 父亲有的东西 儿子可以直接使用
      // 父亲做了修改,儿子跟着发生修改

      // 稍后我们的代码如果可以做到
      // 1 父亲有的属性,儿子不用再写一次
      // 2 父亲某些属性做了修改,儿子可以跟着发生修改
      console.log(p1);
      console.log(s1);
    </script>
2.call

(1)叫上下文模式,分为 apply 与 call

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

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

    const obj = {
      name: "屌丝"
    }

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

(3)call应用:将伪数组转为数组

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

    }

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

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

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

    const obj = {
      name: "屌丝"
    }

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

(5)apply应用:简化log方法

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

(6)**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
3.原型-继承-call

(1)关键代码

// 父
function Person(name, age, height) {
        this.username = name;
        this.age = age;
        this.height = height;
      }
// 子
function Student(name, age, height, color) {
// 儿子想要复用父亲的某些代码 父亲方法.call(this,形参1,形参2。)
        Person.call(this, name, age, height); 
        // 父亲有的形参有重复的可不写
        // this.name = name;
        // this.age = age;
        // this.height = height;
        // 添加color
        this.color = color;
      }

(2)完整

<script>
      function Person(name, age, height) {
        this.username = name;
        this.age = age;
        this.height = height;
      }

      function Student(name, age, height, color) {
        // 这一行代码解决了继承的问题!!
        // call 借用别人的方法
        // 谁有方法
        // 谁想要借用person方法  this = 实例 = 下面代码的s1
        Person.call(this, name, age, height); //   儿子想要复用父亲的某些代码 父亲方法.call(this,形参1,形参2。)
        // this.name = name;
        // this.age = age;
        // this.height = height;
        this.color = color;
      }

      // const p1 = new Person('奥特之父', 5000, 200);
      const s1 = new Student("迪迦", 2000, 100, "yellow");

      // console.log(p1);
      console.log(s1);
    </script>

3.案例-call

(1)call

    <script>
      // call 借调 可以让一个对象 来借用另外一个对象的方法
      const obj = {
        name: "老王",
        skill() {
          // this = obj   this.name =  obj.name
          console.log(this.name + " 翻墙"); // this 等于 调用者
        },
      };

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

      // 大郎没有翻墙的方法 但是 老王有
      // 让大郎 借用一下 老王的翻墙功能
      obj.skill.call(obj2); // 你有把老王的翻墙技能 借给谁使用

      const obj3 = {
        name: "刘同学",
      };
      obj.skill.call(obj3); //  ? 老王还是刘同学
    </script>

(2)call

    <script>
      const obj = {
        name: "超人",
        skill() {
          console.log(this.name + " 会飞");
        },
      };
      // 超人有技能 会飞
      const obj2 = {
        name: "普通人",
      };

      // 让普通人  借 一下超人的技能 skill  固定的规则
      obj.skill.call(obj2);
    </script>

(1)call

①超人案例

      const obj = {
        name: "超人",
        skill: function (fly) {
          console.log(this.name + fly);
        },
      };
      obj.skill(" 会飞");
      obj.skill(" 发射激光");
      console.log(obj);

      // console.log(obj);{name:"超人",skill:function(){}}

②装甲车案例

const car = {
        name: "装甲车",
        add: function (username, age) {
          // 本质 可以给car添加新的属性
          this.username = username;
          this.age = age;
        },
      };

      // car.add('发动机', 100);
      // console.log(car);

      const obj3 = {};
      // car.add.call(obj); // obj 想要借用 添加属性的方法
      car.add.call(obj3, "发动机", 200); // call 传入参数  add.call(谁要借用,被借用的方法的形参1,被借用的方法形参2)
      console.log(obj3); // obj 有没有 username属性(不用管属性值)、有没有age属性(不用属性值)

      // 可以实现 了 一个空对象 obj 通过call的使用 来实现了 借别人的方法add 来给obj添加新的属性
4.继承-案例

(1)对象的两大特点

①属性 - 属性的继承

②行为-方法的继承

(2)案例

      function Person(name, age) {
        this.username = name;
        this.age = age;
      }

      Person.prototype.say = function () {
        console.log(this.username, this.age); // 我是谁 我的年龄是
      };
      Person.prototype.show = function () {
        console.log(this.username + " 秀肌肉");
      };


      function Student(name, age, color) {
        Person.call(this, name, age);
        this.color = color;
      }

      // 函数赋值
      const s1 = new Student("三好学生", 10, "yellow");

      // 把父亲的行为 也设置到儿子上
      Student.prototype.say = Person.prototype.say;
      Student.prototype.show = Person.prototype.show;

      s1.say();
      s1.show();

(3)继承案例

1)封装 代码 实现以下的功能

父亲 Element 、属性 dom this.dom 、行为 实例.append(父元素标签的选择器)

  //  父亲
      function Element() {
        this.dom = null;
      }
      //  父亲
      Element.prototype.append = function () {};

2)儿子1 ElementDouble div

继承父亲 Element 、属性 dom 、 行为 append

   // 儿子1
      function ElementDouble(tagName, content) {
        const dom = document.createElement(tagName);
        dom.innerText = content;
        this.dom = dom;
      }
      ElementDouble.prototype.append = function (parentSelector) {
        // 真正的dom父元素.appendChild(子元素)
        document.querySelector(parentSelector).appendChild(this.dom);
      };

3)儿子2 ElementSingle img

属性 dom 、行为 append

 // 儿子2
      function ElementSingle(tagName, src) {
        const dom = document.createElement(tagName); // 图片
        dom.src = src;
        this.dom = dom;
      }
      ElementSingle.prototype.append = function (parentSelector) {
        document.querySelector(parentSelector).appendChild(this.dom);
      };

4)当执行以下代码时 出现对应效果

①const divModel = new ElementDouble("div","div内的文字")

②divModel.append("body") ; body标签可以出现 一个 div

③const imgModel = new ElementSingle("img","图片地址")

④imgModel.append("body"); body标签可以出现 一个图片

const divModel = new ElementDouble("div", "div内的文字");
divModel.append("body");

const imgModel = new ElementSingle("img", "./images/b_01.jpg");
imgModel.append("body");

三、ES6

1.目前浏览器 支持的js的代码版本,主要都支持 es5 (for if while 函数 forEach )版本,新的语法 es6 (提供了更加简单强大的代码更能力) 提高我们的开发效率。
2.函数参数的默认值

如果你给我传递了形参 我就输出你的形参,如果没有给我输出,我就输出你好

// es6 函数参数默认值
// 你好 默认值
function show(msg = "你好", str = "你我都好") {
  console.log(msg, str);
}

show(); // 没有传递参数  输出你好
show("大家好"); // 输出 大家好
show("大家好", "世界美好"); // 输出 大家好
3.对象简写

(1)变量名如果和你的对象的属性名一致 可以简写

let username='悟空'
const obj = { username }

(2)对象的方法简写

 const obj ={
          say(){  // 简写的方法
          }
        }

(3)案例

      const username = 123;
      const color = "red";

      const say = function () {};

      function show() {}

      // 很常用
      const obj = {
        username, // username:username
        color, // color : color
        say,
        show,
        height: 100,
      };

      obj.height = 200;
      console.log(obj);

      // 对象中方法的简写
      const person = {
        show: function () {
          console.log("show");
        }, // 常规写法
        // es6 关于 方法简写
        say() {
          console.log("say");
        }, // es6 关于 方法简写
      };
      person.show();
      person.say();
4.解构
(1)解构 对象 和 数组

① 对象:const { username,height } = {username:"悟空",height:200}

      // 对象的解构
      const obj = {
        username: "悟空",
        height: 100,
      };

      // 对象解构
      const { username, height } = obj;
      console.log(username, height);

      // 声明两个变量 来获取obj的两个属性
      // low 代码!!!
      // const username = obj.username;
      // const height = obj.height;

②数组:const [a,b]=[100,200];

      // 数组解构
      // const [a, b, c, d] = arr;
      const [a, b, c, d] = ["悟空", "八戒", "龙马", "沙僧", "三藏"];
      console.log(a, b, c, d);

③通过dataset获取data属性

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

④解构+默认值

  // 解构 + 默认值2
      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);
(2)解构 + 默认值

①如果 右边的对象中没有height 属性 那么 height变量 = 1000

const { username,height=1000 } = {username:"悟空",height:200}

②c 在右边找不到对应的数据 c 就使用默认值 300

const [a,b,c=300]=[100,200];

     //   const obj = {
      //     username: 100,
      //     height: 500,
      //   };
      //   const { username, height = 200 } = obj;
      //   console.log(username, height);
(3)拓展运算符-剩余运算符-延展运算符

1)剩余运算符

①数组中 const [a,b,...c]=[1,2,3,4,5,6,7]; c =[3,4,5,6,7]

      // 获取剩下 数组
      const [a, b, ...c] = [1, 2, 3, 4, 5, 6, 7];
      // const [a, b, ...c] = [1, 2, 3];
      // const [a, b, ...c] = [1, 2];
      // console.log(a, b); // 1 , 2
      console.log(c); // [3,4,5,6,7]
      // console.log(c); //  [3]
      // console.log(c); //  []

      // 获取剩下 对象
      //   const { a, ...c } = { a: 1, b: 2, c: 3 };
      //   const { a, b, c, ...d } = { a: 1, b: 2, c: 3 };
      //   console.log(c); // ? {b:2,c:3}
      //   console.log(d); // ?

②应用场景 伪数组转真正的数组

      // const [...a]=[1,2,3,4];
      // console.log(a);// [1,2,3,4]
      // console.log(a.map);
      // const lis = document.querySelectorAll('li');// lis 伪数组 没有map方法
      // const newList = [...lis]; // 如何理解 ? 转成真正的数组
      // console.log(newList.map);

2)对象中 const { a,...d } = { a: 1, b: 2, c: 3 }; // d = {b:2,c:3 }

3)函数的形参中

① calc(1, 2, 3); function calc(...args) {} // args = [1,2,3]

      // 计算数据和的功能
      // calc(1,2)// 1 + 2
      // calc(1,2,3) // 1 + 2 + 3
      calc(1, 2, 3, 4); // 1 + 2 + 3 + 4
      function calc(...args) {
        // args 数组 装载这所有传给calc的参数
        // console.log(args);
        let sum = 0;
        args.forEach((value) => (sum += value));
        console.log(sum);
      }
      //   calc(1, 2); // ? [1,2]
      //   calc(1, 2, 3); // [1,2,3]
      calc(1, 2, 3, 4); // [1,2,3,4]

计算数字总和

4)练习题目

​ getMax(1,2,3) // 输出3

​ getMax(1,4) // 输出4

​ getMax(1,20,4) // 输出20

​ getMax(1,20,4,30,2) // 输出30

方法一:

    // 计算最大值的写法
      function getMax(...args) {
        // args= 数组  接收 所有传递给 getMax方法的 参数
        // console.log(args);
        // 计算最大值 的
        let max = args[0];
        args.forEach((value) => {
          if (value > max) {
            max = value;
          }
        });
        console.log(max);
      }

      // getMax(1);
      // getMax(1, 2);
      // getMax(1, 2, 3);
      // getMax(11, 33, 2, 3);

      // Math对象 自己就封装过 计算最大值和最小值的代码
      // console.log(Math.max(1,3,4,2));// 4
      // console.log(Math.min(1,3,4,2));// 1

方法二

      function getMax2(...args) {
        // Math.max(1,3,4,2)
        // args=[1,3,4,3]
        // Math.max([1,3,4,3]) =>  Math.max(1,3,4,2)
        // 剩余运算符的
        console.log(Math.max(...args));
      }
      getMax2(1, 2, 3, 4); // 体现的思想 不再是 剩余 。  展开-延展-拓展 思想

5)展开运算符

      // 展开 ... 用法
      const obj = {
        username: "悟空",
        height: 200,
      };
      // // 新创建一个对象 这个对象 具有 所有 obj的属性
      // // 同时 还有多一个属性,color
      // // const newObj = obj;// 对象是引用类型   写 =  将 obj的地址 给了一份 newObj  两个变量指向的地址同样的 同一个对象
      // // newObj.color = 'yellow';
      // // console.log("新的对象",newObj);
      // // console.log("旧的对象",obj);
      // // 建议这做  互补影响
      const newObj = { ...obj, color: "yellow" }; // 给newObj 开辟新的内存空间
      // const newObj={ username:"悟空",height:20}; // 给newObj 开辟新的内存空间
      newObj.username = "八戒";
      newObj.weight = 100;
      console.log(obj);
      console.log(newObj);

6)展开运算符 对数组操作

      // 展开运算符 对数组操作
      const arr = ["a", "b", "c"];
      // 在数组的后面 新增一个 元素 'd'
      const newArr = [...arr, "d"];

      // 在数组宅前面 新增一个属性 w
      console.log(newArr);
      // const newArr = ['w', ...arr];
      // console.log(newArr);
      // arr.push
      // arr.unshift
      // 中间 无法使用 ...
      // splice 来实现
5.数组去重

1.输入框绑定键盘按下事件

(1)判断按下的是不是 回车键

if (event.key === "Enter"){}

(2)是的话 获取输入框的值

const isHas = arr.some((value) => value === this.value); // 在我的数组中找到了和你待添加的元素 一样的值 返回true
          
        }

(3)判断当前要显示的数据 是否已经存在于数组中 filter 可以 some可以

​ ①如果是已经存在 就不要继续添加

if (isHas) {
            // 有重复了 不要再添加
            console.log("有重复了 不要再添加");
          } 

​ ②如果是不存在 就继续添加

else {
            // 没有重复 你可以添加
            // 把它添加到数组中
            arr.push(this.value);
            // 数组发生了改变 重新调用render方法 来实现页面的渲染
            render();
          }

(4)把它添加到数组中

else {
            // 没有重复 你可以添加
            // 把它添加到数组中
            arr.push(this.value);
            // 数组发生了改变 重新调用render方法 来实现页面的渲染
            render();
          }

(5)写一个方法 把数组的数据 拼接成li标签,插入到 ul中

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

2.完整代码

①方法一:some()

      const input = document.querySelector("input");
      const ul = document.querySelector("ul");
      const arr = ["a", "b"];
      render();

      input.addEventListener("keydown", function (event) {
        //判断按下的是不是回车
        if (event.key === "Enter") {
          // console.log(this.value);

          // some 如果数组中有一项 是返回了true 整个some方法就返回了true
          // 调用some方法的时候,在它的回调函数中 拿数组中的元素 和 当前要添加的元素 做比较 如果相等 就返回true 表示找到了重复

          const isHas = arr.some((value) => value === this.value); // 在我的数组中找到了和你待添加的元素 一样的值 返回true
          if (isHas) {
            // 有重复了 不要再添加
            console.log("有重复了 不要再添加");
          } else {
            // 没有重复 你可以添加
            // 把它添加到数组中
            arr.push(this.value);
            // 数组发生了改变 重新调用render方法 来实现页面的渲染
            render();
          }
        }
      });

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

②方法二:

const input = document.querySelector("input");
      const ul = document.querySelector("ul");
      const arr = ["a", "b"];
      render();

      input.addEventListener("keydown", function (event) {
        if (event.key === "Enter") {
          // 定一个变量,表示数组有没有重复的数据
          // 假设它 没有重复
          let isHas = false;
          for (let index = 0; index < arr.length; index++) {
            // 判断数组中的每一个元素 和 当前要输入的值做比较
            // 如果找到了,设置 isHas=true 同时 打断循环
            if (arr[index] === this.value) {
              // 找到重复了
              isHas = true;
              break;
            }
          }

          // 判断数据有没有重复
          if (isHas) {
            // 有重复
            console.log("有重复");
          } else {
            // 没有重复 添加
            arr.push(this.value);
            render();
          }
        }
      });

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

③方法三:filter()过滤

      const input = document.querySelector("input");
      const ul = document.querySelector("ul");
      let arr = ["a", "b"];
      render();

      input.addEventListener("keydown", function (event) {
        if (event.key === "Enter") {
          // filter 来解决
          // filter 过滤
          // 先过滤出  和当前的输入框的值 不相等的 数据
          // 然后再添加
          // 当前输入框的值 "d"
          // 我数组 ['a','c','d']
          // 数组过滤 不包含 "d"  => ['a','c']
          // ['a','c'] 再次添加 'd' 进去 就可以了

          // 过滤出不包含 当前输入框的值的数组
          const newArr = arr.filter((value) => value !== this.value);
          // debugger
          // 让我们的旧的数组 等于你过滤后的数组
          arr = newArr;
          arr.push(this.value);
          render();
        }
      });

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

④方法四:

 const input = document.querySelector("input");
      const ul = document.querySelector("ul");
      let arr = ["a", "b"];
      render();

      input.addEventListener("keydown", function (event) {
        if (event.key === "Enter") {
          // this.value =  "b"
          // arr = ["a","b","c"]

          // 先找到 “b” 在我数组的索引 位置
          // 执行 数组 删除元素  arr = [a,c]
          // 然后 再去执行 添加元素的操作

          let i = -1; // -1 表示没有找到
          for (let index = 0; index < arr.length; index++) {
            if (arr[index] === this.value) {
              i = index;
              break;
            }
          }

          // 判断 i 等于-1 表示没有相同,直接添加
          //  i 不等于-1 表示有相同,先执行删除 再添加
          if (i === -1) {
            // arr.push(this.value);
          } else {
            // 找到相同
            arr.splice(i, 1);
            // arr.push(this.value);
          }
          arr.push(this.value);

          render();
        }
      });

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

⑤方法五(了解):

    const input = document.querySelector("input");
      const ul = document.querySelector("ul");
      let arr = ["a", "b"];
      render();

      input.addEventListener("keydown", function (event) {
        if (event.key === "Enter") {
          // 数组去重 一堆方法
          // for forEach some
          // 没有细讲 indexOf find findIndex  includes set every

          // 没有足够的技术积累和代码量  优雅不起来!!

          // indexOf find findIndex includes set

          // const index=arr.indexOf(this.value);
          // const index=arr.findIndex(value=>value===this.value)
          // const item=arr.find(value=>value===this.value);
          // const isHas=arr.includes(this.value)
          // if(!isHas){
          //   // 没有找到
          //   arr.push(this.value);
          //   render();
          // }

          const set = new Set(arr);
          set.add(this.value);
          arr = [...set];
          render();
        }
      });

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