类的语法
class关键字声明,内部使用constructor初始化实例化对象的属性。

class User {
    // constructor方法是对属性进行初始化的 接收参数,同样在实例化对象后,对象会自动执行
    constructor(name) {
        this.name = name;
    }
    //方法
    getName() {
        return this.name;
    }
}
console.log(typeof User); // function

// 既然是函数的话,我们还有另外一种定义的方式
let F = class {
    constructor(name) {
        this.name = name;
    }
    getName() {
        return this.name;
    }
};
let user = new User("sunny");

类的内部实现原理
类的本质还是原生js的构造函数,通过constructor构造方法声明的属性是对象的属性,类中定义的其他方法自动存在类的原型中。

对比一下类和构造函数实现的对象:

// 语法糖的结构:结构清晰

// 内部实现与构造函数一样
class User {
    // 初始化对象的属性
    constructor(name) {
        this.name = name;
    }
    // 在类里面添加方法的方法,会自动添加到类的原型中,并且这些方法不会被遍历,默认特征为false
    show() {
        console.log(this.name);
    }
}
let u = new User("user");
console.log(u); // 与f结构一样
// 类也有自己的原型对象,也有原型对象的属性
console.log(User.prototype.constructor == User);

function Fun(name) {
    this.name = name;
}
// 构造函数呢,是我们手动添加的
Fun.prototype.show = function () {
    console.log(this.name);
}
let f = new Fun("fun");
console.log(f);

从显示中也可以看出,类方法默认添加在类原型当中并且方法是不可遍历的。
1.jpg
类和构造函数实例化出的对象以及二者的原型均是一样的:

console.log(Object.getOwnPropertyNames(User.prototype));
console.log(Object.getOwnPropertyNames(u));
        
console.log(Object.getOwnPropertyNames(Fun.prototype));
console.log(Object.getOwnPropertyNames(f));

2.jpg
在类中设置对象的属性和方法
对象属性和方法都有两种设置的形式。
属性:属性的声明可以在constructor方法内也可在方法外,通常我们设置在构造方法内
方法:类方法通常在构造方法外声明,也可以在构造方法内声明。

class User {
    site = "www.baidu.com";
    constructor(name) {
        this.name = name;
        this.only = function onlyMe() {
            console.log("对象自己的方法");
        }
    }
    changeSite(value) {
        this.site = value;
    }
}
let ff = new User("xiaomin");
console.log(ff);
ff.site = "bai.com";
console.log(ff.name + "的size是:" + ff.site); // 可更改

let ff2 = new User("xiaofeng");
console.log(ff2);
ff2.site = "修改后的size";
console.log(ff2.name + "的size是:" + ff2.site); // 不受其他对象改变属性的影响

定义类的静态属性和方法
类的静态方法和属性通过 static关键字声明。
定义类的静态属性和方法,只能本类及其子类能访问,不对实例化对象开放,如果想让对象访问,在类中定义方法即可。
类的静态属性和方法:适合批量操作实例化对象

class Vip {
    // static 定义 静态属性 保存在类中
    static host = "www.baidu.com";
    constructor(name) {
        this.name = name;
    }
    // 静态方法 static关键字
    static getHost(url) {
        // this指向Vip类 getHost
        return this.host + `/${url}`;
    }

    static create(...args) {
        return new this(...args);
    }
    show() {
        console.log(this.name);
    }
}

let v = new Vip("vip");
console.log(v);
console.log(Vip.getHost("index"));
console.dir(Vip.prototype.constructor == Vip); //true

// 通过create方法创建对象 核心还是new 类的操作
let obj = Vip.create("tt", 18);
console.dir(obj);

3.jpg
静态方法的使用
案例:课程类
批量操作对象,统计、筛选等操作。

const lesson = [{
    name: "js",
    price: 198
}, {
    name: "css",
    price: 82
}, {
    name: "html",
    price: 100
}, {
    name: "jquery",
    price: 150
}, {
    name: "mysql",
    price: 300
}];
// 定义一个课程类
class Lesson {
    constructor(date) {
        this.module = date;
    }
    // 设置获取name和price属性
    get name() {
        return this.module.name;
    }
    get price() {
        return this.module.price;
    }
    // 对所有对象批量操作:使用静态方法
    // 为每个对象创建实例化对象
    static create(date) {
        // 因为是数组类型,所以传入的数据是数组,遍历分别创建对象
        return date.map(item => {
            return new this(item);
        })
    }
    // 选出价格最高的课程
    static maxPrice(date) {
        // 排序,降序取第一个
        return date.sort((a, b) => {
            return b.price - a.price;
        })[0];
    }
    // 计算课程总额
    static totalPrice(date) {
        // 参数1:归并的结果
        // 参数2:遍历时当前的元素
        return date.reduce((total, cur) => {
            return total += cur.price;
        }, 0)
    }
    // 筛选价格高于一个价格的课程
    static filterPrice(date, putPrice) {
        return date.filter(item => {
            return item.price > putPrice;
        }).map(item => {
            return `课程是:${item.name},价格是${item.price}`;
        });
    }
}
//通过静态方法实例化对象
let lessones = Lesson.create(lesson);
console.log(lessones[0].name);
//console.log(lessones);
//获取价格最高的课程
let maxPrice = Lesson.maxPrice(lesson).price;
console.log(`价格最高的课程是:${Lesson.maxPrice(lesson).name},价格是:${maxPrice}`);

