(javascript)(基础知识+实例) 17.面向对象

本文详细介绍了如何在JavaScript中封装动画,包括单属性、多属性动画以及判断动画结束的逻辑。此外,还展示了如何实现轮播图功能,包括手动创建对象、使用工厂函数、构造函数和原型链的方式。最后,通过实例演示了轮播图的基础版和动画版实现,涉及到元素平移的动画效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

封装js动画

  • 流程
    1. 定位
    2. 间隔定时器 计算运动量 让元素在当前位置的基础上加上一个运动量
    3. 判断是否到达终点 清除定时器
  • 封装单属性动画
    • 单个元素 单方向的运动
    • 三个参数
      • 运动的元素
      • 运动的属性
      • 运动的终点值
  • 判断动画何时结束
    • 四个参数
      • 运动的元素
      • 运动的属性
      • 运动的终点值
      • 运动结束之后执行的回调函数
  • 多属性的运动
    • 三个参数
      • 运动的元素
      • 运动的一组属性(对象)
        • 对象的key是属性
        • 对象的value是终点值
      • 运动结束之后执行的回调函数

轮播图

  • 通过改变图片长廊left属性

swiper轮播图插件

  • 已经封装好的轮播图插件
  • 使用步骤
    1. 下载swiper的css文件和js文件
    2. 准备轮播图的结构(不要轻易改变原结构的类名)记住低版本最外层swiper-container
    3. 定义整个容器的宽高
    4. 进行初始化js操作

面向对象 OOP

  • 面向对象是一门编程思想,用来指导我们思想编程的

  • 编程思想

    1. 面向过程
      • 关注每一步的实现
      • 一般开发使用都面向过程
    2. 面向对象
      • 不需要关注每一步的实现 只需要了解它的使用步骤
      • 一般是用来造轮子
    3. 面向函数
      • 暂不做介绍
  • 区别案例

    • 吃饺子
    • 面向过程
      1. 买面粉
      2. 买馅料
      3. 和面
      4. 做馅料
      5. 擀面皮
      6. 包饺子
      7. 煮饺子
      8. 吃饺子
    • 面向对象
      1. 开一个饺子店
      2. 点一份饺子等着吃饺子
  • 面向: 关注

  • 对象: 有多个键值对的数据类型

  • 核心思想: 利用自定义构造函数来创建对象,利用this来添加属性,利用构造函数的prototype来添加方法,在利用创建出来的对象解决开发中的问题

  • Math Date()

如何创建一个对象

  • 需要给创建出来的对象添加属性 添加方法
    + 学生属性
    - 用户名
    - 密码
    - 学号
    + 学生方法
    - 登陆
    - 测评
    - 考试
  1. 采用字面量或者内置构造函数来创建对象
    • var obj = {}
    • var obj = new Object()
    • 问题:代码冗余 不方便维护
  2. 利用工厂函数来创建对象
    • 工厂函数: 某个函数功能是用来批量生产数据
    • 优点: 代码简洁 方便维护
    • 缺点: 自己创建一个对象 自己返回一个对象 公共的方法用的不是同一份
  3. 原型
    • 每一个函数都有一个自带属性叫做prototype
    • 每一个对象都有一个自带属性叫做__proto__
    • 一般原型都要和构造函数结合使用
    • 构造函数的prototype就是new出来对象的__proto__
  4. 构造函数
    • 凡是利用new来调用的函数都称之为构造函数
    • 内置构造函数: js自带的构造函数
    • 自定义构造函数: 自己定义的构造函数
    • 构造函数定义和普通一样(建议函数名大写)
    • 构造函数利用new来调用的 普通函数直接调用的
  5. 利用自定义的构造函数创建对象
    • new可以自动创建对象 自动返回对象
    • 缺点:公共的方法用的不是同一份
  6. 原型链
    • 每一个对象使用属性或者方法的时候会优先在自身查找 自身有就使用自身的
    • 自身没有会往它的__proto__里面查找,找到了就使用 找不到就为undefined
  7. 利用自定义构造函数+原型来创建对象
  • new关键字到底做了啥?(面试题)

    • 函数内部的this指向
    • 函数的返回值
    1. 会自动在构造函数内部创建一个空对象
    2. 将构造函数内部的this指向到这个空对象
    3. 会自动返回这个创建的空对象
    • 利用new来实现面向对象创建对象的功能,只要给构造函数内部的this添加属性和方法,new出来的对象就有这些属性和方法
  • 实例化和实例(实例化对象)

    • 实例化: new调用的过程就成为实例化过程
      • new Person() 叫做new了一个person 或者叫做实例化了person
    • 实例化对象: new出来的对象就成为实例化对象,简称实例
      • var p = new Person() p叫做new出来的对象 也叫做实例化对象 也叫做实例 Person的实例

