欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

class类学习 — 封装elementUI的dialog组件

程序员文章站 2022-06-28 16:16:42
前言在ES6之前,准确来说JavaScript语言只有对象,没有类的概念。生成实例对象的传统方法是通过构造函数,与传统的面向对象语言(比如 C++ 和 Java)差异很大,ES6 提供了更接近传统语言的写法,引入了 class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。但需要清楚的是ES6中class只是构造函数的一种语法糖,并非新鲜玩意,class能实现的,我们通过ES5构造函数同样可以实现。一、构造函数与class写法的部分区别【1.1】构造函数 functio...

前言

在ES6之前,准确来说JavaScript语言只有对象,没有类的概念。生成实例对象的传统方法是通过构造函数,与传统的面向对象语言(比如 C++ 和 Java)差异很大,ES6 提供了更接近传统语言的写法,引入了 class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。但需要清楚的是ES6中class只是构造函数的一种语法糖,并非新鲜玩意,class能实现的,我们通过ES5构造函数同样可以实现。

一、构造函数与class写法的部分区别

【1.1】构造函数

  function Person(name, age) {
    this.name = name;
    this.age = age;
  }
  Person.prototype.sayHi = function () {
    console.log("您好");
  };
  let person = new Person("dingFY", 20);
  console.log(person.name) // dingFY
  console.log(person.age) // 20
  person.sayHi() // 您好

【1.2】类

  class Person {
    constructor(name, age) {
      this.name = name;
      this.age = age;
    };
    sayHi() {
      console.log("您好");
    };
  };
  let person = new Person('dingFY', 20);
  console.log(person.name) // dingFY
  console.log(person.age) // 20
  person.sayHi() // 您好

【1.3】 主要区别

  1. 构造函数名Person在class写法时变成了类名,但调用方式不变,依然通过new关键字创建实例
  2. 构造函数中this相关操作,在class写法时归纳到了constructor方法中,也就是说ES5的构造函数Person对应ES6的Person类中的constructor构造方法
  3. ES5中原型上的方法sayHi在ES6 class写法中直接写在了内部,同时省略了function关键字
// 对于方法添加,下面这两种写法是等效的
// ES5
function Parent() {};
Parent.prototype = {
    constructor: function () {},
    sayName: function () {},
    sayAge: function () {}
};

// ES6
class Parent {
    constructor() {}
    sayName() {}
    sayAge() {}
};

// constructor()、sayName()、sayAge()这三个方法,其实都是定义在Parent.prototype上面
// 因此,在类的实例上面调用方法,其实就是调用原型上的方法。

二、类的基础用法

【2.1】类定义

  // 匿名类
  let Example = class {
    constructor(a) {
      this.a = a;
    }
  }
  // 命名类
  let Example = class Example {
    constructor(a) {
      this.a = a;
    }
  }

【2.2】类声明

// 类声明
class Example {
  constructor(a) {
    this.a = a;
  }
}

// 不可重复声明
// class Example {}
// class Example {} 
// 报错:Uncaught SyntaxError: Identifier 'Example' has already been declared

// let Example = class {}
// class Example {} 
// 报错:Uncaught SyntaxError: Identifier 'Example' has already been declared

【2.3】注意

 

  • class不存在变量提升,所以需要先定义再使用。因为ES6不会把类的声明提升到代码头部,但是ES5就不一样,ES5存在变量提升,可以先使用,然后再定义。
//ES5可以先使用再定义,存在变量提升
new A();
function A(){}

//ES6不能先使用再定义,不存在变量提升 会报错
new B(); // Uncaught ReferenceError: Cannot access 'B' before initialization
class B{}
  • 在类中声明方法的时候,千万不要给该方法加上function关键字,方法之间也不要用逗号分隔,否则会报错
  class Example {
    constructor(a) {
      this.a = a;
    }
    getName() {}      // 不需要逗号
    function getAge() // 报错
  }
  • 类的内部所有定义的方法,都是不可枚举的,这与ES5构造函数行为不一致
  class Example {
      constructor(x, y) {}
      getName() {}
  }
  console.log(Object.keys(Example.prototype)) // []
  console.log(Object.getOwnPropertyNames(Example.prototype))  // ["constructor","getName"]
  •  constructor()方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor()方法,如果没有显式定义,一个空的constructor()方法会被默认添加。
