Class

# Class

JS中并不存在类,class只是语法糖,本质还是函数。

class Person {}
Person instanceof Function	// true
// 即Person. __proto__===Function. prototype
1
2
3

[TOC]

# 一、基本语法

let dragBox = new Drag(box);
class Drag {
    // 构造函数
    constructor(el){
        this.el=el;
    }
}
console.log(dragBox)
Drag
////el:div#box
////_proto_: Object
1
2
3
4
5
6
7
8
9
10
11
// ECMAScript 2015 中引入的 JavaScript 类实质上是 JavaScript 现有的基于原型的继承的语法糖。
// 类语法不会为JavaScript引入新的面向对象的继承模型。
// 类声明
// 函数声明和类声明之间的一个重要区别是函数声明会提升,类声明不会。
// 你首先需要声明你的类,然后访问它。
class Drag {
    // 构造函数
    constructor(el){
        this.el = el;
        // 鼠标摁下时的元素的位置
        this.startOffset = {};
        // 鼠标摁下时鼠标的坐标
        this.startPoint = {};
        let move = (e)=>{
            this.move(e);
        };
        let end = (e)=>{
           document.removeEventListener("mousemove",move);
           document.removeEventListener("mouseup",end); 
        };
        el.addEventListener("mousedown",(e)=>{
            // 由于箭头函数不绑定this
            // 它会捕获其所在(即定义的位置)上下文的this值, 作为自己的this值
           this.start(e);
           // 下面这两个语句放在mousedown里,才能确保只有鼠标按下后才会执行
           // 因为执行结束后都要被清除,所以不能直接用匿名函数
           // 这里用document,而不用el,就是为了避免鼠标滑动过快,移出拖拽对象
           document.addEventListener("mousemove",move);
           document.addEventListener("mouseup",end);
        });
    }
    // 摁下时的处理函数
    start(e){
        // 解构赋值
        // 等价于 let el = this.el
        let {el} = this;
        this.startOffset = {
            x: el.offsetLeft,
            y: el.offsetTop
        };
        this.startPoint = {
            x: e.clientX,
            y:e.clientY
        };
    }
    //移动时的处理函数
    move(e){
        let {el,startOffset,startPoint} = this;
        let nowPoint = {
            x: e.clientX,
            y: e.clientY
        };
        let dis = {
            x: nowPoint.x - startPoint.x,
            y: nowPoint.y - startPoint.y
        }
        el.style.left = dis.x + startOffset.x + "px";
        el.style.top = dis.y + startOffset.y + "px";
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

# 二、静态方法

类相当于实例的原型, 所有在类中定义的方法, 都会被实例继承。 如果在一个方法前, 加上static关键字, 就表示该方法不会被实例继承, 而是直接通过类来调用, 这就称为“ 静态方法”。

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

Foo.classMethod() // 'hello'

var foo = new Foo();
foo.classMethod()	// TypeError: foo.classMethod is not a function
1
2
3
4
5
6
7
8
9
10

上面代码中, Foo类的classMethod方法前有static关键字, 表明该方法是一个静态方法, 可以直接在Foo类上调用( Foo.classMethod()), 而不是在Foo类的实例上调用。 如果在实例上调用静态方法, 会抛出一个错误, 表示不存在该方法。

  • 父类的静态方法, 可以被子类继承。
class Bar extends Foo {}
Bar.classMethod(); // 'hello'
1
2

上面代码中, 父类Foo有一个静态方法, 子类Bar可以调用这个方法。

  • 静态方法也是可以从super对象上调用的。
class Bar extends Foo {
	static classMethod() {
		return super.classMethod() + ', too';
	}
}
Bar.classMethod();	// "Hello, too"
1
2
3
4
5
6

# 请实现一个 Event 类,继承自此类的对象都会拥有四个方法 on, off, once 和 trigger。

//event.js
class Event {
    constructor(){
        this.handlers = {}; //记录所有的事件及处理函数
        /*handles存放的类似下面的事件类型
            {
                click:[fn1,fn2],
                mouseover: [fn3,fn4]
            }
        */
    }
    /**
     * on 添加事件监听
     * @param type 事件类型
     * @param handler 事件处理函数
     */
    on(type,handler,once=false){
        // 如果事件类型不存在的话,就给它加进去
       if(!this.handlers[type]){
         this.handlers[type] = [];
       }
       // includes()避免事件重复添加
       if(!this.handlers[type].includes(handler)){
         this.handlers[type].push(handler);
         handler.once = once;
       }
    }
    /**
     * off 取消事件监听
     * @param 用来记录所需要的参数
     * @param type 要取消的事件类型
     * @param handler 要取消的事件函数,如果不传则清除该类型所有函数
     */
    off(type,handler){
        if(this.handlers[type]){
            // 如果不存在,则作清空处理
            if(handler === undefined){
                this.handlers[type] = [];
            } else {
                // filter创建一个新数组,不改变原来的数组,返回符合条件的数组
                this.handlers[type] = this.handlers[type].filter(f => f!=handler);
            }
        }
    }
    /**
     * trigger  执行函数
     * @param type 要执行哪个类型的函数
     * @param eventData 事件对象
     * @param point this执行
     */
    trigger(type,eventData={},point=this){
        if(this.handlers[type]){
            this.handlers[type].forEach(f => {
                
                if(f.once){
                    this.off(type,f);
                }
                // call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
                f.call(point,eventData);
                
            });
        }
    }
    /**
     * once 该函数只执行一次
     * @param type 事件类型
     * @param handler 事件处理函数
     */
    once(type,handler){
        this.on(type,handler,true);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72