实例

(以下代码均为课程实例)

(1)利用字面量或者构造函数创建对象

    <script>
        // 创建一个空对象
        var student01 = {}
        // 给对象添加属性
        student01.username = 'jack'
        student01.password = '123456'
        student01.stuNo = '10086'
        // 给对象添加方法
        student01.login = function(){
            console.log('登陆的方法')
        }
        student01.cePing = function(){
            console.log('测评的方法')
        }
        student01.test = function(){
            console.log('考试的方法')
        }
        // 得到一个对象
        console.log(student01)
        var student02 = {}
        // 每一个同学都需要创建对象 添加属性和方法
        // 对象属性
        student02.username = 'rose'
        student02.password = '123456'
        student02.stuNo = '10088'

        // 对象方法
        student02.login = function(){
            console.log('登陆的方法')
        }
        student02.cePing = function(){
            console.log('测评的方法')
        }
        student02.test = function(){
            console.log('考试的方法')
        }
        console.log(student02)
        var student03 = {}
        // 每一个同学都需要创建对象 添加属性和方法
        // 对象属性
        student03.username = 'tom'
        student03.password = '3423'
        student03.stuNo = '3333'

        // 对象方法
        student03.login = function(){
            console.log('登陆的方法')
        }
        student03.cePing = function(){
            console.log('测评的方法')
        }
        student03.test = function(){
            console.log('考试的方法')
        }

        console.log(student03)
    </script>

(2)利用工厂函数创建对象

    <script>
        function createStudent(username, password, stuNo){
            // 创建一个空对象
            var emptyObj = {}
            // 添加属性
            emptyObj.username = username
            emptyObj.password = password
            emptyObj.stuNo = stuNo
            // 给对象添加方法
            emptyObj.login = function(){
                console.log('登陆的方法')
            }
            emptyObj.cePing = function(){
                console.log('测评的方法')
            }
            emptyObj.test = function(){
                console.log('考试的方法')
            }
            // 返回这个创建的对象
            return emptyObj
        }

        var student01 = createStudent('jack', '123456', '10086')
        console.log(student01)
        var student02 = createStudent('rose', '123456', '10088')
        console.log(student02)
        // 两个对象方法功能相同 使用的是同一个方法吗?
        student01.test()
        student02.test()
        // 不相等 原因是因为每一次创建的对象都是一个新的地址
        console.log(student01.test === student02.test)
        var student03 = createStudent('tom', '123456', '3333')
        console.log(student03)
    </script>

(3)原型

    <script>
        // 每一个函数都有一个自带的属性叫做prototype
        function fn(){

        }
        console.dir(fn)
        console.log(fn.prototype) // 这个prototype就是fn的原型 是一个对象


        // 每一个对象都有一个自带的属性叫做__proto__ 也是一个对象
        // 这个__proto__其实就是我们在浏览器里面看到的[[Prototype]]
        var obj = {}
        var obj1 = new Object()

        console.log(obj)
        console.log(obj.__proto__)
        console.log(obj1)
        console.log(obj.__proto__)
    </script>

(4)构造函数

    <script>
        // 利用内置构造函数创建数组或者对象 正则表达式 date
        new Array()
        new Object()
        new RegExp()
        new Date()

        // 自己创建一个构造函数
        function student(){

        }
        // 使用方式 普通函数直接调用的  构造函数利用new来调用的
        student() // 就是普通的函数
        new student() // 就是构造函数
    </script>

(5)new关键字

   <script>
        function student(){
            console.log('student调用了...')
            console.log(this) // 此处this代表谁?  直接调用指向是window  利用new来调用指向的是空对象
        }
        /* 从内部的this上面构造函数和普通函数的区别 */
        // 普通函数
        // student()
        // 构造函数
        // new student() // 也会调用student



        /* 从返回值上面构造函数和普通函数的区别 */
        function teacher(){

        }
        var t = teacher()
        console.log(t) // undefined

        var t1 = new teacher()
        console.log(t1) // 返回的是一个空对象


        /*  */

        function ss(){
            // 会自动在构造函数ss里面创建一个空对象  var s = {}
            // 会将函数内部的this指向到上面创建出来的对象
            // 要给创建出来的空对象添加属性添加方法 this就是这个创建出来的空对象
            // 给this添加属性 添加方式  其实就是给创建出来的空对象添加属性添加方法
            // console.log(this)
            this.username = 'jack' // this.username --> s.username
            // 会将这个创建出来的空对象返回  return s
        }
        // 得到就是返回的空对象
        var s = new ss()
        console.log(s)
    </script>