class Example {}
// 等同于
class Example {
  constructor() {}
}
  • 通过class类创建实例必须使用new关键字,不使用会报错,这点与构造函数不同,在ES5中其实我们不使用new也能调用构造函数创建实例,虽然这样做不符合规范。
  // ES6
  class Person {
    constructor() {}
  };
  let person = Person(); // 报错

  // ES5
  let Person = function (name) {
    this.name = name;
  };
  let person = Person(); // 不符合规范
  • 与 ES5 一样,类的所有实例共享一个原型对象

  class Person {
    constructor() {}
  };

  let p1 = new Person()
  let p2 = new Person()
  console.log(p1.__proto__ === p2.__proto__) // true
  • ES6 的类只是 ES5 的构造函数的一层包装,所以函数的许多特性都被Class继承,包括name属性。name属性总是返回紧跟在class关键字后面的类名。

class Person{}
Person.name // "Point"
  • 类和模块的内部,默认就是严格模式,所以不需要使用use strict指定运行模式

三、取值函数(getter)和存值函数(setter)

与 ES5 一样,在“类”的内部可以使用getset关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为

class Person {
    constructor() {
        // ...
    }
    get name() {
        return 'getter';
    }
    set name(value) {
        console.log('setter: ' + value);
    }
}

let son = new Person();

son.name = 'dingFY' // setter: dingFY
console.log(son.name) // getter

四、静态属性和静态方法

【4.1】静态属性

静态属性指的是 Class 本身的属性,写法是在实例属性的前面,加上static关键字

class Person {
    static age = 18;
    constructor() {
        console.log(Person.age); 
    }
}
let person = new Person() // 18

【4.2】静态方法

类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是只能在类内部使用或者直接通过类来调用,这就称为“静态方法”。如果静态方法包含this关键字,这个this指的是类,而不是实例。父类的静态方法,可以被子类继承。

  class Foo {
    static classMethod() {
      console.log('test')
    }
  }

  // 类调用静态方法
  Foo.classMethod() // 'test'

  // 实例调用静态方法
  let foo = new Foo();
  foo.classMethod() // TypeError: foo.classMethod is not a function
  class Foo {
    static classMethod() {
      console.log('test')
    }
  }

  // 父类的静态方法可以被子类继承
  class Bar extends Foo {}
  Bar.classMethod() // 'test'

五、Class 的继承

  • class 通过 extends 关键字实现类的继承,子类可以继承父类的所有属性和方法。
class Person {}
class Son extends Person {}
  • 子类 constructor 方法中必须有 super ,且必须出现在 this 之前,否则新建实例时会报错
    class Person {
        constructor(age) {
            this.age = age
        }
        sayHi() {
            return '您好'
        }
    }

    class Son extends Person {
        constructor(age, name) {
            super(age) // 子类继承必须要有super(), 且必须在this前面
            this.name = name
        }
        sayHi() {
            console.log(super.sayHi() + `,我是${this.name}`)
        }
    }

    let son = new Son(18, 'dingFY');
    console.log(son.age) // 18
    console.log(son.name) // dingFY
    son.sayHi() // 您好,我是dingFY

六、实例

