JS面向对象和组件封装

200 阅读3分钟

本文主要是对js面向对象和组件封装做一个简单的学习笔记;

1  Tab组件封装

1.1 面向过程

下面代码实现了一个简单的tab功能

<!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>
</head>
<body>
  <div class="tab1">
    <button style="background: red">按钮一</button>
    <button>按钮二</button>
    <button>按钮三</button>
    <p>内容一</p>
    <p style="display: none">内容二</p>
    <p style="display: none">内容三</p>
  </div>
  <div class="tab2">
    <button style="background: red">按钮一</button>
    <button>按钮二</button>
    <button>按钮三</button>
    <p>内容一</p>
    <p style="display: none">内容二</p>
    <p style="display: none">内容三</p>
  </div>
  <button class="nextPre">点我下一页</button>
  <button class="autoPlay">点我第二个选项卡自动轮播</button>
</body>
<script type="text/javascript">
  function Tab(btns, ps, isNextPre = false, isAutoPlay = false) {
    btns.forEach((value, key) => {
      value.onclick = function() {
        psFor(key)
      }
    })
    if(isNextPre) {
      let num = 0;
      document.querySelector('.nextPre').onclick = function() {
        num++;
        num = num > 2 ? 0 : num;
        psFor(num);
      }
    }
    if(isAutoPlay) {
      document.querySelector('.autoPlay').onclick = function() {
        let num = 0;
        setInterval(() => {
          num++;
          num = num > 2 ? 0 : num;
          psFor(num);
        }, 1000)
      }
    }
    function psFor(key) {
      ps.forEach((v, k) => {
        if(key === k) {
          btns[k].style.background = 'red';
          ps[k].style.display = 'block';
        } else {
          btns[k].style.background = '';
          ps[k].style.display = 'none';
        }
      })
    }
  }
  let btns = document.querySelectorAll(".tab1 button");
  let ps = document.querySelectorAll(".tab1 p");
  Tab(btns, ps, false, false);

  let btns2 = document.querySelectorAll(".tab2 button");
  let ps2 = document.querySelectorAll(".tab2 p");
  Tab(btns2, ps2, true, true);
</script>
</html>

效果如下:

1.2 工厂模式

工厂模式简介,实现步骤如下:

function Tab() {
  // 添加原料
  let obj = {};
  //加工原料
  obj.tabLength = 1;
  obj.psFor = function() {
    console.log("psFor....");
  }
  // 出厂
  return obj;
}

工厂模式问题:

对象识别问题:

// 缺点一:对象识别问题
let tab1 = Tab();

console.log(tab1.constructor);

// ƒ Object() { [native code] }

性能问题:

tab1.psFor();
let tab2 = Tab();
tab2.psFor();

// 缺点二:性能问题
console.log(tab1.psFor === tab2.psFor); // false

tab组件的工厂模式实现

<script type="text/javascript">
  function Tab(btns, ps, isNextPre = false, isAutoPlay = false) {
    btns.forEach((value, key) => {
      value.onclick = function() {
        psFor(key)
      }
    })
    function psFor(key) {
      ps.forEach((v, k) => {
        if(key === k) {
          btns[k].style.background = 'red';
          ps[k].style.display = 'block';
        } else {
          btns[k].style.background = '';
          ps[k].style.display = 'none';
        }
      })
    }
    let obj = {};
    obj.psFor = psFor;
    obj.eleLength = btns.length;
    return obj;
  }
  let btns = document.querySelectorAll(".tab1 button");
  let ps = document.querySelectorAll(".tab1 p");
  let tab1 = Tab(btns, ps);
  let num = 0;
  document.querySelector('.nextPre').onclick = function() {
    num++;
    num = num > tab1.eleLength - 1 ? 0 : num;
    tab1.psFor(num);
  }

  let btns2 = document.querySelectorAll(".tab2 button");
  let ps2 = document.querySelectorAll(".tab2 p");
  let tab2 = Tab(btns2, ps2);

  document.querySelector('.autoPlay').onclick = function() {
    let num2 = 0;
    setInterval(() => {
      num2++;
      num2 = num2 > tab2.eleLength - 1 ? 0 : num2;
      tab2.psFor(num2);
    }, 1000)
  }
</script>

1.3 构造函数模式

构造函数一般首字母大写, 属性放在构造函数里,方法放在原型

function Tab() {
   this.name = '张三';
   this.hobby = function() {
     console.log('篮球');
   }
 }

 Tab.prototype.psFor = function() {
   console.log("psFor...");
 }

 Tab.prototype.hobby = function() {
   console.log("hobby...");
 }

 let tab1 = new Tab();
 console.log(tab1.name);
 tab1.hobby();

 let tab2 = new Tab();
 console.log(tab1.psFor === tab2.psFor); // true

 console.log(tab1.__proto__ === Tab.prototype); // true

 // 每个原型上都有一个预定义属性:constructor --> 构造函数;
console.log(Tab.prototype.constructor === Tab)  // true
console.log(tab1.constructor === Tab) // true

构造函数继承

继承:子类继承父类所有的属性和行为,父类不受影响
目的:找到类之间的共性精简代码

function Person(name) {
  this.name = name;
  this.eyes = '两只';
  this.legs = '两条';
}

function Student(name) {
  Person.call(this, name);
  this.className = "二班"
}