(6)利用自定义构造函数

    <script>
        function createStudent(username, password, stuNo){
            // 添加属性
            this.username = username
            this.password = password
            this.stuNo = stuNo
            // 给对象添加方法
            this.login = function(){
                console.log('登陆的方法')
            }
            this.cePing = function(){
                console.log('测评的方法')
            }
            this.test = function(){
                console.log('考试的方法')
            }
            // 自动返回这个创建的对象
        }
        // createStudent就变成构造函数 这个构造函数是我们自己定义 称为自定义构造函数
        var student01 = new createStudent('jack', '123456', '10086')
        console.log(student01)
        // 每new一次都会调用createStudent
        var student02 = new createStudent('rose', '123456', '10088')
        console.log(student02)

        // 公用的方法用的是同一个吗?
        console.log(student01.test === student02.test) 
    </script>

(7)再看原型

    <script>
        function student(){

        }
        console.log(student.prototype) // 构造函数也是一个函数 他也有prototype
        // 在构造函数prototype添加一个属性或者方法
        student.prototype.a = 1
        student.prototype.test = function(){
            console.log('考试')
        }
        var s = new student() // 每new一次构造函数 都会返回一个对象
        // new出来的对象也是一个对象 
        console.log(s)
        console.log(s.__proto__)
        // 构造函数的prototype和new构造函数得到的对象__proto__是同一个地址
        console.log(student.prototype === s.__proto__)
        // 给student.prototype添加就相当于给s.__proto__添加
        console.log(s.__proto__.a)
        s.__proto__.test()
        // 这是为什么?
        console.log(s.a)
        s.test()
    </script>

(8)原型链

    <script>
        //  对象自身有a,b,say
        var obj = {
            // a:1, 
            b:2,
            say: function(){
                console.log('say...')
            }
        }
        // 对象的原型上面也有 a,b,say
        // obj.__proto__.a = 2
        obj.__proto__.b = 3
        obj.__proto__.say = function(){
            console.log('say2...')
        }
        console.log(obj)
        console.log(obj.a)
    </script>

(9)利用自定义构造函数来解决创建对象的问题

  <script>
    function createStudent(username, password, stuNo) {
      // 给对象自身添加属性 属性是每一个对象独特
      this.username = username;
      this.password = password;
      this.stuNo = stuNo;
      // 给对象添加方法

      // 自动返回这个创建的对象
    }
    // 把公共的方法放到构造函数的原型添加
    createStudent.prototype.login = function () {
      console.log("登陆的方法");
    };
    createStudent.prototype.cePing = function () {
      console.log("测评的方法");
    };
    createStudent.prototype.test = function () {
      console.log("考试的方法");
    };
    // createStudent就变成构造函数 这个构造函数是我们自己定义 称为自定义构造函数
    var student01 = new createStudent("jack", "123456", "10086");
    console.log(student01);
    // 每new一次都会调用createStudent
    var student02 = new createStudent("rose", "123456", "10088");
    console.log(student02);
    // 每一个new出来对象它的__proto__和构造函数prototype是同一个地址
    console.log(student01.__proto__ === createStudent.prototype)
    console.log(student02.__proto__ === createStudent.prototype)
    student01.test() // student01.__proto__.test()
    student02.test()
    // 公用的方法用的是同一个吗?
    // 因为他们使用的都是createStudent.prototype里面的test方法
    console.log(student01.test === student02.test);
  </script>

(10)轮播图基础版

html部分