【6.1】封装弹框组件

  // dialog组件封装
  class Dialog {
    constructor(options) {
      // 合并默认配置和用户传的配置参数;
      this.opts = Object.assign({
        width: "30%",
        height: "200px",
        title: "标题",
        content: "内容",
        dragable: false, //是否可拖拽
        maskable: true, //是否有遮罩
        isCancel: true, //是否有取消按钮
        success() {
          console.log('点击确定按钮处理逻辑'); //
        },
        cancel() {
          console.log('点击取消按钮处理逻辑');
        }
      }, options);
      this.init(); // 自动调用
    }
    init() {
      // 渲染视图
      this.renderView();
      // 捕获弹框点击事件
      this.dialogHtml.onclick = e => {
        switch (e.target.className) {
          case 'k-close': // 关闭按钮
            this.close();
            this.opts.cancel();
            break;
          case 'k-cancel': // 取消按钮
            this.close();
            this.opts.cancel();
            break;
          case 'k-primary': // 确定按钮
            this.close();
            this.confim();
            break;
        }
      }
    }

    // 确定按钮操作
    confim(value) {
      this.opts.success(value);
    }

    //关闭对话框;
    close() {
      this.dialogHtml.querySelector(".k-wrapper").style.display = "none";
      this.dialogHtml.querySelector(".k-dialog").style.display = "none";
    }

    // 显示对话框;
    open() {
      // 是否显示遮罩层
      if (this.opts.maskable) {
        this.dialogHtml.querySelector(".k-wrapper").style.display = "block";
      }
      // 是否可拖拽
      if (this.opts.dragable) {
        let dialog = this.dialogHtml.querySelector(".k-dialog")
        let drag = new Drag(dialog);
      }
      this.dialogHtml.querySelector(".k-dialog").style.display = "block";
    }

    // 渲染视图
    renderView() {
      this.dialogHtml = document.createElement("div");
      this.dialogHtml.innerHTML = `<div class="k-wrapper"></div>
                <div class="k-dialog"  style="width:${this.opts.width};height:${this.opts.height}">
                    <div class="k-header">
                        <span class="k-title">${this.opts.title}</span><span class="k-close">X</span>
                    </div>
                    <div class="k-body">
                        <span>${this.opts.content}</span>
                    </div>
                    <div class="k-footer">
                        ${this.opts.isCancel ? '<span class="k-cancel">取消</span>' : ''}
                        <span class="k-primary">确定</span>
                    </div>
                </div>`;
      document.querySelector("body").appendChild(this.dialogHtml);
    }
  }

【6.2】封装拖拽方法

  // 拖拽方法封装
  class Drag {
    constructor(ele) {
      this.ele = ele
      this.downFn();
    }

    // 按下鼠标
    downFn() {
      this.ele.onmousedown = e => {
        let ev = e || window.event;
        let x = ev.clientX - this.ele.offsetLeft;
        let y = ev.clientY - this.ele.offsetTop;
        this.moveFn(x, y);
        this.upFn();
      }
    }

    // 移动鼠标
    moveFn(x, y) {
      this.ele.onmousemove = e => {
        let ev = e || window.event;
        let xx = ev.clientX;
        let yy = ev.clientY;
        this.setStyle(xx - x, yy - y);
      }
    }

    // 设置拖拽后元素的定位
    setStyle(leftNum, topNum) {
      leftNum = leftNum < 0 ? 0 : leftNum;
      topNum = topNum < 0 ? 0 : topNum;
      this.ele.style.left = leftNum + "px";
      this.ele.style.top = topNum + "px";
    }

    // 鼠标抬起
    upFn() {
      this.ele.onmouseup = () => {
        this.ele.onmousemove = "";
      }
    }
  }

【6.3】通过继承封装扩展弹框组件

  // 扩展Dialog, 可输入内容
  class ExtendsDialog extends Dialog {
    constructor(options) {
      super(options);
      this.renderInput();
    }
    renderInput() {
      let myInput = document.createElement("input");
      myInput.type = "text";
      myInput.classList.add("input-inner");
      this.dialogHtml.querySelector(".k-body").appendChild(myInput);
    }
    confim() {
      let value = this.dialogHtml.querySelector(".input-inner").value;
      super.confim(value);
    }
  }