let newPerson = new Student('张三');
console.log(newPerson.className);

// 简单原型继承,出现影响父类的情况
Student.prototype = Person.prototype; // 直接赋值

原型链继承

原型链是指对象在访问属性或者方法时的查找方式

当访问一个对象的属性或者方法时,会在对象自身上查找属性或方法是否存在,如果存在就使用自身的属性或方法,如果不存在就去创建对象的构造函数的原型对象中查找,依次类推,直到找到为止,如果到顶层对象中还找不到,则返回undefined

原型链最顶层为Object构造函数的prototype原型对象

function Dad(height) {
  this.name = "张三";
  this.age = 20;
  this.heigth = height;
  this.money = "$1000000";
}
Dad.prototype.hobby = function() {
  console.log('喜欢高尔夫');
}

function Son(height) {
  Dad.call(this, height);
}
// 原型链继承
let Link = function() {};
Link.prototype = Dad.prototype;
Son.prototype = new Link();
Son.prototype.constructor = Son;
Son.prototype.hobby = function() {
  console.log('喜欢篮球');
}

let newSon = new Son("178cm");
// console.log(newSon);
newSon.hobby();

let newDad = new Dad("179cm");
newDad.hobby();

tab组件的构造函数实现

function Tab(btns, ps) {
    this.btns = btns;
    this.ps = ps;
    this.btnFor();
  }
  Tab.prototype.btnFor = function() {
    this.btns.forEach((value, key) => {
      value.onclick = () => {
        this.psFor(key)
      }
    })
  }
  Tab.prototype.psFor = function(key) {
    this.ps.forEach((v, k) => {
      if(key === k) {
        this.btns[k].style.background = 'red';
        this.ps[k].style.display = 'block';
      } else {
        this.btns[k].style.background = '';
        this.ps[k].style.display = 'none';
      }
    })
  }

  let btns = document.querySelectorAll(".tab1 button");
  let ps = document.querySelectorAll(".tab1 p");
  let tab1 = new Tab(btns, ps);
  let num = 0;
  document.querySelector('.nextPre').onclick = function() {
    num++;
    num = num > tab1.btns.length - 1 ? 0 : num;
    tab1.psFor(num);
  }

  let btns2 = document.querySelectorAll(".tab2 button");
  let ps2 = document.querySelectorAll(".tab2 p");
  let tab2 = new Tab(btns2, ps2);

  document.querySelector('.autoPlay').onclick = function() {
    let num2 = 0;
    setInterval(() => {
      num2++;
      num2 = num2 > tab2.btns.length - 1 ? 0 : num2;
      tab2.psFor(num2);
    }, 1000)
  }

2  Drag组件封装

下面代码实现了拖拽组件的封装

2.1  es5继承

<!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>
    .mydiv1{
      width: 100px;
      height: 100px;
      background: red;
      position: absolute;
    }
    .mydiv2{
      width: 100px;
      height: 100px;
      background: purple;
      position: absolute;
      left: 300px;
    }
  </style>
</head>
<body>
  <div class="mydiv1"></div>
  <div class="mydiv2"></div>
</body>
<script type="text/javascript">
  // 面向对象
  function Drag(ele) {
    this.ele = ele;
    this.downFn();
  }
  Drag.prototype.downFn = function() {
    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();
    }
  }
  Drag.prototype.moveFn = function(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);
    }
  }
  Drag.prototype.setStyle = function(leftNum, topNum) {
    this.ele.style.left = leftNum + "px";
    this.ele.style.top = topNum + "px";
  }
  Drag.prototype.upFn = function() {
    this.ele.onmouseup = () => {
      this.ele.onmousemove = "";
    }
  }
  let mydiv1 = document.querySelector(".mydiv1");
  let drag1 = new Drag(mydiv1);


  function LimitDrag(ele) {
    Drag.call(this, ele);
  }
  // 通过中间函数切断子类与父类的原型链接
  let Link = function() {};
  Link.prototype = Drag.prototype;
  LimitDrag.prototype = new Link();
  //  此处也可以采用深拷贝继承
  // LimitDrag.prototype = deepClone(Drag.prototype);
  // 子类的constructor属性指回子类的构造函数
  LimitDrag.prototype.constructor = LimitDrag;

  LimitDrag.prototype.setStyle = function(leftNum, topNum) {
    leftNum = leftNum < 0 ? 0 : leftNum;
    topNum = topNum < 0 ? 0 : topNum;
    this.ele.style.left = leftNum + "px";
    this.ele.style.top = topNum + "px";
  }

  let mydiv2 = document.querySelector(".mydiv2");
  let drag2 = new LimitDrag(mydiv2);
</script>
</html>

2.2 es6继承

父类拖拽组件

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) {
    this.ele.style.left = leftNum + "px";
    this.ele.style.top = topNum + "px";
  }
  upFn() {
    this.ele.onmouseup = () => {
      this.ele.onmousemove = "";
    }
  }
}

export default Drag;

子类

import Drag from './Drag.js'

class LimitDrag extends Drag {
  constructor(ele) {
    super(ele);
  }
  setStyle(leftNum, topNum) {
    leftNum = leftNum < 0 ? 0 : leftNum;
    topNum = topNum < 0 ? 0 : topNum;
    // 通过super调用父类的方法
    super.setStyle(leftNum, topNum)
  }
}

export default LimitDrag;