<!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>
    <link rel="stylesheet" href="./index.css" />
  </head>
  <body>
    <div class="slider">
      <!-- 图片部分 -->
      <ul class="imgs">
        <li>
          <img src="./img/01.png" alt="" />
        </li>
        <li>
          <img src="./img/02.png" alt="" />
        </li>
        <li>
          <img src="./img/03.jpg" alt="" />
        </li>
        <li>
          <img src="./img/04.png" alt="" />
        </li>
        <li>
          <img src="./img/05.png" alt="" />
        </li>
      </ul>
      <!-- 左右箭头 -->
      <div class="arrow">
        <a href="javascript:void 0;" class="left">&lt;</a>
        <a href="javascript:void 0;" class="right">&gt;</a>
      </div>
      <!-- 小点 -->
      <ol class="dots">
        <li class="active"></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
      </ol>
    </div>
    <script>
      // 获取整个图片的长廊
      var slider = document.querySelector("ul");
      // 获取所有的图片
      var imgs = document.querySelectorAll("ul>li");
      // 获取所有的点
      var dots = document.querySelectorAll("ol>li");
      // 获取一张图片的宽度
      // console.log(imgs)
      var width = imgs[0].offsetWidth;
      // console.log(width)
      /* 获取左右箭头 绑定点击事件 */
      var leftBtn = document.querySelector(".arrow .left");
      var rightBtn = document.querySelector(".arrow .right");
      // 用一个变量表示当前是第几张图片
      var index = 0;
      /* 点击右箭头切换到下一张 */
      rightBtn.addEventListener("click", function () {
        index++;
        // 如果到达最后一张 回到第一张
        if (index === imgs.length) {
          index = 0;
        }
        // 右键就是让整个长廊向左移动一张图片的宽度
        slider.style.left = -width * index + "px";
        setDotActive();
      });
      /* 点击左箭头切换到上一张 */
      leftBtn.addEventListener("click", function () {
        index--;
        // 判断到达第一张
        if (index === -1) {
          index = imgs.length - 1;
        }
        slider.style.left = -width * index + "px";
        setDotActive();
      });
      /* 封装一个点的激活色跟随的函数 */
      function setDotActive() {
        // console.log('设置点的激活色')
        // 清除所有点的激活色
        for (let i = 0; i < dots.length; i++) {
          dots[i].classList.remove("active");
        }
        // 找到点和图片的对应关系 只给当前对应的点加激活色
        dots[index].classList.add("active");
      }
      /* 点击点跳转到对应的图片 */
      for (let i = 0; i < dots.length; i++) {
        dots[i].onclick = function () {
          index = i; // 把点击点的下标赋值给图片index
          slider.style.left = -width * index + "px";
          setDotActive();
        };
      }
      var id = null
      /* 自动轮播图 */
      function next() {
        id = setInterval(() => {
          index++;
          // 如果到达最后一张 回到第一张
          if (index === imgs.length) {
            index = 0;
          }
          // 右键就是让整个长廊向左移动一张图片的宽度
          slider.style.left = -width * index + "px";
          setDotActive();
        }, 2000);
      }
      next()
      // 鼠标移入停止轮播
      slider.onmouseover = function () {
        clearInterval(id);
      };
      // 鼠标离开继续轮播
      slider.onmouseout = function () {
        next()
      };
    </script>
  </body>
</html>

css部分(index.css)

* {
    margin: 0;
    padding: 0;
    list-style: none;
  }
  .slider {
    position: relative;
    width: 600px;
    height: 400px;
    border: 1px solid #ddd;
    margin: 30px auto;
    overflow: hidden;
  }
  .slider ul {
    position: absolute;
    left: 0;
    top: 0;
  }
  .slider ul li {
    width: 600px;
    height: 400px;
    float: left;
  }
  .slider ul li img {
    width: 100%;
    height: 100%;
  }
  .slider .arrow .left,
  .slider .arrow .right {
    position: absolute;
    width: 40px;
    height: 40px;
    background-color: rgba(0, 0, 0, 0.4);
    color: #fff;
    text-align: center;
    line-height: 40px;
    text-decoration: none;
    font-size: 30px;
    top: 50%;
  }
  .slider .arrow .left {
    left: 0;
  }
  .slider .arrow .right {
    right: 0;
  }
  .slider .dots {
    position: absolute;
    bottom: 20px;
    left: 0;
    width: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
  }
  .slider .dots li {
    width: 20px;
    height: 20px;
    border-radius: 50%;
    border: 2px solid orange;
    cursor: pointer;
    margin: 0 5px;
  }
  .slider .dots li.active {
    background-color: brown;
  }

js部分(move.js)