【6.4】完整代码

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
    .k-dialog {
      width: 30%;
      z-index: 2001;
      display: block;
      position: absolute;
      background: #fff;
      border-radius: 2px;
      box-shadow: 0 1px 3px rgba(0, 0, 0, .3);
      margin: 0 auto;
      top: 15vh;
      left: 30%;
      display: none;
    }

    .k-wrapper {
      position: fixed;
      left: 0px;
      top: 0px;
      bottom: 0px;
      right: 0px;
      background: black;
      opacity: 0.4;
      z-index: 2000;
      display: none;
    }

    .k-header {
      padding: 10px 20px 30px;
    }

    .k-header .k-title {
      line-height: 24px;
      font-size: 18px;
      color: #303133;
      float: left;
    }

    .k-body {
      padding: 30px 20px;
      color: #606266;
      font-size: 14px;
    }

    .k-footer {
      padding: 10px 20px 30px;
      position: absolute;
      bottom: 0;
      right: 0;
    }

    .k-close {
      color: #909399;
      font-weight: 400;
      float: right;
      cursor: pointer;
    }

    .k-cancel {
      color: #606266;
      border: 1px solid #dcdfe6;
      text-align: center;
      cursor: pointer;
      padding: 12px 20px;
      font-size: 14px;
      border-radius: 4px;
      font-weight: 500;
      margin-right: 10px;
    }

    .k-cancel:hover {
      color: #409eff;
      background: #ecf5ff;
      border-color: #c6e2ff;
    }

    .k-primary {
      border: 1px solid #dcdfe6;
      text-align: center;
      cursor: pointer;
      padding: 12px 20px;
      font-size: 14px;
      border-radius: 4px;
      font-weight: 500;
      background: #409eff;
      color: #fff;
      margin-left: 10px;
    }

    .k-primary:hover {
      background: #66b1ff;
    }

    .k-input {
      width: 100%;
      margin-left: 20px;
      margin-bottom: 20px;
    }

    .input-inner {
      -webkit-appearance: none;
      background-color: #fff;
      background-image: none;
      border-radius: 4px;
      border: 1px solid #dcdfe6;
      box-sizing: border-box;
      color: #606266;
      display: inline-block;
      font-size: inherit;
      height: 40px;
      line-height: 40px;
      outline: none;
      padding: 0 15px;
      transition: border-color .2s cubic-bezier(.645, .045, .355, 1);
      width: 100%;
      margin-top: 20px;
    }
  </style>
</head>

<body>
  <button class="dialog1">dialog1</button>
  <button class="dialog2">dialog2</button>