//计算课程总价
let sumPrice = Lesson.totalPrice(lesson);
console.log(sumPrice);

//筛选符合价格条件的课程
console.log(Lesson.filterPrice(lesson, 100));

类的继承
上面说到了类本质就是构造函数,继承的实现同样是原型的继承,不过在类中继承不像之前那么麻烦了,只需要extends关键字就能实现类的继承。
A extends B :A类 继承 B类。
B类的构造方法中必须调用super(),同时super()还必须放在this之前,否则会报错。

class Father {
    static father = "father.attr";
    constructor() {
        this.name = "name";
        this.age = "age";
    }
    get msg() {
        return `名字是:${this.name},年龄${this.age}岁`;
    }
    show() {
        //方法自动挂在Father的原型上
    }
}
class Son extends Father {
    host = "private";
    constructor() {
        // 继承父类中的对象的所有属性
        // 必须在子类构造方法的第一行,也就是this之前调用
        super();
        this.only = "son.attr";
    }
}
let son = new Son;
console.log(son);
console.dir(Son);
console.log(Son.prototype.__proto__ == Father.prototype); // true

super原理
super是通过call、apply、bind方法实现,但是在多继承的时候又比它们好用,也正是super解决了原生多继承的问题。
两个对象的继承
先看下用原生的对象实现继承:

let user = {
    name: "user.name",
    // person要执行commen对象的show方法
    show: function () {
        console.log(this.name + " user对象");
    }
};
let person = {
    name: "person.name",
    // person继承user对象
    __proto__: user,
    show: function () {
        // person对象执行父级show方法
        // this.__proto__.show(); // 这个是打印的是user的name
        // 这才是本对象调用了父级方法  这样就实现了clas中super的作用
        this.__proto__.show.call(this);
    }
};
person.show();

定义了两个对象,user和person,让person继承user对象,这样我们需要设置person的__proto__指向user。person调用show方法时想使用父级的show方法,这时候我们需要让show方法的this指向person对象,则需要call或apply等方法。

    show: function () {
        //这个是user执行的,打印的是user的name,因为this.__proto__指向了user
        // this.__proto__.show(); 
        // 这才是本对象调用了父级方法  这样就实现了clas中super的作用
        this.__proto__.show.call(this);
    }

两个对象以上的继承
此时是没有发现问题,当我们再加一层,让user对象继承commen对象;
定义commen对象:

let commen = {

    name: "commen.name",
    show: function () {
        console.log(this.name + " commen对象");
    }
}

只需让user的__proto__ 属性指向 commen。

let user = {
    name: "user.name",
    // // user继承commen
    __proto__: commen,
    // person要执行commen对象的show方法
    show: function () {
        console.log(this.name + "  user对象");
        //此时this指向person,
           //则this.__proto__指向user
           //所以就变成了 user.show.call(person)
        // 会不断的调用自己,陷入死循环的状态
        this.__proto__.show.call(this);
        // 这就体现了super的作用了
    }
};
person.show();

这就需要用到类的super了,既方便又好用,不会出现多继承出现的这种问题。

私有属性的设置
在类中,我们人为的规定私有属性使用 _开头定义对象私有属性。

class User {
    // 这种命名方法,就是认为的告诉用户这个属性是不对外开放的,不可访问的
    // 但本质上还是能访问和修改的,这种方式知识命名方式的保护属性
    _size = "http://baidu.com";
    // 如果想开放,设置接口
    set url(url) {
        if (!/^http?:/i.test(url)) {
            throw new Error("地址异常");
        }
        this._size = url;
    }
    get url() {
        return this._size;
    }
}
let user = new User;
console.log(user);
user._size = "http://js.com"; // 能设置 并没有真正意义上实现保护
console.log(user._size); // 能获取
user.url = "http://baidu.com";
console.log(user.url);