function move(element,attr,end, cb) { // 在此处添加第四个参数 是一个回调函数
    // 计算运动量
    // 整个距离的十分之一
    // 整个距离 (终点-起点)
    // 运动量 =  (终点-起点) / 10
    // 获取起点的值
    // console.log(getComputedStyle(element))
    // console.log(getComputedStyle(element).attr) // attr不是对象里面某一个具体key名称 是一个变量
    // console.log(getComputedStyle(element)[attr]) // attr不是对象里面某一个具体key名称 是一个变量
    let start = parseFloat(getComputedStyle(element)[attr])
    // console.log(start)
    let step = (end - start) / 10
    // console.log(step)
    let id = setInterval(()=>{
        // 获取当前位置
        let current = parseFloat(getComputedStyle(element)[attr])
        // 判断是否到达终点
        if(current === end){
            // 此处动画执行结束了
            clearInterval(id)
            // 在动画结束的时候调用cb函数 调用cb其实就是调用传递的第四个实参
            cb && cb()
        }else{
             // 在当前位置的基础上运动一个运动量
            element.style[attr] = current + step + 'px'
        }
    }, 400)
  }

动画版

html部分

<!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>
    <link rel="stylesheet" href="./index.css" />
    <script src="./move.js"></script>
  </head>
  <body>
    <div class="slider">
      <!-- 图片部分 -->
      <ul class="imgs">
        <li>
          <img src="./img/01.png" alt="" />
        </li>
        <li>
          <img src="./img/02.png" alt="" />
        </li>
        <li>
          <img src="./img/03.jpg" alt="" />
        </li>
        <li>
          <img src="./img/04.png" alt="" />
        </li>
        <li>
          <img src="./img/05.png" alt="" />
        </li>
      </ul>
      <!-- 左右箭头 -->
      <div class="arrow">
        <a href="javascript:void 0;" class="left">&lt;</a>
        <a href="javascript:void 0;" class="right">&gt;</a>
      </div>
      <!-- 小点 -->
      <ol class="dots">
        <li class="active"></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
      </ol>
    </div>
    <script>
      // 获取整个图片的长廊
      var slider = document.querySelector("ul");
      // 获取所有的图片
      var imgs = document.querySelectorAll("ul>li");
      var len = imgs.length;
      console.log(len);
      // 获取所有的点
      var dots = document.querySelectorAll("ol>li");
      // 获取一张图片的宽度
      // console.log(imgs)
      var width = imgs[0].offsetWidth;

      /* 动态生成假的第一张和最后一张 */
      var cloneFirst = imgs[0].cloneNode(true);
      var cloneLast = imgs[len - 1].cloneNode(true);
      console.log(cloneFirst);
      console.log(cloneLast);
      // 把第一张插入最后一张
      slider.appendChild(cloneFirst);
      // 把最后一张插入第一张
      slider.insertBefore(cloneLast, imgs[0]);
      // 默认显示第一张
      slider.style.left = -width + "px";
      // 重新设置ul的宽度 = 图片的张数 * 单张图片宽度
      slider.style.width = width * (len + 2) + "px";
      // console.log(width)
      /* 获取左右箭头 绑定点击事件 */
      var leftBtn = document.querySelector(".arrow .left");
      var rightBtn = document.querySelector(".arrow .right");
      // 用一个变量表示当前是第几张图片
      var index = 1; // 前面还有一张假的

      /* 
        点击的时候需要判断动画是否在运动的过程中 
        如果动画还没有结束 不允许它再开启新的定时器 
        只有动画结束之后 才允许开启新的定时器
      */
      // 定义变量
      var isMove = false; // 是否在动画过程中
      /* 点击右箭头切换到下一张 */
      rightBtn.addEventListener("click", function () {
        if(isMove) return // 如果动画在运动过程中 不能往下走 不会再次开启一个新的动画
        console.log('动画是否在运动', isMove)
        index++;
        isMove = true // 点击代表动画开启
        // 右键就是让整个长廊向左移动一张图片的宽度
        move(slider, "left", -width * index, function () {
          // 到达最后一张切换到假的第一张  瞬间切换真的最后一张
          if (index === len + 1) {
            index = 1;
            slider.style.left = -width * index + "px";
          }
          isMove = false // 动画结束
        });
      });
      /* 点击左箭头切换到上一张 */
      leftBtn.addEventListener("click", function () {
        index--;
        move(slider, "left", -width * index, function () {
          // 动画结束之后才执行此处代码
          // 判断如果由第一张切换到假的最后一张  瞬间拉到真的最后一张
          if (index === 0) {
            index = len;
            slider.style.left = -width * index + "px";
          }
        });
      });
    </script>
  </body>
</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不二哈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值