</body>
<script>
  // 拖拽方法封装
  class Drag {
    constructor(ele) {
      this.ele = ele
      this.downFn();
    }

    // 按下鼠标
    downFn() {
      this.ele.onmousedown = e => {
        let ev = e || window.event;
        let x = ev.clientX - this.ele.offsetLeft;
        let y = ev.clientY - this.ele.offsetTop;
        this.moveFn(x, y);
        this.upFn();
      }
    }

    // 移动鼠标
    moveFn(x, y) {
      this.ele.onmousemove = e => {
        let ev = e || window.event;
        let xx = ev.clientX;
        let yy = ev.clientY;
        this.setStyle(xx - x, yy - y);
      }
    }

    // 设置拖拽后元素的定位
    setStyle(leftNum, topNum) {
      leftNum = leftNum < 0 ? 0 : leftNum;
      topNum = topNum < 0 ? 0 : topNum;
      this.ele.style.left = leftNum + "px";
      this.ele.style.top = topNum + "px";
    }

    // 鼠标抬起
    upFn() {
      this.ele.onmouseup = () => {
        this.ele.onmousemove = "";
      }
    }
  }


  // dialog组件封装
  class Dialog {
    constructor(options) {
      // 合并默认配置和用户传的配置参数;
      this.opts = Object.assign({
        width: "30%",
        height: "200px",
        title: "标题",
        content: "内容",
        dragable: false, //是否可拖拽
        maskable: true, //是否有遮罩
        isCancel: true, //是否有取消按钮
        success() {
          console.log('点击确定按钮处理逻辑'); //
        },
        cancel() {
          console.log('点击取消按钮处理逻辑');
        }
      }, options);
      this.init(); // 自动调用
    }
    init() {
      // 渲染视图
      this.renderView();
      // 捕获弹框点击事件
      this.dialogHtml.onclick = e => {
        switch (e.target.className) {
          case 'k-close': // 关闭按钮
            this.close();
            this.opts.cancel();
            break;
          case 'k-cancel': // 取消按钮
            this.close();
            this.opts.cancel();
            break;
          case 'k-primary': // 确定按钮
            this.close();
            this.confim();
            break;
        }
      }
    }

    // 确定按钮操作
    confim(value) {
      this.opts.success(value);
    }

    //关闭对话框;
    close() {
      this.dialogHtml.querySelector(".k-wrapper").style.display = "none";
      this.dialogHtml.querySelector(".k-dialog").style.display = "none";
    }

    // 显示对话框;
    open() {
      // 是否显示遮罩层
      if (this.opts.maskable) {
        this.dialogHtml.querySelector(".k-wrapper").style.display = "block";
      }
      // 是否可拖拽
      if (this.opts.dragable) {
        let dialog = this.dialogHtml.querySelector(".k-dialog")
        let drag = new Drag(dialog);
      }
      this.dialogHtml.querySelector(".k-dialog").style.display = "block";
    }

    // 渲染视图
    renderView() {
      this.dialogHtml = document.createElement("div");
      this.dialogHtml.innerHTML = `<div class="k-wrapper"></div>
                <div class="k-dialog"  style="width:${this.opts.width};height:${this.opts.height}">
                    <div class="k-header">
                        <span class="k-title">${this.opts.title}</span><span class="k-close">X</span>
                    </div>
                    <div class="k-body">
                        <span>${this.opts.content}</span>
                    </div>
                    <div class="k-footer">
                        ${this.opts.isCancel ? '<span class="k-cancel">取消</span>' : ''}
                        <span class="k-primary">确定</span>
                    </div>
                </div>`;
      document.querySelector("body").appendChild(this.dialogHtml);
    }
  }


  // 当用户点击dialog1按钮创建弹框
  document.querySelector(".dialog1").onclick = function () {
    let dialog1 = new Dialog({
      width: '30%',
      height: "200px",
      title: "弹框标题",
      content: "弹框内容",
      dragable: true,
      isCancel: true,
      maskable: true,
      success() {
        console.log('点击确定按钮处理逻辑');
      },
      cancel() {
        console.log('点击确定按钮处理逻辑');
      }
    })
    dialog1.open();
  }


  // 扩展Dialog, 可输入内容
  class ExtendsDialog extends Dialog {
    constructor(options) {
      super(options);
      this.renderInput();
    }
    renderInput() {
      let myInput = document.createElement("input");
      myInput.type = "text";
      myInput.classList.add("input-inner");
      this.dialogHtml.querySelector(".k-body").appendChild(myInput);
    }
    confim() {
      let value = this.dialogHtml.querySelector(".input-inner").value;
      super.confim(value);
    }
  }

  // 当用户点击dialog2按钮创建弹框
  document.querySelector(".dialog2").onclick = function () {
    let dialog2 = new ExtendsDialog({
      width: "30%",
      height: "250px",
      title: "扩展弹框",
      content: "扩展弹框内容",
      isCancel: true,
      maskable: true,
      success(val) {
        console.log(val);
      },
      cancel() {
        console.log('点击取消');
      }
    })
    dialog2.open();
  }
</script>

</html>

【6.5】查看效果

class类学习 — 封装elementUI的dialog组件

class类学习 — 封装elementUI的dialog组件

 

文章每周持续更新,可以微信搜索「 前端大集锦 」第一时间阅读,回复【视频】【书籍】领取200G视频资料和30本PDF书籍资料

class类学习 — 封装elementUI的dialog组件class类学习 — 封装elementUI的dialog组件

本文地址:https://blog.csdn.net/qq_38128179/article/details/111085282