毕竟是人为规定的,实际还是能操作的。
我们可以使用Symbol数据类型来设置真正的私有属性。

// []存储Symbol的值
const date = Symbol();
class User {
    // 设置属性  当前类及其子类可使用,换句话说就是有其他类继承本类也可使用这个私有属性
    // [HOST] = "http://www.baidu.com";
    // 当私有变量多的时候,使用对象把这些变量都存储起来
    constructor(name) {
        this[date] = {};
        this[date].host = "http://www.baidu.com";
        // this.name = name;
        // 设置name为私有属性
        this[date].name = name;
    }

    // 若想操作私有属性时,开放接口来操作
    // 通过host属性来访问
    set host(url) {
        //正则验证地址是否合法
        if (!(/^http(s?):/i).test(url)) {
            throw new Error("地址异常");
        }
        this[date].host = url;
    }
    get host() {
        return this[date].host;
    }
    // 设置接口开放name属性
    get name() {
        return this[date].name;
    }
}
let u1 = new User("u1");
let u1 = new User("u1");
console.log(u1[Symbol()]); // 获取不到 实现属性的保护作用
console.log(u1.host); // this[date].host
console.log(u1.name); // undefined 不可访问

总结类的几点优势
类中声明的方法,会自动放到原型对象中,;简化了原型的操作
类的原型对象中的属性不可变量,属性特征默认为false;
类默认在严格模式下运行,使得编写得代码更加健壮
类的综合案例:滑动栏
有点类似tab栏切换,就是一列选项,点击当前选项显示其内容,其余的隐藏,进行切换效果。
效果图:
4.gif

整个区域:事件的处理;
存在面板:面板做的动画;
存在动画:显示、隐藏的效果;

// 动画类
class Animation {
    // 传递一个参数:做动画的部分
    constructor(ele) {
        this.ele = ele;
        this.defaultHeight = this.height;
        // 默认为显示
        this.isShow = true;
        // 滑动的速度
        this.speed = 1;
        // 定时器的间隔时间
        this.cutTime = 5;
    }
    // 隐藏方法
    hide(callback) {
        this.isShow = false;

        let timer = setInterval(() => {
            if (this.height <= 0) {
                clearInterval(timer);
                callback && callback();
                return;
            }
            this.height = this.height - this.speed;
        }, this.cutTime);
    }
    // 显示方法
    show(callback) {
        let timer = setInterval(() => {
            if (this.height >= this.defaultHeight) {
                clearInterval(timer); // 到达效果只会移除定时器
                callback && callback();
                return;
            }
            this.height = this.height + this.speed;
        }, this.cutTime);
    }
    get height() {
        // 去除px单位 去掉后两位 变为数值类型
        return parseInt(window.getComputedStyle(this.ele).height.slice(0, -2));
    }
    set height(height) {
        this.ele.style.height = height + "px";
    }
}
//测试动画效果
// let box = document.querySelector(".box");
// let an = new Animation(box);
// console.log(an);
// an.hide(() => {
//     console.log("隐藏完了");
//     // an.show(() => {
//     //     console.log("显示完了");

//     // })
// });

// 区域类
class Slider {
    constructor(ele) {
        this.ele = document.querySelector(ele);
        this.links = this.ele.querySelectorAll("dt");
        // 使用类创建对象
        this.panles = [...this.ele.querySelectorAll("dd")].map(item => {
            // 继承了动画类 , 需要一个元素
            return new Panle(item);
        });
        this.bind(); // 添加事件
    }
    bind() {
        this.links.forEach((element, index) => {
            element.addEventListener("click", item => {
                this.handler(index);
            })
        });
    }
    // 事件中的处理方法
    handler(index) {
        // 隐藏当前之外的元素
        // console.log(Panle.filter(this.panles, index));
        // 解决多动画抖动
        Panle.hideAll(Panle.filter(this.panles, index), () => {
            // 显示自己
            this.panles[index].show();
        });
    }
}

// 这个面板区域, 做动画操作 
class Panle extends Animation {
    // 检测动画
    static flag = 0;
    // 批量操作对象
    static hideAll(panle, callback) {
        // 解决多动画
        if (Panle.flag > 0) return;
        panle.forEach(item => {
            Panle.flag++;
            item.hide(() => {
                Panle.flag--;
            });
        })
        callback && callback();
    }
    static filter(panle, index) {
        // 过滤掉点击的对象
        return panle.filter((item, i) => i != index);
    }
}
let s = new Slider(".slider");
console.log(s);

标签: none

添加新评论