深入了解JavaScript之面向对象(二)之 class

class是什么
  • class是定义类的方法。ES6之前用构造函数的方式定义类,ES6引入了class。
  • class是语法糖。
  • class内部默认是严格模式。
  • class不存在变量提升现象,必须要先声明后使用
使用

传统方式定义一个类

function Point1(x,y){
    
    
  this.x = x
  this.y = y
}
Point1.prototype.add = function(){
    
    
  return this.x + this.y
}
var p1 = new Point1(3,9)
console.log(p1.add())

class方式定义一个类

class Point2{
    
    
  constructor(x, y){
    
    
    this.x = x
    this.y = y
  }
  add(){
    
      // 这里的add方法实际上是定义在原型上的。
    return this.x + this.y
  }
}
var p2 = new Point2(4,77)
console.log(p2.add())

类的数据类型

console.log(typeof Point1)  // function
console.log(typeof Point2)  // function
constructor

constructor 是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须要有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。

class Point {
    
    
}
//等同于
class Point{
    
    
	constructor(){
    
    
	}
}

constructor 方法默认返回实例对象(即this),完全可以指定返回另外一个对象。

class Foo{
    
    
	constructor(){
    
    
	return Object.create(null);
	}
}
new Foo() instanceof Foo
//false

上面代码中,constructor函数返回一个全新的对象,结果导致实例对象不是Foo类的实例。

this指向问题

如果方法里有this,this指向的是实例

类的数据类型就是函数,类本身就是指向函数的
类的所有方法都是定义在类的prototype上的

类的私有变量
  • 私有变量就是只能在类内部访问的变量,外部(类的实例化对象)无法访问的变量。
  • 子类不能继承父类的私有变量。

私有变量的实现

  • 私有变量定义在constructor 外面,并且加上#号;
<!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>
        #box {
    
    
            position: absolute;
            left: 100px;
            top: 100px;
            width: 100px;
            height: 100px;
            background: red;
        }
        #box2 {
    
    
            position: absolute;
            left: 200px;
            top: 200px;
            width: 100px;
            height: 100px;
            background: green;
        }
    </style>
</head>
<body>
    <div id="box">文字</div>
    <div id="box2">文字</div>
<script>
/*
    抽象:
        拖动之后元素跟随移动
    封装: 
        封装成类    
*/
class Drag {
    
    
    startMouse = {
    
    };
    startEl = {
    
    };
    #el = null;//私有变量,在实例化对象中不能获取
    constructor(el){
    
    
        this.#el = el;
        this.start();
    }
    start(){
    
    
        let move = (e)=>{
    
    
            this.move(e);
        };
       this.#el.addEventListener("mousedown",(e)=>{
    
    
            this.startMouse = {
    
    
                x: e.clientX,
                y: e.clientY
            };
            this.startEl= this.getOffset();
           // console.log(this.ondragstart);
            this.ondragstart&&this.ondragstart(e);//start执行才会去执行ondragstart,所以ondragstart依赖于start 
            document.addEventListener("mousemove",move);
            document.addEventListener("mouseup",()=>{
    
    
                document.removeEventListener("mousemove",move);
                this.end();
            },{
    
    once: true});
            e.preventDefault();
       }); 
    }
    move(e){
    
    
        let nowMouse = {
    
    
            x: e.clientX,
            y: e.clientY
        };
        let disMouse = {
    
    
            x: nowMouse.x - this.startMouse.x,
            y: nowMouse.y - this.startMouse.y
        };
        this.setOffset(disMouse);
        this.ondrag&&this.ondrag(e);
    }
    //拖拽结束
    end(e){
    
    
        this.ondragend&&this.ondragend(e);
    }
    // 获取元素的位置
    getOffset(){
    
    
        return {
    
    
            x: parseFloat(getComputedStyle(this.#el)["left"]),
            y: parseFloat(getComputedStyle(this.#el)["top"])
        }
    }
    // 设置元素的位置
    setOffset(dis){
    
    
        this.#el.style.left = dis.x + this.startEl.x + "px";
        this.#el.style.top = dis.y + this.startEl.y + "px"
    }
}
{
    
    
    let box = document.querySelector("#box");
    let box2 = document.querySelector("#box2");
    let d = new Drag(box);
    let d2 = new Drag(box2);
    let box2Clone = null;
    d2.ondragstart = function(){
    
    
        //console.log("开始拖拽");
        box2Clone = box2.cloneNode(true);
        //console.log(box2Clone);
        document.body.appendChild(box2Clone);
        box2.style.opacity = .5;
    };
    d2.ondrag = function(){
    
    
        //console.log("拖拽中");
    };
    d2.ondragend = function(){
    
    
        document.body.removeChild(box2Clone);
        box2.style.opacity = 1;
    };
}
</script>    
</body>
</html>

为什么要使用私有变量

  let d = new Drag(box);
  d.el = box2;
  上面的这个例子中,假如我们已经对一个元素实例化了,但是我们改变实例化对象的el 属性的话,那对el的这个拖拽就无法生效了,
  el这个属性必须是私有属性。
类的继承

继承:继承可以使得子类具有父类的属性和方法并重新定义、追加属性和方法等。

class Person {
    
    
    constructor(name,age){
    
    
        this.name = name;
        this.age = age;
    }
    say(){
    
    
        console.log(this.name);
    }
}
class Teacher extends Person {
    
    
    constructor(name,age){
    
    
        // 如果在子类中重新定义constructor,一定在这里调用 super
        super(name,age);
        this.speciality = "讲课";
    }
    skill(){
    
    
        console.log("连续上课24小时");
    }
}
console.log(new Person("小明",18));
console.log(new Teacher("小红",58));
类的静态属性

关于类有两个概念,1,类自身 ;2,类的实例对象
总的来说:静态的是指向类自身,而不是指向实例对象,主要是归属不同,这是静态属性的核心。

静态方法的使用:在方法前加上static关键字

class Foo {
    
    
  static classMethod() {
    
    
    return 'hello';
  }
}

为什么使用静态方法:阻止方法被实例继承,类的内部相当于实例的原型,所有在类中直接定义的方法相当于在原型上定义方法,都会被类的实例继承,但是使用static静态方法定义的不会被实例继承,而且可以被实例直接应用Foo.classMethod(),此时写成new Foo.classMethod()会提示不存在此方法

静态方法中this指向:this指向类而不是类的实例

class Foo {
    
    
  static bar () {
    
    
    this.baz();
  }
  static baz () {
    
    
    console.log('hello');
  }
  baz () {
    
    
    console.log('world');
  }
}
 
Foo.bar() // hello

猜你喜欢

转载自blog.csdn.net/literarygirl/article/details/105879510